This commit is contained in:
parent
db4b99aee3
commit
1dee8b528f
30 changed files with 3491 additions and 2444 deletions
132
website/src/features/toolboxes/modules/index.jsx
Normal file
132
website/src/features/toolboxes/modules/index.jsx
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import { ChecklistModule } from "./ChecklistModule.jsx";
|
||||
import { NotepadModule } from "./NotepadModule.jsx";
|
||||
import { ScreenshotsModule } from "./ScreenshotsModule.jsx";
|
||||
|
||||
const MODULE_COMPONENTS = {
|
||||
notepad: { label: "Bloc notes", icon: "notepad", Component: NotepadModule },
|
||||
checklist: { label: "Checklist", icon: "checklist", Component: ChecklistModule },
|
||||
screenshots: { label: "Screenshots", icon: "picture", Component: ScreenshotsModule }
|
||||
};
|
||||
|
||||
export const TOOLBOX_MODULES = Object.fromEntries(Object.entries(MODULE_COMPONENTS).map(([type, module]) => [
|
||||
type,
|
||||
{ label: module.label, icon: module.icon }
|
||||
]));
|
||||
|
||||
export function ToolboxModules({ toolbox, moduleColumns, context, onRename, onDelete, onMove }) {
|
||||
if (moduleColumns === 1) {
|
||||
return (
|
||||
<section className="modules" data-toolbox-id={toolbox.id} data-layout-cols={moduleColumns}>
|
||||
{toolbox.modules.map((module) => <ModuleShell key={module.id} toolbox={toolbox} module={module} context={context} onRename={onRename} onDelete={onDelete} onMove={onMove} />)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const columns = [[], []];
|
||||
toolbox.modules.forEach((module, index) => columns[index % 2].push(module));
|
||||
|
||||
return (
|
||||
<section className="modules" data-toolbox-id={toolbox.id} data-layout-cols={moduleColumns}>
|
||||
{columns.map((modules, index) => (
|
||||
<div className="module-column" key={index}>
|
||||
{modules.map((module) => <ModuleShell key={module.id} toolbox={toolbox} module={module} context={context} onRename={onRename} onDelete={onDelete} onMove={onMove} />)}
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleShell({ toolbox, module, context, onRename, onDelete, onMove }) {
|
||||
const definition = MODULE_COMPONENTS[module.type] || MODULE_COMPONENTS.notepad;
|
||||
const Component = definition.Component;
|
||||
const label = definition.label || module.type;
|
||||
|
||||
function handleDragStart(event) {
|
||||
if (event.target.closest("button, input, select, textarea, [contenteditable='true']")) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
event.currentTarget.classList.add("is-dragging");
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
event.dataTransfer.setData("text/plain", module.id);
|
||||
}
|
||||
|
||||
function handleDragOver(event) {
|
||||
const fromModuleId = event.dataTransfer.getData("text/plain");
|
||||
if (!fromModuleId || fromModuleId === module.id) return;
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
event.currentTarget.classList.add("is-drop-target");
|
||||
const rect = event.currentTarget.getBoundingClientRect();
|
||||
event.currentTarget.classList.toggle("drop-after", event.clientY > rect.top + rect.height / 2);
|
||||
}
|
||||
|
||||
function handleDrop(event) {
|
||||
const fromModuleId = event.dataTransfer.getData("text/plain");
|
||||
if (!fromModuleId || fromModuleId === module.id) return;
|
||||
event.preventDefault();
|
||||
const rect = event.currentTarget.getBoundingClientRect();
|
||||
onMove(fromModuleId, module.id, event.clientY > rect.top + rect.height / 2 ? "after" : "before");
|
||||
}
|
||||
|
||||
function clearDragClasses(event) {
|
||||
event.currentTarget.classList.remove("is-dragging", "is-drop-target", "drop-after");
|
||||
}
|
||||
|
||||
return (
|
||||
<article
|
||||
className="module"
|
||||
data-toolbox-id={toolbox.id}
|
||||
data-module-id={module.id}
|
||||
draggable
|
||||
onDragStart={handleDragStart}
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={clearDragClasses}
|
||||
onDrop={handleDrop}
|
||||
onDragEnd={clearDragClasses}
|
||||
>
|
||||
<header>
|
||||
<div>
|
||||
<span className="module-drag-handle" aria-hidden="true" title="Déplacer l'outil" />
|
||||
<span className="module-icon" aria-hidden="true">
|
||||
<span className={`module-icon-svg module-icon-${definition.icon || "notepad"}`} />
|
||||
</span>
|
||||
<EditableModuleTitle value={module.title || label} fallback={label} onSave={(title) => onRename(module.id, title)} />
|
||||
</div>
|
||||
<div>
|
||||
<button className="module-delete-button danger" onClick={() => onDelete(module.id)} aria-label={`Retirer ${module.title || label}`} title="Retirer">
|
||||
<span className="ui-icon ui-icon-trash" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<Component toolboxId={toolbox.id} moduleId={module.id} context={context} />
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
function EditableModuleTitle({ value, fallback, onSave }) {
|
||||
return (
|
||||
<h2
|
||||
className="module-title"
|
||||
contentEditable
|
||||
suppressContentEditableWarning
|
||||
spellCheck="false"
|
||||
title="Cliquer pour renommer"
|
||||
onFocus={(event) => { event.currentTarget.dataset.previousTitle = event.currentTarget.textContent.trim(); }}
|
||||
onBlur={(event) => onSave(event.currentTarget.textContent.trim() || fallback)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
event.currentTarget.blur();
|
||||
}
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
event.currentTarget.textContent = event.currentTarget.dataset.previousTitle || value;
|
||||
event.currentTarget.blur();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue