126 lines
4.7 KiB
JavaScript
126 lines
4.7 KiB
JavaScript
// Rôle : affiche une toolbox dans la liste, avec actions rapides et icône personnalisable.
|
|
import React from "react";
|
|
import { CompactDropdown } from "../../components/CompactDropdown.jsx";
|
|
import { Icon } from "../../components/Icon.jsx";
|
|
import {
|
|
DEFAULT_TOOLBOX_ICON,
|
|
normalizeToolboxIcon,
|
|
TOOLBOX_ICONS
|
|
} from "./storage/toolboxStorage.js";
|
|
|
|
export function getGameCardCover(game) {
|
|
return game?.images?.cardCover || game?.image || "";
|
|
}
|
|
|
|
export function getGameCardBackground(game) {
|
|
return game?.cover || "var(--gradient-nebula)";
|
|
}
|
|
|
|
export function formatDate(value) {
|
|
return new Intl.DateTimeFormat("fr-FR", { dateStyle: "medium", timeStyle: "short" }).format(new Date(value));
|
|
}
|
|
|
|
export function ToolboxCard({ toolbox, game, actions, reorder }) {
|
|
const gameCoverImage = getGameCardCover(game);
|
|
const coverImage = gameCoverImage || toolbox.icon || DEFAULT_TOOLBOX_ICON;
|
|
const className = [
|
|
"card",
|
|
"toolbox-card",
|
|
reorder.isItemDragging(toolbox.id) ? "is-dragging" : "",
|
|
reorder.isItemDropTarget(toolbox.id) ? "is-drop-target" : "",
|
|
reorder.getDropPlacement("item", toolbox.id) === "after" ? "drop-after" : ""
|
|
].filter(Boolean).join(" ");
|
|
|
|
return (
|
|
<article className={className} {...reorder.getItemProps({ itemId: toolbox.id })}>
|
|
<button
|
|
className="toolbox-card-drag-handle"
|
|
type="button"
|
|
onPointerDown={(event) => reorder.itemReorder.startDrag(event, toolbox.id)}
|
|
aria-label={`Déplacer ${toolbox.name}`}
|
|
title="Déplacer"
|
|
>
|
|
<Icon name="drag" />
|
|
</button>
|
|
<a
|
|
className={`card-cover toolbox-card-cover-link ${gameCoverImage ? "toolbox-card-cover" : "toolbox-icon-cover"}`}
|
|
href={`#/toolbox/${toolbox.id}`}
|
|
aria-label={`Ouvrir ${toolbox.name}`}
|
|
title={`Ouvrir ${toolbox.name}`}
|
|
style={{ "--game-cover": getGameCardBackground(game), background: gameCoverImage ? "" : "var(--gradient-nebula)" }}
|
|
>
|
|
<img src={coverImage} alt={game ? game.title : `Icône de ${toolbox.name}`} loading="lazy" />
|
|
</a>
|
|
<div className="card-body">
|
|
<p className="eyebrow">{game ? game.title : "Toolbox libre"}</p>
|
|
<h2>{toolbox.name}</h2>
|
|
<small>Modifiée le {formatDate(toolbox.updatedAt)}</small>
|
|
<div className="card-actions">
|
|
<a className="button card-icon-button" href={`#/toolbox/${toolbox.id}`} aria-label={`Ouvrir ${toolbox.name}`} title="Ouvrir"><Icon name="open" /></a>
|
|
<button className="card-icon-button" onClick={() => actions.exportToolbox(toolbox.id)} aria-label={`Exporter ${toolbox.name}`} title="Exporter"><Icon name="export" /></button>
|
|
<button className="card-icon-button danger" onClick={() => actions.setConfirmModal({
|
|
title: "Supprimer la toolbox",
|
|
message: `Supprimer "${toolbox.name}" et ses données locales ?`,
|
|
confirmLabel: "Supprimer",
|
|
danger: true,
|
|
onResolve: (confirmed) => confirmed && actions.deleteToolbox(toolbox.id)
|
|
})} aria-label={`Supprimer ${toolbox.name}`} title="Supprimer"><Icon name="trash" /></button>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
);
|
|
}
|
|
|
|
export function ToolboxIconPicker({ toolbox, onChange }) {
|
|
const icon = normalizeToolboxIcon(toolbox.icon);
|
|
|
|
return (
|
|
<CompactDropdown
|
|
className="toolbox-icon-picker"
|
|
menuClassName="toolbox-icon-picker-menu"
|
|
label="Icônes de toolbox"
|
|
renderTrigger={({ open, toggle }) => (
|
|
<button
|
|
className="toolbox-icon-picker-button"
|
|
type="button"
|
|
onClick={toggle}
|
|
aria-label="Changer l'icône de la toolbox"
|
|
aria-expanded={open}
|
|
title="Changer l'icône"
|
|
>
|
|
<img src={icon} alt="" aria-hidden="true" />
|
|
</button>
|
|
)}
|
|
>
|
|
{({ close }) => (
|
|
<>
|
|
{TOOLBOX_ICONS.map((path) => (
|
|
<button
|
|
key={path}
|
|
className={path === icon ? "active" : ""}
|
|
type="button"
|
|
onClick={() => {
|
|
onChange(path);
|
|
close();
|
|
}}
|
|
aria-label={`Utiliser l'icône ${path.split("/").pop().replace(".png", "")}`}
|
|
aria-pressed={path === icon}
|
|
title={path.split("/").pop().replace(".png", "")}
|
|
>
|
|
<img src={path} alt="" aria-hidden="true" loading="lazy" />
|
|
</button>
|
|
))}
|
|
</>
|
|
)}
|
|
</CompactDropdown>
|
|
);
|
|
}
|
|
|
|
export function ToolboxGameIcon({ game }) {
|
|
const gameCoverImage = getGameCardCover(game);
|
|
return (
|
|
<div className="toolbox-game-icon" title={game.title} aria-label={`Jeu associé : ${game.title}`}>
|
|
<img src={gameCoverImage} alt="" aria-hidden="true" />
|
|
</div>
|
|
);
|
|
}
|