From e512eef232ad6043f23f91dc1d7acf596e81eaa5 Mon Sep 17 00:00:00 2001 From: Shinuwa Date: Mon, 24 Aug 2026 23:01:35 +0200 Subject: [PATCH] Extract combo editor and table grid --- tests/static-toolboxes.test.mjs | 11 +- .../toolboxes/modules/CombosEditor.jsx | 194 ++++++++++++++++++ .../toolboxes/modules/CombosModule.jsx | 193 ++++------------- .../features/toolboxes/modules/TableGrid.jsx | 92 +++++++++ .../toolboxes/modules/TableModule.jsx | 87 +++----- 5 files changed, 360 insertions(+), 217 deletions(-) create mode 100644 website/src/features/toolboxes/modules/CombosEditor.jsx create mode 100644 website/src/features/toolboxes/modules/TableGrid.jsx diff --git a/tests/static-toolboxes.test.mjs b/tests/static-toolboxes.test.mjs index b2eb214..71c8154 100644 --- a/tests/static-toolboxes.test.mjs +++ b/tests/static-toolboxes.test.mjs @@ -140,15 +140,17 @@ test("toolbox module registry and modules expose expected behavior", async () => const moduleMetadataRegistry = await readFile("website/src/features/toolboxes/toolboxModuleRegistry.js", "utf8"); const notepadModule = await readFile("website/src/features/toolboxes/modules/NotepadModule.jsx", "utf8"); const combosModule = await readFile("website/src/features/toolboxes/modules/CombosModule.jsx", "utf8"); + const combosEditor = await readFile("website/src/features/toolboxes/modules/CombosEditor.jsx", "utf8"); const combosParts = await readFile("website/src/features/toolboxes/modules/CombosParts.jsx", "utf8"); const combosList = await readFile("website/src/features/toolboxes/modules/CombosList.jsx", "utf8"); - const combosBundle = `${combosModule}\n${combosParts}\n${combosList}`; + const combosBundle = `${combosModule}\n${combosEditor}\n${combosParts}\n${combosList}`; const combosUtils = await readFile("website/src/features/toolboxes/modules/combosUtils.js", "utf8"); const calculatorModule = await readFile("website/src/features/toolboxes/modules/CalculatorModule.jsx", "utf8"); const tableModule = await readFile("website/src/features/toolboxes/modules/TableModule.jsx", "utf8"); + const tableGrid = await readFile("website/src/features/toolboxes/modules/TableGrid.jsx", "utf8"); const tableGridParts = await readFile("website/src/features/toolboxes/modules/TableGridParts.jsx", "utf8"); const tableToolbar = await readFile("website/src/features/toolboxes/modules/TableToolbar.jsx", "utf8"); - const tableBundle = `${tableModule}\n${tableGridParts}\n${tableToolbar}`; + const tableBundle = `${tableModule}\n${tableGrid}\n${tableGridParts}\n${tableToolbar}`; const tableUtils = await readFile("website/src/features/toolboxes/modules/tableUtils.js", "utf8"); const tableFormulaEngine = await readFile("website/src/features/toolboxes/modules/tableFormulaEngine.js", "utf8"); const timerModule = await readFile("website/src/features/toolboxes/modules/TimerModule.jsx", "utf8"); @@ -258,9 +260,9 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(combosUtils, /grab/); assert.match(combosUtils, /protect/); assert.match(combosUtils, /shoot/); - assert.match(combosModule, /holdMs/); + assert.match(combosBundle, /holdMs/); assert.match(combosModule, /toggleSelectedInputHold/); - assert.match(combosModule, /combo-hold-field/); + assert.match(combosEditor, /combo-hold-field/); assert.match(combosParts, /useInlineEdit/); assert.match(combosModule, /useGroupedReorder/); assert.match(combosModule, /reorderFeatures/); @@ -316,6 +318,7 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(tableUtils, /parseTsvBlock/); assert.match(tableGridParts, /is-move-target/); assert.match(tableUtils, /isMultiCellSelection/); + assert.match(tableGrid, /export function TableGrid/); assert.match(tableModule, /handleCellKeyDown/); assert.match(tableModule, /clearSelection/); assert.match(tableModule, /event\.key === "Delete"/); diff --git a/website/src/features/toolboxes/modules/CombosEditor.jsx b/website/src/features/toolboxes/modules/CombosEditor.jsx new file mode 100644 index 0000000..8b44a47 --- /dev/null +++ b/website/src/features/toolboxes/modules/CombosEditor.jsx @@ -0,0 +1,194 @@ +// Rôle : rend le formulaire de création et édition des combos. +import { Icon } from "../../../components/Icon.jsx"; +import { ComboInputToken, ComboSequence } from "./CombosParts.jsx"; +import { + DEVICE_OPTIONS, + createInput, + getHoldSeconds +} from "./combosUtils.js"; + +export function CombosEditor({ + data, + textContent, + comboCategory, + comboName, + customKey, + keyboardLayout, + paletteColumns, + draftInputs, + cleanDraftInputs, + activeStepIndex, + selectedInput, + selectedDraftInput, + simultaneousMode, + editingComboId, + onDeviceChange, + onKeyboardLayoutChange, + onComboCategoryChange, + onComboNameChange, + onCustomKeyChange, + onCustomKeyAdd, + onComboSave, + onDraftInputAdd, + onPaletteInputDrag, + onDraftInputDrag, + onInputDrop, + onNewStepDrop, + onStepSelect, + onInputSelect, + onSelectedInputHoldToggle, + onSelectedInputHoldChange, + onSimultaneousModeToggle, + onLastInputRemove, + onActiveStepRemove, + onComboEditCancel +}) { + return ( +
+
+ + {data.device === "keyboardMouse" && ( +
+ {[ + { value: "azerty", label: "AZERTY" }, + { value: "qwerty", label: "QWERTY" } + ].map((layout) => ( + + ))} +
+ )} +
+ +
+
+ + +
+
+ {paletteColumns.map((columnGroups, columnIndex) => ( +
+ {columnGroups.map((group) => ( +
+ {group.title} +
+ {group.inputs.map((input, inputIndex) => { + if (!input) return
+
+ ))} +
+ ))} +
+ + {data.device === "keyboardMouse" && ( +
+ onCustomKeyChange(event.target.value)} placeholder={textContent.customKeyPlaceholder || "Touche personnalisée"} /> + +
+ )} + +
+ +
+ + + + +
+ {editingComboId && ( + + )} + +
+
+
+
+
+ ); +} diff --git a/website/src/features/toolboxes/modules/CombosModule.jsx b/website/src/features/toolboxes/modules/CombosModule.jsx index de394cd..2ee0d8c 100644 --- a/website/src/features/toolboxes/modules/CombosModule.jsx +++ b/website/src/features/toolboxes/modules/CombosModule.jsx @@ -1,20 +1,17 @@ // Rôle : fournit l'outil Combos avec palettes d'inputs, rendu visuel et échange texte. import { useMemo, useState } from "react"; -import { Icon } from "../../../components/Icon.jsx"; import { applyGroupedReorderOperation, getGroupedEntries, useGroupedReorder } from "../../../hooks/useGroupedReorder.js"; import { TextExchangeActions } from "./TextExchangeActions.jsx"; -import { ComboInputToken, ComboSequence } from "./CombosParts.jsx"; +import { CombosEditor } from "./CombosEditor.jsx"; import { CombosList } from "./CombosList.jsx"; import { - DEVICE_OPTIONS, createCombo, createInput, getComboCategory, - getHoldSeconds, getPaletteColumns, getPaletteGroups, normalizeCustomKey, @@ -326,155 +323,45 @@ export function CombosModule({ toolboxId, moduleId, context, editing, textExchan return (
{(editing || editingComboId) && ( -
-
- - {data.device === "keyboardMouse" && ( -
- {[ - { value: "azerty", label: "AZERTY" }, - { value: "qwerty", label: "QWERTY" } - ].map((layout) => ( - - ))} -
- )} -
- -
-
- - -
-
- {paletteColumns.map((columnGroups, columnIndex) => ( -
- {columnGroups.map((group) => ( -
- {group.title} -
- {group.inputs.map((input, inputIndex) => { - if (!input) return
-
- ))} -
- ))} -
- - {data.device === "keyboardMouse" && ( -
- setCustomKey(event.target.value)} placeholder={textContent.customKeyPlaceholder || "Touche personnalisée"} /> - -
- )} - -
- { - setActiveStepIndex(stepIndex); - setSelectedInput({ stepIndex, inputIndex }); - }} - onDropInput={dropInputOnStep} - onDropNewStep={dropInputAsNewStep} - onDragInput={dragDraftInput} - /> -
- - - - -
- {editingComboId && ( - - )} - -
-
-
-
-
+ { + setActiveStepIndex(stepIndex); + setSelectedInput({ stepIndex, inputIndex }); + }} + onSelectedInputHoldToggle={toggleSelectedInputHold} + onSelectedInputHoldChange={updateSelectedInputHold} + onSimultaneousModeToggle={() => setSimultaneousMode((enabled) => !enabled)} + onLastInputRemove={removeLastInput} + onActiveStepRemove={removeActiveStep} + onComboEditCancel={cancelComboEdit} + /> )} {textExchangeOpen && (
diff --git a/website/src/features/toolboxes/modules/TableGrid.jsx b/website/src/features/toolboxes/modules/TableGrid.jsx new file mode 100644 index 0000000..d568216 --- /dev/null +++ b/website/src/features/toolboxes/modules/TableGrid.jsx @@ -0,0 +1,92 @@ +// Rôle : rend la grille du tableau et connecte les cellules aux handlers du module. +import { columnIndexToName, parseCellAddress } from "./tableFormulaEngine.js"; +import { HeaderLabelInput, TableRow } from "./TableGridParts.jsx"; + +export function TableGrid({ + data, + textContent, + evaluatedCells, + editingCell, + highlightedCell, + selection, + movePreview, + columnWidths, + rowHeaderWidth, + draggingSelectionRef, + onColumnLabelChange, + onRowLabelChange, + onCellChange, + onFormulaSelectionStart, + onFormulaSelectionExtend, + onStartMoveSelection, + onMoveSelection, + onSelectCell, + onExtendSelection, + onPasteSelection, + onCellKeyDown, + onStartEditing, + onEditingCellClear, + registerInput +}) { + return ( +
+
`minmax(${width}px, 1fr)`).join(" ")}`, + "--table-min-width": `${rowHeaderWidth + columnWidths.reduce((total, width) => total + width, 0)}px` + }} + role="grid" + aria-label={textContent.gridLabel || "Tableau"} + > +
+
+ ); +} diff --git a/website/src/features/toolboxes/modules/TableModule.jsx b/website/src/features/toolboxes/modules/TableModule.jsx index d75b21c..faed251 100644 --- a/website/src/features/toolboxes/modules/TableModule.jsx +++ b/website/src/features/toolboxes/modules/TableModule.jsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { TextExchangeActions } from "./TextExchangeActions.jsx"; import { cellAddress, columnIndexToName, evaluateTable, parseCellAddress } from "./tableFormulaEngine.js"; -import { HeaderLabelInput, TableRow } from "./TableGridParts.jsx"; +import { TableGrid } from "./TableGrid.jsx"; import { TableToolbar } from "./TableToolbar.jsx"; import { DEFAULT_COLUMNS, @@ -410,65 +410,32 @@ export function TableModule({ toolboxId, moduleId, context, textExchangeOpen })
)} -
-
`minmax(${width}px, 1fr)`).join(" ")}`, - "--table-min-width": `${rowHeaderWidth + columnWidths.reduce((total, width) => total + width, 0)}px` - }} - role="grid" - aria-label={textContent.gridLabel || "Tableau"} - > -
-
+ setEditingCell("")} + registerInput={registerInput} + />
); }