Add text import export for toolbox modules
This commit is contained in:
parent
a9b5ca423a
commit
117b7b79e1
27 changed files with 1430 additions and 181 deletions
|
|
@ -1,10 +1,9 @@
|
|||
// Rôle : fournit l'outil checklist avec quantités, catégories et imports texte.
|
||||
import { useCallback, useState } from "react";
|
||||
// Rôle : fournit l'outil checklist avec quantités, catégories et échange texte.
|
||||
import { useState } from "react";
|
||||
import { Icon } from "../../../components/Icon.jsx";
|
||||
import { useGroupedReorder, moveItem } from "../../../hooks/useGroupedReorder.js";
|
||||
import { useInlineEdit } from "../../../hooks/useInlineEdit.js";
|
||||
import { TextImportModal } from "./TextImportModal.jsx";
|
||||
import { parseColonImportLines } from "./textImport.js";
|
||||
import { TextExchangeActions } from "./TextExchangeActions.jsx";
|
||||
|
||||
export function ChecklistModule({ toolboxId, moduleId, context, editing }) {
|
||||
const data = context.normalizeChecklistData(context.getModuleData(toolboxId, moduleId, { items: [] }));
|
||||
|
|
@ -12,9 +11,6 @@ export function ChecklistModule({ toolboxId, moduleId, context, editing }) {
|
|||
const [label, setLabel] = useState("");
|
||||
const [sectionTitle, setSectionTitle] = useState("");
|
||||
const [qty, setQty] = useState(1);
|
||||
const [textImport, setTextImport] = useState("");
|
||||
const [importOpen, setImportOpen] = useState(false);
|
||||
const closeImportModal = useCallback(() => setImportOpen(false), []);
|
||||
const namedSections = data.sections.filter((section) => section.title);
|
||||
const completedSectionsMode = data.hideCompletedSectionsFully ? "hidden" : data.hideCompletedSections ? "reduced" : "visible";
|
||||
const reorderItems = getChecklistReorderItems(data.sections);
|
||||
|
|
@ -84,17 +80,6 @@ export function ChecklistModule({ toolboxId, moduleId, context, editing }) {
|
|||
setQty(1);
|
||||
}
|
||||
|
||||
function importItems(event) {
|
||||
event.preventDefault();
|
||||
const importedSections = parseChecklistImport(textImport, context);
|
||||
if (!importedSections.length) return;
|
||||
const nextSections = importedSections.reduce((sections, section) => appendItems(sections, section.title, section.items), data.sections);
|
||||
save(nextSections);
|
||||
setTextImport("");
|
||||
setImportOpen(false);
|
||||
context.notify?.("Import checklist terminé.");
|
||||
}
|
||||
|
||||
function setCompletedSectionsMode(mode) {
|
||||
save(data.sections.map((section) => ({ ...section, hideWhenComplete: undefined })), {
|
||||
hideCompletedSections: mode === "reduced",
|
||||
|
|
@ -132,25 +117,9 @@ export function ChecklistModule({ toolboxId, moduleId, context, editing }) {
|
|||
<input className="checklist-qty-input" name="qty" type="number" min="1" value={qty} onChange={(event) => setQty(event.target.value)} aria-label={textContent.quantityLabel || "Quantité cible"} />
|
||||
<button className="primary">{textContent.addButton || "Ajouter"}</button>
|
||||
</form>
|
||||
<div className="text-import-actions">
|
||||
<button type="button" onClick={() => setImportOpen(true)}>
|
||||
<Icon name="import" />
|
||||
<span>{textContent.importOpenButton || "Importer du texte"}</span>
|
||||
</button>
|
||||
</div>
|
||||
<TextExchangeActions type="checklist" data={data} context={context} textContent={textContent} onImport={(nextData) => context.setModuleData(toolboxId, moduleId, nextData, "checklist")} />
|
||||
</div>
|
||||
)}
|
||||
{importOpen && (
|
||||
<TextImportModal
|
||||
title={textContent.importModalTitle || "Importer une checklist"}
|
||||
value={textImport}
|
||||
onChange={setTextImport}
|
||||
placeholder={textContent.importPlaceholder || "Importer plusieurs items...\nPotion:10\nMéga potion:5"}
|
||||
submitLabel={textContent.importButton || "Importer le texte"}
|
||||
onSubmit={importItems}
|
||||
onClose={closeImportModal}
|
||||
/>
|
||||
)}
|
||||
{namedSections.length > 0 && (
|
||||
<div className="checklist-toolbar" role="radiogroup" aria-label={textContent.completedSectionsModeTitle || "Affichage des catégories terminées"}>
|
||||
{[
|
||||
|
|
@ -211,32 +180,6 @@ export function ChecklistModule({ toolboxId, moduleId, context, editing }) {
|
|||
);
|
||||
}
|
||||
|
||||
function parseChecklistImport(text, context) {
|
||||
const sections = [];
|
||||
let currentSection = { id: context.uid("section"), title: "", items: [] };
|
||||
|
||||
String(text || "").split(/\r?\n/).forEach((line) => {
|
||||
const cleanLine = line.trim();
|
||||
if (!cleanLine) return;
|
||||
if (cleanLine.startsWith("#")) {
|
||||
if (currentSection.items.length || currentSection.title) sections.push(currentSection);
|
||||
currentSection = { id: context.uid("section"), title: cleanLine.replace(/^#+/, "").trim(), items: [] };
|
||||
return;
|
||||
}
|
||||
parseColonImportLines(cleanLine).forEach((entry) => {
|
||||
currentSection.items.push({
|
||||
id: context.uid("item"),
|
||||
label: entry.label,
|
||||
qtyTarget: Math.max(1, Number.parseInt(entry.value, 10) || 1),
|
||||
qtyCurrent: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (currentSection.items.length || currentSection.title) sections.push(currentSection);
|
||||
return sections.filter((section) => section.items.length);
|
||||
}
|
||||
|
||||
function isSectionComplete(section, context) {
|
||||
return section.items.length > 0 && section.items.every((item) => context.clampQty(item.qtyCurrent, item.qtyTarget) >= item.qtyTarget);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue