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) => (
+
+ ))}
+
+ )}
+
+
+
+
+ );
+}
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) => (
-
- ))}
-
- )}
-
-
-
-
+
{
+ 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"}
+ >
+
+ {Array.from({ length: data.columns }, (_, columnIndex) => (
+
onColumnLabelChange(columnIndex, value)}
+ aria-label={`${textContent.columnLabel || "Intitulé de colonne"} ${columnIndexToName(columnIndex)}`}
+ title={`${columnIndexToName(columnIndex)} - ${textContent.columnFormulaTitle || "Référence conservée dans les formules"}`}
+ />
+ ))}
+ {Array.from({ length: data.rows }, (_, rowIndex) => (
+ {
+ const parsed = parseCellAddress(cell);
+ if (parsed && !selection) onSelectCell(parsed.rowIndex, parsed.columnIndex);
+ }}
+ onBlur={(cell) => {
+ if (editingCell === cell) onEditingCellClear();
+ }}
+ onCellChange={onCellChange}
+ onFormulaSelectionStart={onFormulaSelectionStart}
+ onFormulaSelectionExtend={onFormulaSelectionExtend}
+ onStartMoveSelection={onStartMoveSelection}
+ onMoveSelection={onMoveSelection}
+ onSelectCell={onSelectCell}
+ onExtendSelection={onExtendSelection}
+ onPasteSelection={onPasteSelection}
+ draggingSelectionRef={draggingSelectionRef}
+ onCellKeyDown={onCellKeyDown}
+ onStartEditing={onStartEditing}
+ registerInput={registerInput}
+ />
+ ))}
+
+
+ );
+}
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"}
- >
-
- {Array.from({ length: data.columns }, (_, columnIndex) => (
-
setColumnLabel(columnIndex, value)}
- aria-label={`${textContent.columnLabel || "Intitulé de colonne"} ${columnIndexToName(columnIndex)}`}
- title={`${columnIndexToName(columnIndex)} - ${textContent.columnFormulaTitle || "Référence conservée dans les formules"}`}
- />
- ))}
- {Array.from({ length: data.rows }, (_, rowIndex) => (
- {
- const parsed = parseCellAddress(cell);
- if (parsed && !selection) selectCell(parsed.rowIndex, parsed.columnIndex);
- }}
- onBlur={(cell) => {
- if (editingCell === cell) setEditingCell("");
- }}
- onCellChange={setCellValue}
- onFormulaSelectionStart={startFormulaSelection}
- onFormulaSelectionExtend={extendFormulaSelection}
- onStartMoveSelection={startMovingSelection}
- onMoveSelection={updateMovePreview}
- onSelectCell={selectCell}
- onExtendSelection={extendSelection}
- onPasteSelection={pasteSelection}
- draggingSelectionRef={draggingSelectionRef}
- onCellKeyDown={handleCellKeyDown}
- onStartEditing={startEditing}
- registerInput={registerInput}
- />
- ))}
-
-
+ setEditingCell("")}
+ registerInput={registerInput}
+ />
);
}