diff --git a/tests/static-toolboxes.test.mjs b/tests/static-toolboxes.test.mjs index ffe4445..3ac475d 100644 --- a/tests/static-toolboxes.test.mjs +++ b/tests/static-toolboxes.test.mjs @@ -145,7 +145,9 @@ test("toolbox module registry and modules expose expected behavior", async () => 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${combosEditor}\n${combosParts}\n${combosList}`; + const combosEditorState = await readFile("website/src/features/toolboxes/modules/useCombosEditorState.js", "utf8"); + const combosReorder = await readFile("website/src/features/toolboxes/modules/useCombosReorder.js", "utf8"); + const combosBundle = `${combosModule}\n${combosEditor}\n${combosParts}\n${combosList}\n${combosEditorState}\n${combosReorder}`; 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 calculatorEntries = await readFile("website/src/features/toolboxes/modules/CalculatorEntries.jsx", "utf8"); @@ -155,27 +157,37 @@ test("toolbox module registry and modules expose expected behavior", async () => 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${tableGrid}\n${tableGridParts}\n${tableToolbar}`; + const tableInteractions = await readFile("website/src/features/toolboxes/modules/useTableInteractions.js", "utf8"); + const tableClipboard = await readFile("website/src/features/toolboxes/modules/useTableClipboard.js", "utf8"); + const tableBundle = `${tableModule}\n${tableGrid}\n${tableGridParts}\n${tableToolbar}\n${tableInteractions}\n${tableClipboard}`; 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"); const timerParts = await readFile("website/src/features/toolboxes/modules/TimerParts.jsx", "utf8"); const timerOptions = await readFile("website/src/features/toolboxes/modules/timerOptions.js", "utf8"); const calendarModule = await readFile("website/src/features/toolboxes/modules/CalendarModule.jsx", "utf8"); + const calendarContent = await readFile("website/src/features/toolboxes/modules/CalendarContent.jsx", "utf8"); const calendarDraft = await readFile("website/src/features/toolboxes/modules/calendarDraft.js", "utf8"); const calendarEventForm = await readFile("website/src/features/toolboxes/modules/CalendarEventForm.jsx", "utf8"); + const calendarEventControls = await readFile("website/src/features/toolboxes/modules/CalendarEventControls.jsx", "utf8"); + const calendarMonthDatePicker = await readFile("website/src/features/toolboxes/modules/CalendarMonthDatePicker.jsx", "utf8"); const calendarViews = await readFile("website/src/features/toolboxes/modules/CalendarViews.jsx", "utf8"); - const calendarBundle = `${calendarModule}\n${calendarDraft}\n${calendarEventForm}\n${calendarViews}`; + const calendarBundle = `${calendarModule}\n${calendarContent}\n${calendarDraft}\n${calendarEventForm}\n${calendarEventControls}\n${calendarMonthDatePicker}\n${calendarViews}`; const calendarUtils = await readFile("website/src/features/toolboxes/modules/calendarUtils.js", "utf8"); const taskPlannerModule = await readFile("website/src/features/toolboxes/modules/TaskPlannerModule.jsx", "utf8"); const taskPlannerItems = await readFile("website/src/features/toolboxes/modules/TaskPlannerItems.jsx", "utf8"); const taskPlannerItemPanels = await readFile("website/src/features/toolboxes/modules/TaskPlannerItemPanels.jsx", "utf8"); - const taskPlannerBundle = `${taskPlannerItems}\n${taskPlannerItemPanels}`; + const taskPlannerReorder = await readFile("website/src/features/toolboxes/modules/useTaskPlannerReorder.js", "utf8"); + const taskPlannerTopbar = await readFile("website/src/features/toolboxes/modules/TaskPlannerTopbar.jsx", "utf8"); + const taskPlannerSettings = await readFile("website/src/features/toolboxes/modules/TaskPlannerSettings.jsx", "utf8"); + const taskPlannerBundle = `${taskPlannerItems}\n${taskPlannerItemPanels}\n${taskPlannerReorder}\n${taskPlannerTopbar}\n${taskPlannerSettings}`; const taskPlannerUtils = await readFile("website/src/features/toolboxes/modules/taskPlannerUtils.js", "utf8"); const goalTreeModule = await readFile("website/src/features/toolboxes/modules/GoalTreeModule.jsx", "utf8"); const goalTreeEditor = await readFile("website/src/features/toolboxes/modules/GoalTreeEditor.jsx", "utf8"); const goalTreeParts = await readFile("website/src/features/toolboxes/modules/GoalTreeParts.jsx", "utf8"); - const goalTreeBundle = `${goalTreeModule}\n${goalTreeEditor}\n${goalTreeParts}`; + const goalTreeController = await readFile("website/src/features/toolboxes/modules/useGoalTreeController.js", "utf8"); + const goalTreeView = await readFile("website/src/features/toolboxes/modules/GoalTreeView.jsx", "utf8"); + const goalTreeBundle = `${goalTreeModule}\n${goalTreeEditor}\n${goalTreeParts}\n${goalTreeController}\n${goalTreeView}`; const goalTreeDisplay = await readFile("website/src/features/toolboxes/modules/goalTreeDisplay.js", "utf8"); const goalTreeUtils = await readFile("website/src/features/toolboxes/modules/goalTreeUtils.js", "utf8"); const equipmentPlannerModule = await readFile("website/src/features/toolboxes/modules/EquipmentPlannerModule.jsx", "utf8"); @@ -264,23 +276,23 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(combosUtils, /Nintendo 64/); assert.match(combosParts, /export function ComboInputToken/); assert.match(combosParts, /export function ComboSequence/); - assert.match(combosModule, /keyboardLayout/); - assert.match(combosModule, /simultaneousMode/); + assert.match(combosBundle, /keyboardLayout/); + assert.match(combosBundle, /simultaneousMode/); assert.match(combosUtils, /SPECIAL_INPUT_GROUP/); assert.match(combosUtils, /grab/); assert.match(combosUtils, /protect/); assert.match(combosUtils, /shoot/); assert.match(combosBundle, /holdMs/); - assert.match(combosModule, /toggleSelectedInputHold/); + assert.match(combosBundle, /toggleSelectedInputHold/); assert.match(combosEditor, /combo-hold-field/); assert.match(combosParts, /useInlineEdit/); - assert.match(combosModule, /useGroupedReorder/); - assert.match(combosModule, /reorderFeatures/); - assert.match(combosModule, /namespace: `combos-\$\{moduleId\}`/); + assert.match(combosBundle, /useGroupedReorder/); + assert.match(combosBundle, /reorderFeatures/); + assert.match(combosBundle, /namespace: `combos-\$\{moduleId\}`/); assert.match(combosBundle, /getGroupBoundaryProps/); assert.match(combosBundle, /combos-category-boundary-drop-zone/); assert.match(combosUtils, /function setComboItemCategory/); - assert.match(combosModule, /setItemGroup: setComboItemCategory/); + assert.match(combosBundle, /setItemGroup: setComboItemCategory/); assert.doesNotMatch(combosModule, /canMoveItem:/); assert.doesNotMatch(combosModule, /canMoveGroup:/); assert.match(calculatorModule, /export function CalculatorModule/); @@ -313,29 +325,29 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(tableBundle, /Icon name="add-column"/); assert.match(tableBundle, /Icon name="remove-row"/); assert.match(tableBundle, /Icon name="remove-column"/); - assert.match(tableModule, /insertFormulaReferenceSelection/); - assert.match(tableModule, /startFormulaSelection/); - assert.match(tableModule, /copyTsv/); + assert.match(tableBundle, /insertFormulaReferenceSelection/); + assert.match(tableBundle, /startFormulaSelection/); + assert.match(tableBundle, /copyTsv/); assert.match(tableBundle, /notepad-toolbar table-toolbar/); assert.match(tableBundle, /notepad-toolbar-group/); assert.match(tableBundle, /notepad-toolbar-button/); - assert.match(tableModule, /selection/); - assert.match(tableModule, /draggingSelectionRef/); - assert.match(tableModule, /movingSelectionRef/); - assert.match(tableModule, /copiedSelectionRef/); + assert.match(tableBundle, /selection/); + assert.match(tableBundle, /draggingSelectionRef/); + assert.match(tableBundle, /movingSelectionRef/); + assert.match(tableBundle, /copiedSelectionRef/); assert.match(tableUtils, /moveSelectionCells/); assert.match(tableUtils, /pasteCellBlock/); 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"/); - assert.match(tableModule, /ArrowUp/); - assert.match(tableModule, /ArrowDown/); - assert.match(tableModule, /ArrowLeft/); - assert.match(tableModule, /ArrowRight/); + assert.match(tableBundle, /handleCellKeyDown/); + assert.match(tableBundle, /clearSelection/); + assert.match(tableBundle, /event\.key === "Delete"/); + assert.match(tableBundle, /ArrowUp/); + assert.match(tableBundle, /ArrowDown/); + assert.match(tableBundle, /ArrowLeft/); + assert.match(tableBundle, /ArrowRight/); assert.match(tableGridParts, /export function HeaderLabelInput/); assert.match(tableGridParts, /useInlineEdit/); assert.match(tableModule, /TextExchangeActions/); @@ -371,8 +383,8 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(calendarBundle, /durationMinutes < 120/); assert.match(calendarBundle, /is-compact/); assert.match(calendarModule, /handleKeyDown/); - assert.match(calendarModule, /TextExchangeActions/); - assert.match(calendarModule, /type="calendar"/); + assert.match(calendarContent, /TextExchangeActions/); + assert.match(calendarContent, /type="calendar"/); assert.match(calendarBundle, /repeatLabel/); assert.match(calendarBundle, /CalendarAlertModeSwitch/); assert.match(calendarDraft, /export function createDefaultDraft/); @@ -387,10 +399,10 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(taskPlannerModule, /normalizeTaskPlannerData/); assert.match(taskPlannerModule, /applyDueResets/); assert.match(taskPlannerModule, /TaskPlannerBranch/); - assert.match(taskPlannerModule, /useGroupedReorder/); + assert.match(taskPlannerBundle, /useGroupedReorder/); assert.match(taskPlannerModule, /useDraftForm/); - assert.match(taskPlannerModule, /TextExchangeActions/); - assert.match(taskPlannerModule, /reorderFeatures/); + assert.match(taskPlannerBundle, /TextExchangeActions/); + assert.match(taskPlannerBundle, /reorderFeatures/); assert.match(taskPlannerItems, /export function TaskPlannerBranch/); assert.match(taskPlannerItems, /function TaskPlannerItem/); assert.match(taskPlannerBundle, /task-planner-warning/); @@ -403,10 +415,10 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.doesNotMatch(taskPlannerModule, /canMoveItem:/); assert.doesNotMatch(taskPlannerModule, /canMoveGroup:/); assert.match(goalTreeModule, /export function GoalTreeModule/); - assert.match(goalTreeModule, /normalizeGoalTreeData/); - assert.match(goalTreeModule, /applyGoalTreeDueResets/); + assert.match(goalTreeBundle, /normalizeGoalTreeData/); + assert.match(goalTreeBundle, /applyGoalTreeDueResets/); assert.match(goalTreeModule, /TextExchangeActions/); - assert.match(goalTreeModule, /DiagramConnectors/); + assert.match(goalTreeBundle, /DiagramConnectors/); assert.match(goalTreeEditor, /export function GoalTreeEditor/); assert.match(goalTreeEditor, /Tabs/); assert.match(goalTreeEditor, /GoalTreeFormulaInput/); @@ -419,20 +431,20 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(goalTreeParts, /GoalTreeChecklistInlineControl/); assert.match(goalTreeParts, /GoalTreeStepLeafControl/); assert.match(goalTreeParts, /export function GoalTreePrerequisitePicker/); - assert.match(goalTreeModule, /function toggleBranch/); - assert.match(goalTreeModule, /function editNode/); - assert.match(goalTreeModule, /function addNodeContentItem/); - assert.match(goalTreeModule, /closedSiblingIds/); - assert.match(goalTreeModule, /useGroupedReorder/); - assert.match(goalTreeModule, /const treeOrientation = data\.orientation \|\| "horizontal"/); - assert.match(goalTreeModule, /const reorderOrientation = treeOrientation === "horizontal" \? "vertical" : "horizontal"/); - assert.match(goalTreeModule, /orientation: reorderOrientation/); - assert.match(goalTreeModule, /orientation={treeOrientation}/); - assert.match(goalTreeModule, /module-scroll-button goal-tree-orientation-switch/); - assert.match(goalTreeModule, /Icon name="rotate"/); - assert.match(goalTreeModule, /function toggleLinkedParent/); + assert.match(goalTreeBundle, /function toggleBranch/); + assert.match(goalTreeBundle, /function editNode/); + assert.match(goalTreeBundle, /function addNodeContentItem/); + assert.match(goalTreeBundle, /closedSiblingIds/); + assert.match(goalTreeBundle, /useGroupedReorder/); + assert.match(goalTreeBundle, /const treeOrientation = data\.orientation \|\| "horizontal"/); + assert.match(goalTreeBundle, /const reorderOrientation = treeOrientation === "horizontal" \? "vertical" : "horizontal"/); + assert.match(goalTreeBundle, /orientation: reorderOrientation/); + assert.match(goalTreeView, /orientation={treeOrientation}/); + assert.match(goalTreeView, /module-scroll-button goal-tree-orientation-switch/); + assert.match(goalTreeView, /Icon name="rotate"/); + assert.match(goalTreeBundle, /function toggleLinkedParent/); assert.match(goalTreeDisplay, /grouped: false/); - assert.match(goalTreeModule, /getGoalTreeConvergenceKeepOpenIds/); + assert.match(goalTreeBundle, /getGoalTreeConvergenceKeepOpenIds/); assert.match(goalTreeDisplay, /progress\.current/); assert.match(goalTreeDisplay, /label: textContent\.itemLabelPlaceholder \|\| "Élément"/); assert.doesNotMatch(goalTreeUtils, /"calculator"/); diff --git a/website/src/features/toolboxes/modules/CalendarContent.jsx b/website/src/features/toolboxes/modules/CalendarContent.jsx new file mode 100644 index 0000000..9250cc8 --- /dev/null +++ b/website/src/features/toolboxes/modules/CalendarContent.jsx @@ -0,0 +1,144 @@ +// Rôle : rend le contenu interactif du calendrier une fois initialisé. +import { Icon } from "../../../components/Icon.jsx"; +import { CalendarEventForm } from "./CalendarEventForm.jsx"; +import { CalendarMonthView, CalendarWeekGrid } from "./CalendarViews.jsx"; +import { TextExchangeActions } from "./TextExchangeActions.jsx"; +import { + addMonths, + formatLocalDateKey, + getMonthStart, + minutesToTimeValue +} from "./calendarUtils.js"; + +export function CalendarContent({ + data, + context, + draft, + error, + activeOccurrenceDate, + monthStart, + monthGridDays, + occurrences, + textContent, + activeEventId, + formOpen, + draftSelection, + selection, + monthSelection, + days, + weekGridLayout, + now, + nowDayIndex, + nowMinutes, + hideEmptyHours, + textExchangeOpen, + onKeyDown, + onSubmit, + onDraftChange, + onAlertModeChange, + onAlertOffsetToggle, + onDeleteEvent, + onDeleteOccurrence, + onClose, + onDisplayedMonthChange, + onImport, + onHideEmptyHoursToggle, + onMonthPointerDown, + onMonthPointerEnter, + onMonthPointerUp, + onSlotPointerDown, + onSlotPointerEnter, + onSlotPointerUp, + onEditEvent +}) { + const selectedAlertOffsets = draft.alertMode === "off" ? [] : draft.alertOffsetMinutes; + + return ( +
+ {formOpen && ( + + )} + {textExchangeOpen && ( +
+ +
+ )} + + {data.viewMode === "week" && ( + + )} + + {data.viewMode === "month" ? ( + onDisplayedMonthChange(getMonthStart(addMonths(monthStart, -1)))} + onCurrentMonth={() => onDisplayedMonthChange(getMonthStart(new Date()))} + onNextMonth={() => onDisplayedMonthChange(getMonthStart(addMonths(monthStart, 1)))} + onPointerDown={onMonthPointerDown} + onPointerEnter={onMonthPointerEnter} + onPointerUp={onMonthPointerUp} + onEditEvent={onEditEvent} + /> + ) : ( + + )} +
+ ); +} + +function CalendarWeekOptions({ textContent, hideEmptyHours, onToggle }) { + return ( +
+ {textContent.hideEmptyHoursLabel || "Masquer les heures vides"} +