92 lines
4.1 KiB
JavaScript
92 lines
4.1 KiB
JavaScript
// Rôle : fournit l'outil compteurs personnalisables.
|
|
import { Icon } from "../../../components/Icon.jsx";
|
|
import { useDraftForm } from "../../../hooks/useDraftForm.js";
|
|
import { moveItem, useGroupedReorder } from "../../../hooks/useGroupedReorder.js";
|
|
|
|
export function CountersModule({ toolboxId, moduleId, context, editing }) {
|
|
const data = context.normalizeCountersData(context.getModuleData(toolboxId, moduleId, { counters: [] }));
|
|
const textContent = context.moduleText?.counters || {};
|
|
const counterDraft = useDraftForm({ label: "" });
|
|
const {
|
|
itemReorder,
|
|
getItemProps,
|
|
isItemDragging,
|
|
isItemDropTarget,
|
|
getDropPlacement
|
|
} = useGroupedReorder({
|
|
namespace: "counters",
|
|
items: data.counters,
|
|
getItemId: (counter) => counter.id,
|
|
getParentId: () => moduleId,
|
|
orientation: "horizontal",
|
|
onItemMove: (operation) => save(moveItem(data.counters, operation.sourceId, operation.targetId, operation.placement)),
|
|
hierarchy: { enabled: true, stickyParents: true }
|
|
});
|
|
|
|
function save(counters) {
|
|
context.setModuleData(toolboxId, moduleId, { counters });
|
|
}
|
|
|
|
function addCounter(event) {
|
|
counterDraft.handleSubmit(event, (draft, { reset }) => {
|
|
const cleanLabel = draft.label.trim();
|
|
if (!cleanLabel) return;
|
|
|
|
save([...data.counters, { id: context.uid("counter"), label: cleanLabel, value: 0 }]);
|
|
reset();
|
|
});
|
|
}
|
|
|
|
function updateCounter(counterId, updater) {
|
|
save(data.counters.map((counter) => counter.id === counterId ? updater(counter) : counter));
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{editing && (
|
|
<form className="inline-form counters-add-form module-add-panel" onSubmit={addCounter}>
|
|
<input {...counterDraft.getFieldProps("label", { placeholder: textContent.labelPlaceholder || "Nom du compteur" })} />
|
|
<button className="primary">{textContent.addButton || "Ajouter"}</button>
|
|
</form>
|
|
)}
|
|
<div className="counters-grid">
|
|
{data.counters.map((counter) => {
|
|
const className = [
|
|
"counter-item",
|
|
isItemDragging(counter.id) ? "is-dragging" : "",
|
|
isItemDropTarget(counter.id) ? "is-drop-target" : "",
|
|
getDropPlacement("item", counter.id) === "after" ? "drop-after" : ""
|
|
].filter(Boolean).join(" ");
|
|
|
|
return (
|
|
<article className={className} key={counter.id} {...getItemProps({ itemId: counter.id, parentId: moduleId })}>
|
|
<button
|
|
className="counter-drag-handle"
|
|
type="button"
|
|
onPointerDown={(event) => itemReorder.startDrag(event, counter.id)}
|
|
aria-label={`${textContent.reorderTitle || "Déplacer"} ${counter.label}`}
|
|
title={textContent.reorderTitle || "Déplacer"}
|
|
>
|
|
<Icon name="drag" />
|
|
</button>
|
|
<div className="counter-value">
|
|
<strong>{counter.value}</strong>
|
|
<span>{counter.label}</span>
|
|
</div>
|
|
<div className="counter-actions">
|
|
<button type="button" onClick={() => updateCounter(counter.id, (item) => ({ ...item, value: item.value - 1 }))} aria-label={`${textContent.decrementLabel || "Décrémenter"} ${counter.label}`}>-</button>
|
|
<button type="button" onClick={() => updateCounter(counter.id, (item) => ({ ...item, value: item.value + 1 }))} aria-label={`${textContent.incrementLabel || "Incrémenter"} ${counter.label}`}>+</button>
|
|
<button type="button" onClick={() => updateCounter(counter.id, (item) => ({ ...item, value: 0 }))} aria-label={`${textContent.resetTitle || "Réinitialiser"} ${counter.label}`} title={textContent.resetTitle || "Réinitialiser"}>
|
|
<Icon name="rubber" />
|
|
</button>
|
|
<button type="button" className="danger" onClick={() => save(data.counters.filter((item) => item.id !== counter.id))} aria-label={`${textContent.deleteTitle || "Supprimer"} ${counter.label}`} title={textContent.deleteTitle || "Supprimer"}>
|
|
<Icon name="trash" />
|
|
</button>
|
|
</div>
|
|
</article>
|
|
);
|
|
})}
|
|
</div>
|
|
</>
|
|
);
|
|
}
|