139 lines
6.2 KiB
JavaScript
139 lines
6.2 KiB
JavaScript
// Rôle : rend les combos enregistrés, leurs catégories et les contrôles de réorganisation.
|
|
import { Icon } from "../../../components/Icon.jsx";
|
|
import { ComboSequence, InlineTextInput } from "./CombosParts.jsx";
|
|
import { getComboCategory } from "./combosUtils.js";
|
|
|
|
export function CombosList({
|
|
entries,
|
|
data,
|
|
textContent,
|
|
reorder,
|
|
onComboUpdate,
|
|
onComboEdit,
|
|
onComboDelete,
|
|
onCategoryToggle
|
|
}) {
|
|
const categoryCount = entries.filter((entry) => entry.type === "group").length;
|
|
|
|
function renderCombo(combo) {
|
|
const category = getComboCategory(combo);
|
|
const comboClassName = [
|
|
"combo-card",
|
|
"is-editing",
|
|
reorder.isItemDragging(combo.id) ? "is-dragging" : "",
|
|
reorder.isItemDropTarget(combo.id) ? "is-drop-target" : "",
|
|
reorder.getDropPlacement("item", combo.id) === "after" ? "drop-after" : ""
|
|
].filter(Boolean).join(" ");
|
|
return (
|
|
<article className={comboClassName} key={combo.id} {...reorder.getItemProps({ itemId: combo.id, groupId: category })}>
|
|
<button
|
|
className="task-planner-drag-handle combo-drag-handle"
|
|
type="button"
|
|
onPointerDown={(event) => reorder.itemReorder.startDrag(event, combo.id)}
|
|
aria-label={`${textContent.reorderComboTitle || "Déplacer le combo"} ${combo.name}`}
|
|
title={textContent.reorderComboTitle || "Déplacer le combo"}
|
|
>
|
|
<Icon name="drag" />
|
|
</button>
|
|
<div className="combo-card-main">
|
|
<InlineTextInput
|
|
className="tool-split-entry-label combo-title-input"
|
|
value={combo.name}
|
|
onCommit={(name) => onComboUpdate(combo.id, (current) => ({ ...current, name: name || current.name }))}
|
|
ariaLabel={textContent.comboNameLabel || "Nom"}
|
|
/>
|
|
<ComboSequence inputs={combo.inputs} device={combo.device || data.device} emptyLabel={textContent.emptySequence || "Combo sans touches"} />
|
|
</div>
|
|
<div className="combo-card-actions">
|
|
<button type="button" className="checklist-delete-button" onClick={() => onComboEdit(combo)} aria-label={`${textContent.editComboTitle || "Modifier le combo"} ${combo.name}`} title={textContent.editComboTitle || "Modifier le combo"}>
|
|
<Icon name="edit" />
|
|
</button>
|
|
<button type="button" className="checklist-delete-button danger" onClick={() => onComboDelete(combo.id)} aria-label={`${textContent.deleteComboTitle || "Supprimer le combo"} ${combo.name}`} title={textContent.deleteComboTitle || "Supprimer le combo"}>
|
|
<Icon name="trash" />
|
|
</button>
|
|
</div>
|
|
</article>
|
|
);
|
|
}
|
|
|
|
function renderCategory(group, index, groupCount) {
|
|
const category = group.category;
|
|
const isCollapsed = data.collapsedCategories.includes(category);
|
|
const isLastCategory = index === groupCount - 1;
|
|
const categoryClassName = [
|
|
"combos-category checklist-section is-grouped",
|
|
isCollapsed ? "is-collapsed" : "",
|
|
reorder.isGroupDragging(category) ? "is-dragging" : "",
|
|
reorder.isGroupDropTarget(category) ? "is-drop-target" : "",
|
|
reorder.getDropPlacement("group", category) === "after" ? "drop-after" : ""
|
|
].filter(Boolean).join(" ");
|
|
const showBoundaryDropZones = reorder.shouldShowGroupBoundaries();
|
|
const beforeDropZone = showBoundaryDropZones ? (
|
|
<div
|
|
className={`combos-category-boundary-drop-zone ${reorder.isBoundaryDropTarget({ groupId: category, placement: "before" }) ? "is-drop-target" : ""}`}
|
|
key={`${category}:before-drop`}
|
|
{...reorder.getGroupBoundaryProps({ groupId: category, placement: "before" })}
|
|
/>
|
|
) : null;
|
|
const afterDropZone = showBoundaryDropZones ? (
|
|
<div
|
|
className={`combos-category-boundary-drop-zone ${reorder.isBoundaryDropTarget({ groupId: category, placement: "after" }) ? "is-drop-target" : ""}`}
|
|
key={`${category}:after-drop`}
|
|
{...reorder.getGroupBoundaryProps({ groupId: category, placement: "after" })}
|
|
/>
|
|
) : null;
|
|
return [
|
|
beforeDropZone,
|
|
<section className={categoryClassName} key={category} {...reorder.getGroupProps({ groupId: category })}>
|
|
<div className="checklist-section-header combos-category-header">
|
|
<div className="combos-category-title">
|
|
<button
|
|
className="task-planner-category-drag-handle"
|
|
type="button"
|
|
onPointerDown={(event) => reorder.groupReorder.startDrag(event, { groupId: category })}
|
|
aria-label={`${textContent.reorderCategoryTitle || "Déplacer la catégorie"} ${category}`}
|
|
title={textContent.reorderCategoryTitle || "Déplacer la catégorie"}
|
|
>
|
|
<Icon name="drag" />
|
|
</button>
|
|
<h3>{category}</h3>
|
|
</div>
|
|
<div className="checklist-section-actions">
|
|
<span>{group.combos.length}</span>
|
|
<button
|
|
className="checklist-section-collapse-button"
|
|
type="button"
|
|
onClick={() => onCategoryToggle(category)}
|
|
aria-expanded={!isCollapsed}
|
|
aria-label={isCollapsed ? textContent.showCategoryTitle || "Afficher la catégorie" : textContent.hideCategoryTitle || "Réduire la catégorie"}
|
|
title={isCollapsed ? textContent.showCategoryTitle || "Afficher la catégorie" : textContent.hideCategoryTitle || "Réduire la catégorie"}
|
|
>
|
|
<Icon name={isCollapsed ? "chevron-down" : "chevron-up"} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{!isCollapsed && (
|
|
<div className="combos-list">
|
|
{group.combos.map((combo) => renderCombo(combo))}
|
|
</div>
|
|
)}
|
|
</section>,
|
|
showBoundaryDropZones && isLastCategory ? afterDropZone : null
|
|
];
|
|
}
|
|
|
|
function getCategoryIndex(entryIndex) {
|
|
return entries.slice(0, entryIndex + 1).filter((entry) => entry.type === "group").length - 1;
|
|
}
|
|
|
|
function renderEntry(entry, entryIndex) {
|
|
if (entry.type !== "group") return renderCombo(entry.item);
|
|
return renderCategory(entry.group, getCategoryIndex(entryIndex), categoryCount);
|
|
}
|
|
|
|
return (
|
|
<div className="combos-category-list">
|
|
{entries.map(renderEntry)}
|
|
</div>
|
|
);
|
|
}
|