structure refacto & file documentation
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s
This commit is contained in:
parent
d7871736c8
commit
895fec2b40
84 changed files with 2629 additions and 2313 deletions
144
website/src/features/toolboxes/ToolboxCard.jsx
Normal file
144
website/src/features/toolboxes/ToolboxCard.jsx
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// Rôle : affiche une toolbox dans la liste, avec actions rapides et icône personnalisable.
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
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, draggingToolboxId, dropTarget, onDragStart }) {
|
||||
const gameCoverImage = getGameCardCover(game);
|
||||
const coverImage = gameCoverImage || toolbox.icon || DEFAULT_TOOLBOX_ICON;
|
||||
const isDragging = draggingToolboxId === toolbox.id;
|
||||
const isDropTarget = dropTarget.id === toolbox.id;
|
||||
const className = [
|
||||
"card",
|
||||
"toolbox-card",
|
||||
isDragging ? "is-dragging" : "",
|
||||
isDropTarget ? "is-drop-target" : "",
|
||||
isDropTarget && dropTarget.placement === "after" ? "drop-after" : ""
|
||||
].filter(Boolean).join(" ");
|
||||
|
||||
return (
|
||||
<article className={className} data-toolbox-id={toolbox.id}>
|
||||
<button
|
||||
className="toolbox-card-drag-handle"
|
||||
type="button"
|
||||
onPointerDown={(event) => onDragStart(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 [open, setOpen] = useState(false);
|
||||
const pickerRef = useRef(null);
|
||||
const icon = normalizeToolboxIcon(toolbox.icon);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return undefined;
|
||||
|
||||
function handlePointerDown(event) {
|
||||
if (!pickerRef.current?.contains(event.target)) setOpen(false);
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
if (event.key === "Escape") setOpen(false);
|
||||
}
|
||||
|
||||
document.addEventListener("pointerdown", handlePointerDown);
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => {
|
||||
document.removeEventListener("pointerdown", handlePointerDown);
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
function selectIcon(nextIcon) {
|
||||
onChange(nextIcon);
|
||||
setOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="toolbox-icon-picker" ref={pickerRef}>
|
||||
<button
|
||||
className="toolbox-icon-picker-button"
|
||||
type="button"
|
||||
onClick={() => setOpen((value) => !value)}
|
||||
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>
|
||||
{open && (
|
||||
<div className="toolbox-icon-picker-menu" aria-label="Icônes de toolbox">
|
||||
{TOOLBOX_ICONS.map((path) => (
|
||||
<button
|
||||
key={path}
|
||||
className={path === icon ? "active" : ""}
|
||||
type="button"
|
||||
onClick={() => selectIcon(path)}
|
||||
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>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue