Update toolbox library examples and interactions
All checks were successful
Deploy Sokko G / deploy (push) Successful in 6s

This commit is contained in:
Shinuwa 2026-08-03 13:15:43 +02:00
parent 1a35dcae7b
commit b52f1887dc
5 changed files with 4186 additions and 4144 deletions

View file

@ -977,29 +977,33 @@
"items": [
{
"id": "i1",
"label": "Arme principale",
"label": "Epee de diamant",
"qtyCurrent": 1
},
{
"id": "i2",
"label": "Arme secondaire",
"label": "Bouclier",
"qtyCurrent": 1
},
{
"id": "i3",
"label": "Casque"
"label": "Arc"
},
{
"id": "i4",
"label": "Armure"
"label": "Casque en diamant"
},
{
"id": "i5",
"label": "Gants"
"label": "Armure en diamant"
},
{
"id": "i6",
"label": "Bottes"
"label": "Gants en diamant"
},
{
"id": "i7",
"label": "Bottes en diamant"
}
],
"title": "Equipements"
@ -1007,22 +1011,20 @@
{
"id": "g2",
"items": [
{
"id": "i7",
"label": "Potion",
"qtyTarget": 10,
"qtyCurrent": 9
},
{
"id": "i8",
"label": "Ether",
"qtyTarget": 10,
"qtyCurrent": 7
"label": "Fleches",
"qtyTarget": 64
},
{
"id": "i9",
"label": "Queue de Phénix",
"qtyTarget": 5
"label": "Potion de soin renforcée",
"qtyTarget": 20
},
{
"id": "ia",
"label": "Perle de l'Ender",
"qtyTarget": 16
}
]
}
@ -1084,42 +1086,6 @@
"combos": [
{
"id": "o1",
"name": "Shoryken",
"inputs": [
[
{
"kind": "direction",
"value": "right"
}
],
[
{
"kind": "direction",
"value": "down"
}
],
[
{
"kind": "direction",
"value": "down-right"
}
],
[
{
"kind": "direction",
"value": "right"
},
{
"kind": "button",
"value": "square"
}
]
],
"category": "Ryu",
"device": "playstation"
},
{
"id": "o2",
"name": "Hadoken",
"inputs": [
[
@ -1149,13 +1115,25 @@
"device": "playstation"
},
{
"id": "o3",
"name": "Kikoken",
"id": "o2",
"name": "Shoryken",
"inputs": [
[
{
"kind": "direction",
"value": "left"
"value": "right"
}
],
[
{
"kind": "direction",
"value": "down"
}
],
[
{
"kind": "direction",
"value": "down-right"
}
],
[
@ -1165,7 +1143,32 @@
},
{
"kind": "button",
"value": "square"
"value": "fist"
}
]
],
"category": "Ryu",
"device": "playstation"
},
{
"id": "o3",
"name": "Kikoken",
"inputs": [
[
{
"kind": "direction",
"value": "left",
"hold": true
}
],
[
{
"kind": "direction",
"value": "right"
},
{
"kind": "button",
"value": "fist"
}
]
],
@ -1192,12 +1195,10 @@
{
"kind": "direction",
"value": "right"
}
],
[
},
{
"kind": "button",
"value": "circle"
"value": "foot"
}
]
],
@ -1214,30 +1215,31 @@
"entries": [
{
"id": "r1",
"label": "Lingot d'acier",
"label": "Lingot de champignite",
"value": 100
},
{
"id": "r2",
"label": "Fragment de fer",
"value": 500,
"label": "Lingot de chlorophyte",
"value": 100,
"parentId": "r1"
},
{
"id": "r3",
"label": "Charbon",
"value": 500,
"label": "Champignon luisant",
"value": 1500,
"parentId": "r1"
},
{
"id": "r4",
"label": "Diamant",
"value": 50
"label": "Lingot de chlorophyte",
"value": 100
},
{
"id": "r5",
"label": "Rubis",
"value": 50
"label": "Minerai de chlorophyte",
"value": 500,
"parentId": "r4"
}
]
},

View file

@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="#000" d="M7.4 3.1c.9 0 1.6.7 1.6 1.6v5.1h.6V3.6c0-.9.7-1.6 1.6-1.6s1.6.7 1.6 1.6v6.2h.6V4.2c0-.9.7-1.6 1.6-1.6s1.6.7 1.6 1.6v6h.6V6.1c0-.9.7-1.6 1.6-1.6s1.6.7 1.6 1.6v7.2c0 5.2-2.7 8.7-7.1 8.7h-2.6c-2.5 0-4.4-1.2-5.7-3.3L2.9 15c-.6-.8-.4-1.8.4-2.4.8-.6 1.8-.4 2.4.3l1.1 1.3V4.7c0-.9.7-1.6 1.6-1.6z"/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="4.5 2.2 21 27.6">
<path fill="#000" d="M23,9.169V6.219l-4-1v-2.5l-6,1.5v2.869C10.462,7.498,9.406,9.439,9.102,11H5v12h4.167C9.5,24.006,10.361,25,12,25h1v2.781l6,1.5v-1.5l4-1v-1.949c1.006-0.333,2-1.193,2-2.831V12C25,10.361,24.006,9.502,23,9.169z M21,7.781V9h-2V7.281L21,7.781z M15,5.781l2-0.5V9h-2V5.781z M15,25h2v1.719l-2-0.5V25z M21,25.219l-2,0.5V25h2V25.219z M21.992,23H12c-0.806,0-0.988-0.55-1-1v-1H7v-8.006h3.988L11,12.005c0.005-0.434,0.134-2.369,2-2.881V11h8.988C22.45,11.012,23,11.195,23,12v1h-2v2h2v1h-2v2h2v1h-2v2h2v1C23,22.803,22.453,22.987,21.992,23z"/>
</svg>

Before

Width:  |  Height:  |  Size: 383 B

After

Width:  |  Height:  |  Size: 621 B

Before After
Before After

View file

@ -33,16 +33,16 @@ const LIBRARY_TOOLBOX_ID = "library";
const MODULE_COMPONENTS = {
notepad: { label: "Bloc notes", mode: "Texte riche", icon: "notepad", Component: NotepadModule },
checklist: { label: "Checklist", mode: "Catégories + sans catégorie", icon: "checklist", Component: ChecklistModule, scrollable: true },
images: { label: "Images", mode: "Média", icon: "picture", Component: ImagesModule, scrollable: true },
links: { label: "Liens", mode: "Liste simple", icon: "link", Component: LinksModule, scrollable: true },
counters: { label: "Compteurs", mode: "Valeurs", icon: "abacus", Component: CountersModule, scrollable: true },
combos: { label: "Combos", mode: "Catégories", icon: "controller", Component: CombosModule, scrollable: true },
checklist: { label: "Checklist", mode: "Catégories + sans catégorie", icon: "checklist", Component: ChecklistModule, editable: true, scrollable: true },
images: { label: "Images", mode: "Média", icon: "picture", Component: ImagesModule, editable: true, scrollable: true },
links: { label: "Liens", mode: "Liste simple", icon: "link", Component: LinksModule, editable: true, scrollable: true },
counters: { label: "Compteurs", mode: "Valeurs", icon: "abacus", Component: CountersModule, editable: true, scrollable: true },
combos: { label: "Combos", mode: "Catégories", icon: "controller", Component: CombosModule, editable: true, scrollable: true },
calculator: { label: "Calculateur", mode: "Arborescence", icon: "calculator", Component: CalculatorModule },
table: { label: "Tableau", mode: "Grille + formules", icon: "table", Component: TableModule, scrollable: true },
timer: { label: "Timer", mode: "Chrono + comptes à rebours", icon: "clock", Component: TimerModule },
taskPlanner: { label: "Planificateur de tâches", mode: "Catégories + parent/enfant", icon: "tasklist", Component: TaskPlannerModule, scrollable: true },
imageAnnotation: { label: "Annotation d'images", mode: "Image + marqueurs", icon: "map", Component: ImageAnnotationModule }
taskPlanner: { label: "Planificateur de tâches", mode: "Catégories + parent/enfant", icon: "tasklist", Component: TaskPlannerModule, editable: true, scrollable: true },
imageAnnotation: { label: "Annotation d'images", mode: "Image + marqueurs", icon: "map", Component: ImageAnnotationModule, editable: true }
};
function cloneData(value) {
@ -51,6 +51,15 @@ function cloneData(value) {
return JSON.parse(JSON.stringify(value));
}
function fileToDataUrl(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener("load", () => resolve(String(reader.result || "")));
reader.addEventListener("error", () => reject(reader.error || new Error("Image illisible.")));
reader.readAsDataURL(file);
});
}
function createModuleDataFromPayload(payload) {
return Object.fromEntries((payload?.toolbox?.modules || []).map((module) => [
`${LIBRARY_TOOLBOX_ID}:${module.id}`,
@ -134,11 +143,33 @@ export function LibraryPage({ siteContent, actions }) {
return true;
},
notify: actions.notify,
compressImageFile: async () => "",
addImageFiles: async () => false,
compressImageFile: fileToDataUrl,
addImageFiles: async (toolboxId, moduleId, files) => {
const imageFiles = [...files].filter((file) => file?.type?.startsWith("image/"));
if (!imageFiles.length) return false;
const images = await Promise.all(imageFiles.map(async (file) => ({
id: uid("image"),
label: file.name?.replace(/\.[^.]+$/, "").trim() || "",
dataUrl: await fileToDataUrl(file)
})));
setModuleData((current) => {
const key = `${toolboxId}:${moduleId}`;
const data = current[key] || { images: [] };
return { ...current, [key]: { ...data, images: [...(data.images || []), ...images] } };
});
return true;
},
setImage: actions.setImage,
createImageAnnotationModule: () => {}
}), [actions, moduleData, siteContent.toolboxes.modules]);
createImageAnnotationModule: (dataUrl) => {
const annotationModule = libraryPayload?.toolbox?.modules?.find((module) => module.type === "imageAnnotation");
if (!annotationModule || !dataUrl) return;
setModuleData((current) => ({
...current,
[`${LIBRARY_TOOLBOX_ID}:${annotationModule.id}`]: { image: dataUrl, markers: [], drawings: { strokes: [] } }
}));
requestAnimationFrame(() => scrollToLibraryTool(getLibraryAnchorId(annotationModule), true));
}
}), [actions, libraryPayload, moduleData, siteContent.toolboxes.modules]);
return (
<div className="library-page">
@ -209,6 +240,7 @@ function LibraryToolExample({ module, sourceData, description, context }) {
const definition = MODULE_COMPONENTS[module.type] || MODULE_COMPONENTS.notepad;
const Component = definition.Component;
const [scrollable, setScrollable] = useState(false);
const [editing, setEditing] = useState(false);
const title = module.title || definition.label || "Outil";
return (
@ -243,13 +275,25 @@ function LibraryToolExample({ module, sourceData, description, context }) {
<i aria-hidden="true" />
</button>
)}
{definition.editable && (
<button
className={`module-edit-button ${editing ? "active" : ""}`}
type="button"
onClick={() => setEditing((value) => !value)}
aria-label={`${editing ? "Masquer" : "Afficher"} l'ajout de ${title}`}
aria-pressed={editing}
title={editing ? "Masquer l'ajout" : "Ajouter"}
>
<Icon name="add" />
</button>
)}
<button className="library-reset-button" type="button" onClick={() => context.setModuleData(LIBRARY_TOOLBOX_ID, module.id, cloneData(sourceData))} aria-label={`Réinitialiser ${title}`} title="Réinitialiser l'exemple">
<Icon name="refresh" />
</button>
</div>
</header>
<div className={`module-content ${scrollable ? "is-scrollable legacy-scrollbar" : ""}`}>
<Component toolboxId={LIBRARY_TOOLBOX_ID} moduleId={module.id} context={context} editing={false} />
<Component toolboxId={LIBRARY_TOOLBOX_ID} moduleId={module.id} context={context} editing={editing} />
</div>
</article>
</section>

View file

@ -380,10 +380,6 @@ span {
padding: var(--space-4);
}
.library-tool-example .module-add-panel {
display: none;
}
.library-tool-example .image-annotation-stage,
.library-tool-example .images-grid img {
max-height: 260px;

View file

@ -3208,12 +3208,12 @@ button.combo-device-n64.combo-value-down-right:hover {
}
.combo-value-grab .ui-icon {
width: 21px;
height: 21px;
width: 24px;
height: 22px;
}
button.combo-input-token.combo-value-grab .ui-icon {
width: 23px;
width: 25px;
height: 23px;
}