// Rôle : fournit l'outil de mémos courts avec post-it, bande déroulante et tri manuel. import { CompactDropdown } from "../../../components/CompactDropdown.jsx"; import { Icon } from "../../../components/Icon.jsx"; import { moveItem, useGroupedReorder } from "../../../hooks/useGroupedReorder.js"; import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { TextExchangeActions } from "./TextExchangeActions.jsx"; import { TEXT_COLORS } from "./notepadUtils.js"; export function MemosModule({ toolboxId, moduleId, context, editing, textExchangeOpen }) { const storedData = context.getModuleData(toolboxId, moduleId, { items: [] }); const data = useMemo(() => context.normalizeMemosData(storedData), [context, storedData]); const textContent = context.moduleText?.memos || {}; const tickerItems = data.tickerEnabled ? data.items : []; const textareaRefs = useRef(new Map()); const [drafts, setDrafts] = useState({}); const { itemReorder, getItemProps, isItemDragging, isItemDropTarget, getDropPlacement } = useGroupedReorder({ namespace: `memos-${moduleId}`, items: data.items, getItemId: (memo) => memo.id, getParentId: () => moduleId, orientation: "horizontal", onItemMove: (operation) => saveItems(moveItem(data.items, operation.sourceId, operation.targetId, operation.placement)), hierarchy: { enabled: true, stickyParents: true } }); useEffect(() => { setDrafts(Object.fromEntries(data.items.map((item) => [item.id, item.text]))); }, [data.items]); useLayoutEffect(() => { data.items.forEach((item) => resizeMemoTextarea(textareaRefs.current.get(item.id))); }, [data.items, drafts]); function resizeMemoTextarea(element) { if (!element) return; element.style.height = "auto"; element.style.height = `${element.scrollHeight + 2}px`; } function save(nextData) { context.setModuleData(toolboxId, moduleId, nextData, "memos"); } function saveItems(items) { save({ ...data, items }); } function addMemo() { const now = new Date().toISOString(); save({ ...data, items: [ { id: context.uid("memo"), text: textContent.defaultText || "Nouveau mémo", color: "#f6c453", updatedAt: now }, ...data.items ] }); } function updateMemo(memoId, updater) { const now = new Date().toISOString(); save({ ...data, items: data.items.map((item) => item.id === memoId ? { ...updater(item), updatedAt: now } : item) }); } function deleteMemo(memoId) { save({ ...data, items: data.items.filter((item) => item.id !== memoId) }); } function commitMemoText(memoId) { const nextText = String(drafts[memoId] || "").trim(); if (!nextText) { deleteMemo(memoId); return; } updateMemo(memoId, (item) => ({ ...item, text: nextText })); } return (
{textExchangeOpen && (
save(nextData)} />
)} {editing && (
)}
{tickerItems.length > 0 && (
{[...tickerItems, ...tickerItems].map((memo, index) => ( {memo.text} ))}
)}
{data.items.length ? data.items.map((memo) => { const className = [ "memo-card", isItemDragging(memo.id) ? "is-dragging" : "", isItemDropTarget(memo.id) ? "is-drop-target" : "", getDropPlacement("item", memo.id) === "after" ? "drop-after" : "" ].filter(Boolean).join(" "); return (