add JSON export
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
98eed72af6
commit
eef1e16830
Before Width: | Height: | Size: 507 B |
After Width: | Height: | Size: 781 B |
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,154 @@
|
||||
import {
|
||||
TacticContext,
|
||||
TacticService,
|
||||
} from "../../service/MutableTacticService.ts"
|
||||
import { useEffect, useState } from "react"
|
||||
import "../../style/export_tactic_popup.css"
|
||||
|
||||
import JsonIcon from "../../assets/icon/json.svg?react"
|
||||
import {
|
||||
StepInfoNode,
|
||||
Tactic,
|
||||
TacticStep,
|
||||
} from "../../model/tactic/TacticInfo.ts"
|
||||
import { countSteps } from "../../domains/StepsDomain.ts"
|
||||
|
||||
export interface ExportTacticPopupProps {
|
||||
service: TacticService
|
||||
show: boolean
|
||||
onHide: () => void
|
||||
}
|
||||
|
||||
export default function ExportTacticPopup({
|
||||
service,
|
||||
show,
|
||||
onHide,
|
||||
}: ExportTacticPopupProps) {
|
||||
const [context, setContext] = useState<TacticContext>()
|
||||
const [panicMessage, setPanicMessage] = useState<string>()
|
||||
|
||||
const [exportPercentage, setExportPercentage] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
async function init() {
|
||||
const result = await service.getContext()
|
||||
if (typeof result === "string") {
|
||||
setPanicMessage("Could not retrieve tactic context")
|
||||
return
|
||||
}
|
||||
setContext(result)
|
||||
}
|
||||
|
||||
if (!context) init()
|
||||
}, [context, service])
|
||||
|
||||
useEffect(() => {
|
||||
function onKeyUp(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") onHide()
|
||||
}
|
||||
|
||||
window.addEventListener("keyup", onKeyUp)
|
||||
return () => window.removeEventListener("keyup", onKeyUp)
|
||||
}, [onHide])
|
||||
|
||||
if (!show) return <></>
|
||||
|
||||
if (panicMessage) return <p>{panicMessage}</p>
|
||||
|
||||
return (
|
||||
<div className="popup" onClick={onHide}>
|
||||
<div className="popup-card" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="popup-header">
|
||||
<p>Exporting {context?.name ?? "Tactic"}</p>
|
||||
</div>
|
||||
<ProgressBar percentage={exportPercentage} />
|
||||
<div className="popup-exports">
|
||||
<div
|
||||
className="export-card"
|
||||
onClick={async () => {
|
||||
await exportInJson(
|
||||
context!,
|
||||
service,
|
||||
setExportPercentage,
|
||||
)
|
||||
setExportPercentage(0)
|
||||
}}>
|
||||
<p className="export-card-title">Export in JSON</p>
|
||||
<JsonIcon className="json-logo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
async function exportInJson(
|
||||
context: TacticContext,
|
||||
service: TacticService,
|
||||
onProgress: (p: number) => void,
|
||||
) {
|
||||
const tree = context.stepsTree
|
||||
|
||||
const treeSize = countSteps(tree)
|
||||
const totalStepsCompleted = new Uint16Array(1)
|
||||
|
||||
async function transformToStep(
|
||||
stepInfoNode: StepInfoNode,
|
||||
): Promise<TacticStep> {
|
||||
const contentResult = await service.getContent(stepInfoNode.id)
|
||||
if (typeof contentResult === "string") throw Error(contentResult)
|
||||
|
||||
Atomics.add(totalStepsCompleted, 0, 1)
|
||||
onProgress((Atomics.load(totalStepsCompleted, 0) / treeSize) * 100)
|
||||
|
||||
return {
|
||||
stepId: stepInfoNode.id,
|
||||
content: contentResult,
|
||||
children: await Promise.all(
|
||||
stepInfoNode.children.map(transformToStep),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const tactic: Tactic = {
|
||||
name: context.name,
|
||||
courtType: context.courtType,
|
||||
root: await transformToStep(tree),
|
||||
}
|
||||
|
||||
const e = document.createElement("a")
|
||||
e.setAttribute(
|
||||
"href",
|
||||
"data:application/octet-stream," +
|
||||
encodeURIComponent(JSON.stringify(tactic, null, 2)),
|
||||
)
|
||||
e.setAttribute("download", `${context.name}.json`)
|
||||
e.style.display = "none"
|
||||
|
||||
document.body.appendChild(e)
|
||||
|
||||
e.click()
|
||||
|
||||
document.body.removeChild(e)
|
||||
}
|
||||
|
||||
interface ProgressBarProps {
|
||||
percentage: number
|
||||
}
|
||||
|
||||
function ProgressBar({ percentage }: ProgressBarProps) {
|
||||
return (
|
||||
<div className={"progressbar"} style={{ display: "flex", height: 3 }}>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "#f5992b",
|
||||
width: `${percentage}%`,
|
||||
}}></div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "white",
|
||||
width: `${100 - percentage}%`,
|
||||
}}></div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
.popup {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
|
||||
background: rgba(49, 36, 36, 0.53);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(1.7px);
|
||||
-webkit-backdrop-filter: blur(1.7px);
|
||||
}
|
||||
|
||||
.popup-card {
|
||||
border: 1px solid rgba(71, 71, 86, 0.72);
|
||||
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
overflow: hidden;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.popup-exports {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
height: 100%;
|
||||
background: rgba(49, 36, 36, 0.53);
|
||||
backdrop-filter: blur(1.7px);
|
||||
-webkit-backdrop-filter: blur(1.7px);
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.export-card {
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: white;
|
||||
|
||||
align-items: center;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.export-card .json-logo {
|
||||
width: 80% !important;
|
||||
height: 80% !important;
|
||||
}
|
||||
|
||||
.export-card .json-logo * {
|
||||
fill: #f5992b;
|
||||
}
|
Loading…
Reference in new issue