Migrate site controls to Nebula design system
All checks were successful
Deploy Sokko G / deploy (push) Successful in 17s

This commit is contained in:
Shinuwa 2026-08-29 09:16:58 +02:00
parent e3d0925eb1
commit 817e21567b
127 changed files with 5380 additions and 2363 deletions

View file

@ -57,7 +57,8 @@ export function CopyChecklistItemsButton({ labels, sections, title = "Copier pou
return (
<button
className="filter-reset-button results-copy-button"
className="filter-reset-button results-copy-button nds-icon-button"
data-size="xs"
type="button"
onClick={copyItems}
disabled={!text}

View file

@ -17,8 +17,6 @@ export function GameFiltersPanel({
onChange
}) {
const selected = new Set(filters[selectedKey] || []);
const logicLabel = filters.logic === "or" ? logicLabels.or : logicLabels.and;
function update(updater) {
onChange(updater(filters));
}
@ -31,7 +29,8 @@ export function GameFiltersPanel({
<h2>{title}</h2>
</div>
<button
className="filter-reset-button"
className="filter-reset-button nds-icon-button"
data-size="xs"
onClick={() => onChange({ name: "", [selectedKey]: [], logic: "and" })}
aria-label={resetLabel}
title={resetLabel}
@ -41,11 +40,24 @@ export function GameFiltersPanel({
</div>
<label className="field compact">
<span>{nameLabel}</span>
<input value={filters.name} placeholder="Rechercher..." onChange={(event) => update((state) => ({ ...state, name: event.target.value }))} />
<input className="nds-input" data-size="sm" type="search" value={filters.name} placeholder="Rechercher..." onChange={(event) => update((state) => ({ ...state, name: event.target.value }))} />
</label>
<div className="filter-logic">
<span>{matchLabel}</span>
<button onClick={() => update((state) => ({ ...state, logic: state.logic === "and" ? "or" : "and" }))}>{logicLabel}</button>
<div className="filter-logic-group nds-radio-button-group" data-size="xs" role="radiogroup" aria-label={matchLabel}>
{["and", "or"].map((logic) => (
<button
key={logic}
className={filters.logic === logic ? "active" : ""}
type="button"
role="radio"
aria-checked={filters.logic === logic}
onClick={() => update((state) => ({ ...state, logic }))}
>
{logicLabels[logic]}
</button>
))}
</div>
</div>
<div className="filter-options legacy-scrollbar">
{options.map((option) => {
@ -53,6 +65,8 @@ export function GameFiltersPanel({
return (
<label className={`filter-chip ${getOptionClassName(option)} ${selected.has(optionId) ? "active" : ""}`} key={optionId}>
<input
className="nds-checkbox"
data-size="sm"
type="checkbox"
checked={selected.has(optionId)}
onChange={(event) => update((state) => {

View file

@ -30,7 +30,7 @@ export function GameListsPage({ game, lists, selectedListId, onSelectList, linke
<h1>Listes</h1>
</div>
<p>Sélectionnez une liste, puis copiez-la ou créez une checklist dans la toolbox associée.</p>
<a className="game-list-help-link button" href="#/about#contribuer">Proposer une nouvelle liste</a>
<a className="game-list-help-link nds-button" data-size="sm" href="#/about#contribuer">Proposer une nouvelle liste</a>
</div>
</section>
{lists.length ? (
@ -43,7 +43,9 @@ export function GameListsPage({ game, lists, selectedListId, onSelectList, linke
<small>{list.itemCount} éléments</small>
</button>
<button
className="game-list-create-button primary"
className="game-list-create-button nds-icon-button"
data-size="sm"
data-variant="primary"
type="button"
disabled={!linkedToolboxId}
onClick={() => onCreateChecklist?.(list)}

View file

@ -32,7 +32,7 @@ export function GamesPage({ siteContent, games, gamesError }) {
<h2>{game.title}</h2>
<p>{game.summary}</p>
<div className="card-actions">
<a className="button primary" href={`#/games/${game.id}`}>Ouvrir</a>
<a className="nds-button" data-size="sm" data-variant="primary" href={`#/games/${game.id}`}>Ouvrir</a>
</div>
</div>
</article>

View file

@ -30,8 +30,8 @@ export function MhwildsListing({ category, game, mhwilds, filters, setFilters, t
<p>{isMonsters ? "Filtrez les monstres par nom et faiblesses, puis consultez leurs dégâts détaillés." : "Filtrez la faune par nom et zones d'apparition."}</p>
</div>
<div className="actions">
<a className={`button ${isMonsters ? "primary" : ""}`} href="#/games/mhwilds/monsters">Monstres</a>
<a className={`button ${!isMonsters ? "primary" : ""}`} href="#/games/mhwilds/endemic">Faune</a>
<a className="nds-button" data-size="sm" data-variant={isMonsters ? "primary" : "secondary"} href="#/games/mhwilds/monsters">Monstres</a>
<a className="nds-button" data-size="sm" data-variant={!isMonsters ? "primary" : "secondary"} href="#/games/mhwilds/endemic">Faune</a>
</div>
</section>
<section className="game-layout" data-mhwilds-category={category}>

View file

@ -34,7 +34,9 @@ export function ToolboxCard({ toolbox, game, actions, reorder }) {
return (
<article className={className} {...reorder.getItemProps({ itemId: toolbox.id })}>
<button
className="toolbox-card-drag-handle"
className="toolbox-card-drag-handle nds-icon-button"
data-size="md"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.itemReorder.startDrag(event, toolbox.id)}
aria-label={`Déplacer ${toolbox.name}`}
@ -56,9 +58,9 @@ export function ToolboxCard({ toolbox, game, actions, reorder }) {
<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({
<a className="card-icon-button nds-icon-button" data-size="md" href={`#/toolbox/${toolbox.id}`} aria-label={`Ouvrir ${toolbox.name}`} title="Ouvrir"><Icon name="open" /></a>
<button className="card-icon-button nds-icon-button" data-size="md" onClick={() => actions.exportToolbox(toolbox.id)} aria-label={`Exporter ${toolbox.name}`} title="Exporter"><Icon name="export" /></button>
<button className="card-icon-button nds-icon-button" data-size="md" data-variant="danger" onClick={() => actions.setConfirmModal({
title: "Supprimer la toolbox",
message: `Supprimer "${toolbox.name}" et ses données locales ?`,
confirmLabel: "Supprimer",
@ -81,11 +83,12 @@ export function ToolboxIconPicker({ toolbox, onChange, tutorialTarget = "" }) {
label="Icônes de toolbox"
renderTrigger={({ open, toggle }) => (
<button
className="toolbox-icon-picker-button"
className={`toolbox-icon-picker-button nds-large-icon-picker-trigger ${open ? "active" : ""}`}
type="button"
onClick={toggle}
aria-label="Changer l'icône de la toolbox"
aria-expanded={open}
aria-pressed={open}
data-toolbox-tutorial-target={tutorialTarget || undefined}
title="Changer l'icône"
>
@ -98,7 +101,8 @@ export function ToolboxIconPicker({ toolbox, onChange, tutorialTarget = "" }) {
{TOOLBOX_ICONS.map((path) => (
<button
key={path}
className={path === icon ? "active" : ""}
className={`nds-icon-button ${path === icon ? "active" : ""}`}
data-size="md"
type="button"
onClick={() => {
onChange(path);

View file

@ -102,17 +102,17 @@ export function ToolboxesPage({ siteContent, toolboxes, getToolboxGame, actions,
<p className="eyebrow">{content.eyebrow}</p>
<h1>{content.title}</h1>
<p>{content.summary}</p>
<a className="button hero-library-button library-link-button" href="#/library"><Icon name="book" />{content.libraryLink}</a>
<a className="hero-library-button library-link-button nds-button" data-size="sm" href="#/library"><Icon name="book" />{content.libraryLink}</a>
</div>
</section>
<section className="toolbar toolbox-actions-row">
<div>
<button className="primary" onClick={() => actions.setCreateModal({})}>{content.newButton}</button>
<ImportButton className="import-button" label={content.importOne} onFile={actions.importToolbox} />
<button className="nds-button" data-size="sm" data-variant="primary" onClick={() => actions.setCreateModal({})}>{content.newButton}</button>
<ImportButton className="nds-button" data-size="sm" label={content.importOne} onFile={actions.importToolbox} />
</div>
<div>
<button className="primary" onClick={actions.exportAllToolboxes}>{content.exportAll}</button>
<ImportButton className="import-button" label={content.importAll} onFile={actions.importAllToolboxes} />
<button className="nds-button" data-size="sm" data-variant="primary" onClick={actions.exportAllToolboxes}>{content.exportAll}</button>
<ImportButton className="nds-button" data-size="sm" label={content.importAll} onFile={actions.importAllToolboxes} />
</div>
</section>
<section className="toolbox-preferences-row" aria-label={content.preferencesLabel}>
@ -169,7 +169,7 @@ function StorageHelpCard({ help }) {
export function ToolboxPage(props) {
const { toolboxId, toolboxes, getToolboxGame } = props;
const toolbox = toolboxes.find((item) => item.id === toolboxId);
if (!toolbox) return <div className="empty"><h1>Toolbox introuvable</h1><a className="button" href="#/toolboxes">Retour</a></div>;
if (!toolbox) return <div className="empty"><h1>Toolbox introuvable</h1><a className="nds-button" data-size="sm" href="#/toolboxes">Retour</a></div>;
return <ToolboxView {...props} toolbox={toolbox} toolboxGame={getToolboxGame(toolbox)} embedded={false} />;
}
@ -265,7 +265,8 @@ function ToolboxView({ siteContent, toolbox, toolboxGame, embedded, actions, sto
</div>
{toolboxGame && (
<a
className="button toolbox-hero-link"
className="toolbox-hero-link nds-button"
data-size="sm"
href={embedded ? `#/toolbox/${toolbox.id}` : `#/games/${toolboxGame.id}`}
onClick={embedded ? () => actions.setDrawerGameId("") : undefined}
>
@ -299,7 +300,7 @@ function ToolboxView({ siteContent, toolbox, toolboxGame, embedded, actions, sto
)}
{!embedded && (
<section className="modules-toolbar" aria-label="Options d'affichage des outils">
<div className="layout-switch" role="group" aria-label="Mode d'affichage des outils">
<div className="layout-switch nds-radio-button-group" data-size="sm" role="group" aria-label="Mode d'affichage des outils">
<button className={moduleColumns === 1 ? "active" : ""} onClick={() => updateToolbox({ ...toolbox, moduleColumns: 1 })} aria-pressed={moduleColumns === 1} aria-label="Afficher en lignes" title="Afficher en lignes"><Icon name="rows" /></button>
<button className={moduleColumns === 2 ? "active" : ""} onClick={() => updateToolbox({ ...toolbox, moduleColumns: 2 })} aria-pressed={moduleColumns === 2} aria-label="Afficher en colonnes" title="Afficher en colonnes"><Icon name="columns" /></button>
</div>
@ -387,15 +388,15 @@ export function ToolboxDrawer({ gameId, game, siteContent, toolboxes, links, act
<div>
<p className="eyebrow">Toolbox liée</p>
<div className="drawer-toolbox-actions" aria-label="Actions toolbox liée">
<button className="drawer-action-button" onClick={() => actions.setLinkModalGameId(gameId)} aria-label="Lier une toolbox" title="Lier"><Icon name="link" /></button>
<ImportButton className="drawer-action-button import-icon-button" ariaLabel="Importer une toolbox" title="Importer" icon="import" onFile={(file) => actions.importToolbox(file, gameId)} />
<button className="drawer-action-button" onClick={() => actions.exportToolbox(selectedId)} disabled={!selectedId} aria-label="Exporter la toolbox liée" title="Exporter"><Icon name="export" /></button>
<button className="drawer-action-button nds-icon-button" data-size="sm" onClick={() => actions.setLinkModalGameId(gameId)} aria-label="Lier une toolbox" title="Lier"><Icon name="link" /></button>
<ImportButton className="drawer-action-button nds-icon-button import-icon-button" data-size="sm" ariaLabel="Importer une toolbox" title="Importer" icon="import" onFile={(file) => actions.importToolbox(file, gameId)} />
<button className="drawer-action-button nds-icon-button" data-size="sm" onClick={() => actions.exportToolbox(selectedId)} disabled={!selectedId} aria-label="Exporter la toolbox liée" title="Exporter"><Icon name="export" /></button>
</div>
</div>
<button className="drawer-close-button" onClick={() => actions.setDrawerGameId("")} aria-label="Fermer" title="Fermer"><Icon name="close" /></button>
<button className="drawer-close-button nds-icon-button" data-size="sm" onClick={() => actions.setDrawerGameId("")} aria-label="Fermer" title="Fermer"><Icon name="close" /></button>
</header>
{toolbox ? <ToolboxView siteContent={siteContent} toolbox={toolbox} toolboxGame={game} embedded actions={actions} storageUsage={storageUsage} storageReady={storageReady} getModuleData={getModuleData} updateToolbox={updateToolbox} updateModuleData={updateModuleData} addImageFiles={addImageFiles} /> : (
<div className="empty"><p>Aucune toolbox associée à cette page jeu.</p><button className="primary" onClick={() => actions.setCreateModal({ gameId })}>Créer et associer</button></div>
<div className="empty"><p>Aucune toolbox associée à cette page jeu.</p><button className="nds-button" data-size="sm" data-variant="primary" onClick={() => actions.setCreateModal({ gameId })}>Créer et associer</button></div>
)}
<StorageQuota usage={storageUsage} />
</div>

View file

@ -27,7 +27,11 @@ export function AddToolControls({ onAdd }) {
aria-label="Ajouter un outil"
>
<button
className="primary tool-add-modal-button"
className="tool-add-modal-button nds-button"
data-size="sm"
data-variant="primary"
data-edge="left"
data-border="joined"
type="button"
onClick={() => setOpen(true)}
data-toolbox-tutorial-target="addTool"
@ -38,11 +42,13 @@ export function AddToolControls({ onAdd }) {
className="tool-add-quick-dropdown"
menuClassName="tool-quick-add"
label="Ajout rapide"
openOnHover
closeOnMouseLeave
renderTrigger={({ open: quickOpen, toggle }) => (
<button
className={`primary tool-add-quick-toggle ${quickOpen ? "active" : ""}`}
className={`tool-add-quick-toggle nds-icon-button ${quickOpen ? "active" : ""}`}
data-size="sm"
data-variant="primary"
data-edge="right"
data-border="joined"
type="button"
onClick={toggle}
aria-label="Afficher l'ajout rapide"
@ -58,7 +64,8 @@ export function AddToolControls({ onAdd }) {
{modules.map(([type, module]) => (
<button
key={type}
className="tool-quick-add-button"
className="tool-quick-add-button nds-icon-button"
data-size="sm"
type="button"
onClick={() => {
onAdd(type);
@ -82,7 +89,7 @@ export function AddToolControls({ onAdd }) {
<section className="confirm-modal tool-add-modal">
<header>
<h2 id="tool-add-modal-title">Ajouter un outil</h2>
<button className="drawer-close-button" type="button" onClick={() => setOpen(false)} aria-label="Fermer" title="Fermer">
<button className="drawer-close-button nds-icon-button" data-size="sm" type="button" onClick={() => setOpen(false)} aria-label="Fermer" title="Fermer">
<Icon name="close" />
</button>
</header>

View file

@ -41,7 +41,9 @@ export function CalculatorEntries({ entries, parentId, getScopedParentId, active
<li className={className} key={entry.id} {...reorder.getItemProps({ itemId: entry.id, parentId: getScopedParentId(parentId) })}>
<div className="tool-split-entry calculator-entry has-drag-handle">
<button
className="calculator-drag-handle"
className="calculator-drag-handle nds-icon-button"
data-size="xs"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.itemReorder.startDrag(event, entry.id)}
aria-label={`${textContent.reorderTitle || "Déplacer"} ${entry.label || formatResult(entry.value)}`}
@ -58,6 +60,8 @@ export function CalculatorEntries({ entries, parentId, getScopedParentId, active
</span>
</button>
<button
className="nds-icon-button"
data-size="xs"
type="button"
onClick={() => onAddChild(entry)}
aria-label={`${textContent.addChildTitle || "Ajouter un calcul enfant"} ${entry.label || formatResult(entry.value)}`}
@ -65,7 +69,7 @@ export function CalculatorEntries({ entries, parentId, getScopedParentId, active
>
<Icon name="add" />
</button>
<button type="button" className="danger" onClick={() => onDelete(entry.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${entry.label}`} title={textContent.deleteTitle || "Supprimer"}>
<button type="button" className="nds-icon-button" data-size="xs" data-variant="danger" onClick={() => onDelete(entry.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${entry.label}`} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</div>

View file

@ -228,8 +228,10 @@ export function CalculatorModule({ toolboxId, moduleId, context, textExchangeOpe
/>
) : (
<input
type="text"
aria-label={textContent.expressionLabel || "Calcul"}
className="calculator-expression-input"
className="calculator-expression-input nds-input"
data-size="sm"
ref={expressionInputRef}
onChange={(event) => setExpression(event.target.value)}
onKeyDown={(event) => {
@ -251,9 +253,9 @@ export function CalculatorModule({ toolboxId, moduleId, context, textExchangeOpe
</div>
<label>
<span>{textContent.labelLabel || "Libellé"}</span>
<input value={label} onChange={(event) => setLabel(event.target.value)} placeholder={textContent.labelPlaceholder || "Lingots de fer"} />
<input className="nds-input" data-size="sm" type="text" value={label} onChange={(event) => setLabel(event.target.value)} placeholder={textContent.labelPlaceholder || "Lingots de fer"} />
</label>
<button className="primary" disabled={result == null}>{formMode === "edit" ? textContent.updateButton || "Mettre à jour" : textContent.saveButton || "Enregistrer"}</button>
<button className="nds-button" data-size="sm" data-variant="primary" disabled={result == null}>{formMode === "edit" ? textContent.updateButton || "Mettre à jour" : textContent.saveButton || "Enregistrer"}</button>
</div>
</form>
<div
@ -262,18 +264,19 @@ export function CalculatorModule({ toolboxId, moduleId, context, textExchangeOpe
>
<div className="tool-split-actions">
{(formulaParent || editingEntry) && (
<button className="tool-split-root-button" type="button" onClick={returnToRoot}>
<button className="tool-split-root-button nds-button" data-size="xs" type="button" onClick={returnToRoot}>
{editingEntry ? textContent.newButton || "Nouveau calcul" : textContent.rootButton || "Revenir à la racine"}
</button>
)}
<button className="tool-split-action-button danger" type="button" onClick={resetCalculator} aria-label={textContent.resetTitle || "Réinitialiser"} title={textContent.resetTitle || "Réinitialiser"}>
<button className="tool-split-action-button nds-icon-button" data-size="sm" data-variant="danger" type="button" onClick={resetCalculator} aria-label={textContent.resetTitle || "Réinitialiser"} title={textContent.resetTitle || "Réinitialiser"}>
<Icon name="rubber" />
</button>
<button className="tool-split-action-button" type="button" onClick={copyChecklistImport} disabled={!data.entries.length} aria-label={textContent.copyTitle || "Copier pour checklist"} title={copied ? textContent.copiedTitle || "Copié" : textContent.copyTitle || "Copier pour checklist"}>
<button className="tool-split-action-button nds-icon-button" data-size="sm" type="button" onClick={copyChecklistImport} disabled={!data.entries.length} aria-label={textContent.copyTitle || "Copier pour checklist"} title={copied ? textContent.copiedTitle || "Copié" : textContent.copyTitle || "Copier pour checklist"}>
<Icon name="copy" />
</button>
<button
className={`tool-split-scroll-toggle ${data.scrollResults ? "active" : ""}`}
className={`tool-split-scroll-toggle nds-switch ${data.scrollResults ? "active" : ""}`}
data-size="md"
type="button"
onClick={() => setScrollResults(!data.scrollResults)}
aria-pressed={data.scrollResults}

View file

@ -130,7 +130,8 @@ function CalendarWeekOptions({ textContent, hideEmptyHours, onToggle }) {
<div className="calendar-week-options">
<span>{textContent.hideEmptyHoursLabel || "Masquer les heures vides"}</span>
<button
className={`tool-split-scroll-toggle calendar-hide-hours-switch ${hideEmptyHours ? "active" : ""}`}
className={`tool-split-scroll-toggle calendar-hide-hours-switch nds-switch ${hideEmptyHours ? "active" : ""}`}
data-size="md"
type="button"
onClick={onToggle}
aria-pressed={hideEmptyHours}

View file

@ -2,19 +2,11 @@
import { Icon } from "../../../components/Icon.jsx";
import {
CALENDAR_ALERT_OFFSETS,
CALENDAR_COLORS
CALENDAR_COLORS,
CALENDAR_COLOR_VALUES
} from "./calendarUtils.js";
import { ALERT_MODES } from "./timerOptions.js";
const CALENDAR_COLOR_VALUES = {
violet: "#8b5cf6",
gold: "#f6c453",
cyan: "#22d3ee",
pink: "#d946ef",
green: "#22c55e",
gray: "#7d879f"
};
export function MonthlyRepeatSwitch({ value, textContent, onChange }) {
const options = [
{ value: "monthDay", icon: "day-number", label: textContent.monthlyRepeatMonthDay || "Numéro du jour" },
@ -22,7 +14,7 @@ export function MonthlyRepeatSwitch({ value, textContent, onChange }) {
];
return (
<div className="layout-switch calendar-month-repeat-switch" role="group" aria-label={textContent.monthlyRepeatLabel || "Règle mensuelle"}>
<div className="layout-switch calendar-month-repeat-switch nds-radio-button-group" data-size="sm" role="group" aria-label={textContent.monthlyRepeatLabel || "Règle mensuelle"}>
{options.map((option) => (
<button
key={option.value}
@ -49,7 +41,7 @@ export function MonthEventRepeatSwitch({ value, monthlyRepeatBy, textContent, on
const safeValue = monthlyRepeatBy === "monthDay" && value === "weekly" ? "none" : value;
return (
<div className="calendar-month-event-repeat-switch" role="group" aria-label={textContent.repeatLabel || "Répétition"}>
<div className="calendar-month-event-repeat-switch nds-radio-button-group" data-size="sm" role="group" aria-label={textContent.repeatLabel || "Répétition"}>
{options.map((option) => (
<button
key={option.value}
@ -80,7 +72,7 @@ export function CalendarAlertOffsets({ selectedOffsets, textContent, onToggle })
<legend>{textContent.alertOffsetLabel || "Alerte"}</legend>
{CALENDAR_ALERT_OFFSETS.map((offset) => (
<label key={offset}>
<input type="checkbox" checked={selectedOffsets.includes(offset)} onChange={() => onToggle(offset)} />
<input className="nds-checkbox" data-size="sm" type="checkbox" checked={selectedOffsets.includes(offset)} onChange={() => onToggle(offset)} />
<span>{labels[offset]}</span>
</label>
))}
@ -94,7 +86,7 @@ export function CalendarColorPicker({ value, textContent, onChange }) {
<div className="calendar-color-picker notepad-color-dropdown">
<span>{label}</span>
<button
className="calendar-color-toggle notepad-color-toggle"
className="calendar-color-toggle notepad-color-toggle nds-swatch"
type="button"
title={label}
aria-label={label}
@ -104,7 +96,7 @@ export function CalendarColorPicker({ value, textContent, onChange }) {
{CALENDAR_COLORS.map((color) => (
<button
key={color}
className={`notepad-swatch ${value === color ? "active" : ""}`}
className={`notepad-swatch nds-swatch ${value === color ? "active" : ""}`}
type="button"
title={textContent[`color${color.charAt(0).toUpperCase()}${color.slice(1)}`] || color}
aria-label={textContent[`color${color.charAt(0).toUpperCase()}${color.slice(1)}`] || color}
@ -126,7 +118,7 @@ export function CalendarAlertModeSwitch({ mode, textContent, onChange }) {
};
return (
<div className="timer-alert-mode-switch calendar-alert-mode-switch" role="group" aria-label={textContent.alertModeTitle || "Mode d'alerte"}>
<div className="timer-alert-mode-switch calendar-alert-mode-switch nds-radio-button-group" data-size="xs" role="group" aria-label={textContent.alertModeTitle || "Mode d'alerte"}>
{ALERT_MODES.map((option) => (
<button
key={option.mode}

View file

@ -29,14 +29,14 @@ export function CalendarEventForm({
<div className="calendar-form-row calendar-form-main-row">
<label className="calendar-title-field">
<span>{textContent.titleLabel || "Titre"}</span>
<input value={draft.title} onChange={(event) => onDraftChange({ title: event.target.value })} placeholder={textContent.titlePlaceholder || "Raid, reset, event..."} />
<input className="nds-input" data-size="sm" type="text" value={draft.title} onChange={(event) => onDraftChange({ title: event.target.value })} placeholder={textContent.titlePlaceholder || "Raid, reset, event..."} />
</label>
<CalendarColorPicker value={draft.color} textContent={textContent} onChange={(color) => onDraftChange({ color })} />
{draft.id && <div className="calendar-delete-actions">
{data.viewMode === "month" && draft.repeat !== "none" && activeOccurrenceDate && (
<button className="danger calendar-form-delete-button" type="button" onClick={() => onDeleteOccurrence(draft.id)} aria-label={textContent.deleteOccurrenceTitle || "Supprimer cette occurrence"} title={textContent.deleteOccurrenceTitle || "Supprimer cette occurrence"}><Icon name="trashcan-one" /></button>
<button className="calendar-form-delete-button nds-icon-button" data-size="sm" data-variant="danger" type="button" onClick={() => onDeleteOccurrence(draft.id)} aria-label={textContent.deleteOccurrenceTitle || "Supprimer cette occurrence"} title={textContent.deleteOccurrenceTitle || "Supprimer cette occurrence"}><Icon name="trashcan-one" /></button>
)}
<button className="danger calendar-form-delete-button" type="button" onClick={() => onDeleteEvent(draft.id)} aria-label={data.viewMode === "month" && draft.repeat !== "none" ? textContent.deleteSeriesTitle || "Supprimer toutes les répétitions" : textContent.deleteTitle || "Supprimer"} title={data.viewMode === "month" && draft.repeat !== "none" ? textContent.deleteSeriesTitle || "Supprimer toutes les répétitions" : textContent.deleteTitle || "Supprimer"}><Icon name="trash" /></button>
<button className="calendar-form-delete-button nds-icon-button" data-size="sm" data-variant="danger" type="button" onClick={() => onDeleteEvent(draft.id)} aria-label={data.viewMode === "month" && draft.repeat !== "none" ? textContent.deleteSeriesTitle || "Supprimer toutes les répétitions" : textContent.deleteTitle || "Supprimer"} title={data.viewMode === "month" && draft.repeat !== "none" ? textContent.deleteSeriesTitle || "Supprimer toutes les répétitions" : textContent.deleteTitle || "Supprimer"}><Icon name="trash" /></button>
</div>}
</div>
<div className={`calendar-form-row ${data.viewMode === "month" ? "calendar-form-month-row" : "calendar-form-time-row"}`}>
@ -67,6 +67,8 @@ export function CalendarEventForm({
/>
) : (
<select
className="nds-select"
data-size="sm"
value={draft.dayIndex}
onChange={(event) => {
const value = Number(event.target.value);
@ -97,11 +99,11 @@ export function CalendarEventForm({
<>
<label className="calendar-time-field">
<span>{textContent.startLabel || "Début"}</span>
<input type="time" step={WEEK_TIME_INPUT_STEP_MINUTES * 60} value={draft.startTime} onChange={(event) => onDraftChange({ startTime: event.target.value })} />
<input className="nds-time" data-size="xs" type="time" step={WEEK_TIME_INPUT_STEP_MINUTES * 60} value={draft.startTime} onChange={(event) => onDraftChange({ startTime: event.target.value })} />
</label>
<label className="calendar-time-field">
<span>{textContent.endLabel || "Fin"}</span>
<input type="time" step={WEEK_TIME_INPUT_STEP_MINUTES * 60} value={draft.endTime} onChange={(event) => onDraftChange({ endTime: event.target.value })} />
<input className="nds-time" data-size="xs" type="time" step={WEEK_TIME_INPUT_STEP_MINUTES * 60} value={draft.endTime} onChange={(event) => onDraftChange({ endTime: event.target.value })} />
</label>
</>
)}
@ -113,7 +115,8 @@ export function CalendarEventForm({
) : <div className="calendar-repeat-field">
<span>{textContent.repeatableTitle || "Répétable"}</span>
<button
className={`tool-split-scroll-toggle calendar-repeat-switch ${draft.repeatable !== false ? "active" : ""}`}
className={`tool-split-scroll-toggle calendar-repeat-switch nds-switch ${draft.repeatable !== false ? "active" : ""}`}
data-size="lg"
type="button"
onClick={() => onDraftChange({ repeatable: draft.repeatable === false })}
aria-pressed={draft.repeatable !== false}
@ -131,8 +134,8 @@ export function CalendarEventForm({
<CalendarAlertModeSwitch mode={draft.alertMode} textContent={textContent} onChange={onAlertModeChange} />
</div>
<div className="calendar-form-actions">
<button className="primary" type="submit">{draft.id ? textContent.saveButton || "Enregistrer" : textContent.addButton || "Ajouter"}</button>
<button type="button" onClick={onClose}>{textContent.cancelButton || "Annuler"}</button>
<button className="nds-button" data-size="sm" data-variant="primary" type="submit">{draft.id ? textContent.saveButton || "Enregistrer" : textContent.addButton || "Ajouter"}</button>
<button className="nds-button" data-size="sm" type="button" onClick={onClose}>{textContent.cancelButton || "Annuler"}</button>
</div>
</div>
{error && <p className="timer-form-error calendar-form-error" role="alert"><Icon name="warning" /><span>{error}</span></p>}

View file

@ -48,7 +48,7 @@ export function MonthDateDropdown({ value, displayedMonth, minDate = "", mode, t
const safeValue = normalizeDraftDateKey(value) || formatLocalDateKey(displayedMonth);
return (
<div className={`calendar-month-date-dropdown ${align === "end" ? "is-aligned-end" : ""}`}>
<button className="calendar-month-date-trigger" type="button" aria-label={label}>
<button className="calendar-month-date-trigger nds-date-trigger" data-size="sm" type="button" aria-label={label}>
<Icon name={mode === "weekday" ? "day-name" : "day-number"} />
<span>{formatMonthDateLabel(safeValue, mode)}</span>
<Icon name="chevron-down" />

View file

@ -4,6 +4,8 @@ import {
WEEK_SLOT_MINUTES,
addDays,
formatLocalDateKey,
getCalendarColorValue,
getCalendarEventSurfaceVariant,
minutesToTimeValue,
pad2
} from "./calendarUtils.js";
@ -137,19 +139,26 @@ function CalendarOccurrence({ occurrence, layout, active, onEdit }) {
const durationMinutes = Math.max(WEEK_SLOT_MINUTES, (occurrence.endMs - occurrence.startMs) / 60000 || WEEK_SLOT_MINUTES);
const startMinutes = occurrence.event.startMinutes;
const timeLabel = `${minutesToTimeValue(occurrence.event.startMinutes)}${occurrence.event.endMinutes ? ` - ${minutesToTimeValue(occurrence.event.endMinutes)}` : ""}`;
const compact = durationMinutes < 120;
const top = getWeekMinutePosition(layout, startMinutes);
const bottom = getWeekMinutePosition(layout, Math.min(24 * 60, startMinutes + durationMinutes));
const height = compact ? WEEK_COMPACT_EVENT_HEIGHT : Math.max(WEEK_COMPACT_EVENT_HEIGHT, bottom - top);
const naturalHeight = bottom - top;
const eventSurfaceSize = naturalHeight < WEEK_COMPACT_EVENT_HEIGHT ? "xs" : naturalHeight < 44 ? "sm" : "md";
const height = eventSurfaceSize === "xs" ? 10 : eventSurfaceSize === "sm" ? WEEK_COMPACT_EVENT_HEIGHT : Math.max(44, naturalHeight);
const color = occurrence.event.color || "violet";
return (
<article
className={`calendar-event calendar-event-${occurrence.event.color || "violet"} ${active ? "is-active" : ""} ${compact ? "is-compact" : ""}`}
className={`calendar-event nds-event-surface ${active ? "is-active" : ""} ${eventSurfaceSize !== "md" ? "is-compact" : ""}`}
data-color={getCalendarColorValue(color)}
data-size={eventSurfaceSize}
data-variant={getCalendarEventSurfaceVariant(color)}
style={{
top: `${top}px`,
width: "calc((100% / var(--calendar-event-lane-count, 1)) - 2px)",
height: `${height}px`,
"--calendar-event-lane": occurrence.lane || 0,
"--calendar-event-lane-count": occurrence.laneCount || 1
"--calendar-event-lane-count": occurrence.laneCount || 1,
"--nds-event-color": getCalendarColorValue(color)
}}
>
<Tooltip
@ -161,7 +170,7 @@ function CalendarOccurrence({ occurrence, layout, active, onEdit }) {
onClick={onEdit}
>
<strong>{occurrence.event.title}</strong>
{!compact && <span>{timeLabel}</span>}
<span>{timeLabel}</span>
</Tooltip>
</article>
);
@ -188,10 +197,10 @@ export function CalendarMonthView({
return (
<>
<div className="calendar-month-toolbar">
<button type="button" onClick={onPreviousMonth} title={textContent.previousMonthTitle || "Mois précédent"} aria-label={textContent.previousMonthTitle || "Mois précédent"}>{textContent.previousShortLabel || "Prec."}</button>
<button className="nds-button" data-size="xs" type="button" onClick={onPreviousMonth} title={textContent.previousMonthTitle || "Mois précédent"} aria-label={textContent.previousMonthTitle || "Mois précédent"}>{textContent.previousShortLabel || "Prec."}</button>
<strong>{formatMonthTitle(monthStart)}</strong>
<button type="button" onClick={onCurrentMonth}>{textContent.currentMonthTitle || "Mois courant"}</button>
<button type="button" onClick={onNextMonth} title={textContent.nextMonthTitle || "Mois suivant"} aria-label={textContent.nextMonthTitle || "Mois suivant"}>{textContent.nextShortLabel || "Suiv."}</button>
<button className="nds-button" data-size="xs" type="button" onClick={onCurrentMonth}>{textContent.currentMonthTitle || "Mois courant"}</button>
<button className="nds-button" data-size="xs" type="button" onClick={onNextMonth} title={textContent.nextMonthTitle || "Mois suivant"} aria-label={textContent.nextMonthTitle || "Mois suivant"}>{textContent.nextShortLabel || "Suiv."}</button>
</div>
<CalendarMonthGrid
days={monthGridDays}
@ -268,22 +277,29 @@ function CalendarMonthBar({ segment, active, onEdit }) {
const startColumn = segment.startIndex % WEEKDAY_LABELS.length + 1;
const endColumn = segment.endIndex % WEEKDAY_LABELS.length + 2;
const laneCount = Math.max(1, Math.min(3, segment.laneCount || 1));
const barHeight = laneCount === 1 ? 28 : laneCount === 2 ? 23 : 18;
const barGap = laneCount === 3 ? 3 : 4;
const barHeight = laneCount <= 2 ? 22 : 10;
const barGap = laneCount === 3 ? 2 : 4;
const eventSurfaceSize = barHeight <= 10 ? "xs" : barHeight <= 22 ? "sm" : "md";
const startLabel = new Date(segment.occurrence.startMs).toLocaleDateString(undefined, { day: "2-digit", month: "short", year: "numeric" });
const endDate = addDays(new Date(segment.occurrence.endMs), -1);
const endLabel = endDate.toLocaleDateString(undefined, { day: "2-digit", month: "short", year: "numeric" });
const dateLabel = startLabel === endLabel ? startLabel : `${startLabel} - ${endLabel}`;
const color = segment.occurrence.event.color || "violet";
return (
<article
className={`calendar-month-bar calendar-event-${segment.occurrence.event.color || "violet"} ${active ? "is-active" : ""}`}
className={`calendar-month-bar nds-event-surface ${active ? "is-active" : ""}`}
data-color={getCalendarColorValue(color)}
data-size={eventSurfaceSize}
data-variant={getCalendarEventSurfaceVariant(color)}
style={{
gridColumn: `${startColumn} / ${endColumn}`,
gridRow,
width: "auto",
"--calendar-event-lane": segment.lane || 0,
"--calendar-month-bar-height": `${barHeight}px`,
"--calendar-month-bar-gap": `${barGap}px`
"--calendar-month-bar-gap": `${barGap}px`,
"--nds-event-color": getCalendarColorValue(color)
}}
>
<Tooltip

View file

@ -113,10 +113,10 @@ export function ChecklistModule({ toolboxId, moduleId, context, editing, textExc
{editing && (
<div className="module-add-panel">
<form className="inline-form checklist-add-form" onSubmit={addItem}>
<input name="section" placeholder={textContent.sectionPlaceholder || "Catégorie"} value={sectionTitle} onChange={(event) => setSectionTitle(event.target.value)} />
<input name="label" placeholder={textContent.itemPlaceholder || "Nouvel item"} value={label} onChange={(event) => setLabel(event.target.value)} />
<input className="checklist-qty-input" name="qty" type="number" min="1" value={qty} onChange={(event) => setQty(event.target.value)} aria-label={textContent.quantityLabel || "Quantité cible"} />
<button className="primary">{textContent.addButton || "Ajouter"}</button>
<input className="nds-input" data-size="sm" name="section" type="text" placeholder={textContent.sectionPlaceholder || "Catégorie"} value={sectionTitle} onChange={(event) => setSectionTitle(event.target.value)} />
<input className="nds-input" data-size="sm" name="label" type="text" placeholder={textContent.itemPlaceholder || "Nouvel item"} value={label} onChange={(event) => setLabel(event.target.value)} />
<input className="checklist-qty-input nds-input" data-size="sm" name="qty" type="number" min="1" value={qty} onChange={(event) => setQty(event.target.value)} aria-label={textContent.quantityLabel || "Quantité cible"} />
<button className="nds-button" data-size="sm" data-variant="primary">{textContent.addButton || "Ajouter"}</button>
</form>
</div>
)}
@ -126,25 +126,27 @@ export function ChecklistModule({ toolboxId, moduleId, context, editing, textExc
</div>
)}
{namedSections.length > 0 && (
<div className="checklist-toolbar" role="radiogroup" aria-label={textContent.completedSectionsModeTitle || "Affichage des catégories terminées"}>
{[
{ mode: "visible", label: textContent.completedSectionsVisibleLabel || "Visible", icon: "eye-open" },
{ mode: "reduced", label: textContent.completedSectionsReducedLabel || "Réduit", icon: "hide" },
{ mode: "hidden", label: textContent.completedSectionsHiddenLabel || "Caché", icon: "eye-closed" }
].map((option) => (
<button
className={`checklist-complete-mode-button ${completedSectionsMode === option.mode ? "active" : ""}`}
type="button"
role="radio"
aria-checked={completedSectionsMode === option.mode}
aria-label={option.label}
key={option.mode}
onClick={() => setCompletedSectionsMode(option.mode)}
title={option.label}
>
<Icon name={option.icon} />
</button>
))}
<div className="checklist-toolbar">
<div className="checklist-complete-mode-group nds-radio-button-group" data-size="xs" role="radiogroup" aria-label={textContent.completedSectionsModeTitle || "Affichage des catégories terminées"}>
{[
{ mode: "visible", label: textContent.completedSectionsVisibleLabel || "Visible", icon: "eye-open" },
{ mode: "reduced", label: textContent.completedSectionsReducedLabel || "Réduit", icon: "hide" },
{ mode: "hidden", label: textContent.completedSectionsHiddenLabel || "Caché", icon: "eye-closed" }
].map((option) => (
<button
className={`checklist-complete-mode-button ${completedSectionsMode === option.mode ? "active" : ""}`}
type="button"
role="radio"
aria-checked={completedSectionsMode === option.mode}
aria-label={option.label}
key={option.mode}
onClick={() => setCompletedSectionsMode(option.mode)}
title={option.label}
>
<Icon name={option.icon} />
</button>
))}
</div>
</div>
)}
<ul className="checklist">

View file

@ -67,7 +67,9 @@ export function ChecklistSection({ section, context, data, reorder, onRenameSect
{showTitle && (
<div className="checklist-section-header">
<button
className="checklist-drag-handle"
className="checklist-drag-handle nds-icon-button"
data-size="xs"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.groupReorder.startDrag(event, { groupId: section.id })}
aria-label={`${textContent.reorderSectionTitle || "Déplacer la catégorie"} ${section.title}`}
@ -79,6 +81,7 @@ export function ChecklistSection({ section, context, data, reorder, onRenameSect
<input
{...titleEdit.getInputProps({
className: "checklist-section-title-input",
type: "text",
title: textContent.sectionEditTitle || "Renommer la catégorie",
"aria-label": textContent.sectionEditTitle || "Renommer la catégorie"
})}
@ -87,7 +90,8 @@ export function ChecklistSection({ section, context, data, reorder, onRenameSect
<div className="checklist-section-actions">
<span>{section.items.filter((item) => context.clampQty(item.qtyCurrent, item.qtyTarget) >= item.qtyTarget).length} / {section.items.length}</span>
<button
className="checklist-section-collapse-button"
className="checklist-section-collapse-button nds-icon-button"
data-size="xs"
type="button"
onClick={toggleSectionCollapsed}
aria-expanded={!isCollapsed}
@ -160,7 +164,9 @@ function ChecklistItem({ item, section, context, reorder, items, save, onUpdateI
<li className={className} {...reorder.getItemProps({ itemId: item.id, groupId: section.id })}>
<div className={`checklist-item-main ${editing ? "is-editing" : ""}`}>
<button
className="checklist-drag-handle"
className="checklist-drag-handle nds-icon-button"
data-size="xs"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.itemReorder.startDrag(event, item.id)}
aria-label={`${textContent.reorderTitle || "Déplacer"} ${item.label}`}
@ -170,17 +176,20 @@ function ChecklistItem({ item, section, context, reorder, items, save, onUpdateI
</button>
{item.qtyTarget === 1 ? (
<input
className="nds-checkbox"
data-size="sm"
type="checkbox"
checked={done}
onChange={(event) => updateItem((entry) => ({ ...entry, qtyTarget: 1, qtyCurrent: event.target.checked ? 1 : 0 }))}
aria-label={`Terminer ${item.label}`}
/>
) : (
<div className="checklist-qty-controls" aria-label={`Quantité ${item.label}`}>
<div className="checklist-qty-controls nds-quantity-stepper" data-size="md" aria-label={`Quantité ${item.label}`}>
<button type="button" onClick={() => updateItem((entry) => ({ ...entry, qtyCurrent: context.clampQty(entry.qtyCurrent - 1, entry.qtyTarget) }))} aria-label={textContent.decrementLabel || "Retirer une quantité"}>-</button>
<label className="checklist-qty-current">
<input
type="number"
className="nds-quantity-stepper-input"
max={item.qtyTarget}
value={context.clampQty(item.qtyCurrent, item.qtyTarget)}
style={{ "--qty-current-digits": String(context.clampQty(item.qtyCurrent, item.qtyTarget)).length }}
@ -196,13 +205,15 @@ function ChecklistItem({ item, section, context, reorder, items, save, onUpdateI
<>
<input
{...labelEdit.getInputProps({
className: "checklist-label-input",
className: "checklist-label-input nds-inline-input",
type: "text",
"aria-label": textContent.itemEditLabel || "Libellé"
})}
/>
<input
{...categoryEdit.getInputProps({
className: "checklist-category-input",
className: "checklist-category-input nds-inline-input",
type: "text",
placeholder: textContent.sectionPlaceholder || "Catégorie",
"aria-label": textContent.categoryEditLabel || "Catégorie"
})}
@ -214,10 +225,10 @@ function ChecklistItem({ item, section, context, reorder, items, save, onUpdateI
</button>
)}
</div>
<button type="button" className="checklist-edit-button" onClick={onToggleEditing} aria-label={`${textContent.editTitle || "Modifier"} ${item.label}`} title={textContent.editTitle || "Modifier"}>
<button type="button" className="checklist-edit-button nds-icon-button" data-size="sm" onClick={onToggleEditing} aria-label={`${textContent.editTitle || "Modifier"} ${item.label}`} title={textContent.editTitle || "Modifier"}>
<Icon name="edit" />
</button>
<button className="checklist-delete-button danger" onClick={() => onDeleteItem(item.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${item.label}`} title={textContent.deleteTitle || "Supprimer"}>
<button className="checklist-delete-button nds-icon-button" data-size="sm" data-variant="danger" onClick={() => onDeleteItem(item.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${item.label}`} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</li>

View file

@ -48,14 +48,14 @@ export function CombosEditor({
<div className="combos-device-row">
<label>
<span>{textContent.deviceLabel || "Périphérique"}</span>
<select value={data.device} onChange={(event) => onDeviceChange(event.target.value)}>
<select className="nds-select" data-size="sm" value={data.device} onChange={(event) => onDeviceChange(event.target.value)}>
{DEVICE_OPTIONS.map((device) => (
<option key={device.value} value={device.value}>{device.label}</option>
))}
</select>
</label>
{data.device === "keyboardMouse" && (
<div className="combos-keyboard-layout-switch" role="group" aria-label={textContent.keyboardLayoutLabel || "Disposition clavier"}>
<div className="combos-keyboard-layout-switch nds-radio-button-group" data-size="sm" role="group" aria-label={textContent.keyboardLayoutLabel || "Disposition clavier"}>
{[
{ value: "azerty", label: "AZERTY" },
{ value: "qwerty", label: "QWERTY" }
@ -78,11 +78,11 @@ export function CombosEditor({
<div className="combos-form-grid">
<label>
<span>{textContent.targetCategoryLabel || "Catégorie"}</span>
<input value={comboCategory} onChange={(event) => onComboCategoryChange(event.target.value)} placeholder={textContent.noCategoryLabel || "Sans catégorie"} />
<input className="nds-input" data-size="sm" type="text" value={comboCategory} onChange={(event) => onComboCategoryChange(event.target.value)} placeholder={textContent.noCategoryLabel || "Sans catégorie"} />
</label>
<label>
<span>{textContent.comboNameLabel || "Nom"}</span>
<input value={comboName} onChange={(event) => onComboNameChange(event.target.value)} placeholder={textContent.comboNamePlaceholder || "Nom du combo"} />
<input className="nds-input" data-size="sm" type="text" value={comboName} onChange={(event) => onComboNameChange(event.target.value)} placeholder={textContent.comboNamePlaceholder || "Nom du combo"} />
</label>
</div>
<div className="combos-palette" aria-label={textContent.paletteLabel || "Palette de touches"}>
@ -115,8 +115,8 @@ export function CombosEditor({
{data.device === "keyboardMouse" && (
<div className="inline-form combos-custom-key-form">
<input value={customKey} onChange={(event) => onCustomKeyChange(event.target.value)} placeholder={textContent.customKeyPlaceholder || "Touche personnalisée"} />
<button type="button" onClick={onCustomKeyAdd}>{textContent.addCustomKeyButton || "Ajouter touche"}</button>
<input className="nds-input" data-size="sm" type="text" value={customKey} onChange={(event) => onCustomKeyChange(event.target.value)} placeholder={textContent.customKeyPlaceholder || "Touche personnalisée"} />
<button className="nds-button" data-size="sm" type="button" onClick={onCustomKeyAdd}>{textContent.addCustomKeyButton || "Ajouter touche"}</button>
</div>
)}
@ -135,7 +135,7 @@ export function CombosEditor({
/>
<div className="combo-draft-actions">
<label className="combo-hold-field">
<span className="combo-hold-controls">
<span className="combo-hold-controls nds-compound-toggle-field">
<button
type="button"
className={selectedDraftInput?.hold || selectedDraftInput?.holdMs ? "active" : ""}
@ -146,6 +146,7 @@ export function CombosEditor({
{textContent.holdToggleLabel || "Hold"}
</button>
<input
className="nds-compound-toggle-field-input"
type="number"
min="0"
max="99"
@ -161,7 +162,8 @@ export function CombosEditor({
</label>
<button
type="button"
className={`combos-simultaneous-toggle ${simultaneousMode ? "active" : ""}`}
className={`combos-simultaneous-toggle nds-radio-button-group ${simultaneousMode ? "active" : ""}`}
data-size="xs"
onClick={onSimultaneousModeToggle}
aria-pressed={simultaneousMode}
title={textContent.simultaneousModeTitle || "Ajout simultané"}
@ -170,19 +172,19 @@ export function CombosEditor({
<span className={!simultaneousMode ? "active" : ""}>{textContent.successiveModeLabel || "A › B"}</span>
<span className={simultaneousMode ? "active" : ""}>{textContent.simultaneousModeLabel || "A + B"}</span>
</button>
<button type="button" className="notepad-toolbar-button" onClick={onLastInputRemove} disabled={!cleanDraftInputs.length} title={textContent.removeLastInputTitle || "Retirer la dernière touche"} aria-label={textContent.removeLastInputTitle || "Retirer la dernière touche"}>
<button type="button" className="notepad-toolbar-button nds-icon-button" data-size="sm" onClick={onLastInputRemove} disabled={!cleanDraftInputs.length} title={textContent.removeLastInputTitle || "Retirer la dernière touche"} aria-label={textContent.removeLastInputTitle || "Retirer la dernière touche"}>
<Icon name="back" />
</button>
<button type="button" className="notepad-toolbar-button danger" onClick={onActiveStepRemove} disabled={draftInputs.length <= 1} title={textContent.removeStepTitle || "Supprimer l'étape"} aria-label={textContent.removeStepTitle || "Supprimer l'étape"}>
<button type="button" className="notepad-toolbar-button nds-icon-button" data-size="sm" data-variant="danger" onClick={onActiveStepRemove} disabled={draftInputs.length <= 1} title={textContent.removeStepTitle || "Supprimer l'étape"} aria-label={textContent.removeStepTitle || "Supprimer l'étape"}>
<Icon name="trash" />
</button>
<div className="combo-save-actions">
{editingComboId && (
<button type="button" className="combo-cancel-button" onClick={onComboEditCancel}>
<button type="button" className="combo-cancel-button nds-button" data-size="sm" onClick={onComboEditCancel}>
{textContent.cancelEditButton || "Annuler"}
</button>
)}
<button className="primary" disabled={!comboName.trim() && !cleanDraftInputs.length}>
<button className="nds-button" data-size="sm" data-variant="primary" disabled={!comboName.trim() && !cleanDraftInputs.length}>
{editingComboId ? textContent.saveComboButton || "Enregistrer" : textContent.addComboButton || "Ajouter le combo"}
</button>
</div>

View file

@ -27,7 +27,9 @@ export function CombosList({
return (
<article className={comboClassName} key={combo.id} {...reorder.getItemProps({ itemId: combo.id, groupId: category })}>
<button
className="task-planner-drag-handle combo-drag-handle"
className="task-planner-drag-handle combo-drag-handle nds-icon-button"
data-size="xs"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.itemReorder.startDrag(event, combo.id)}
aria-label={`${textContent.reorderComboTitle || "Déplacer le combo"} ${combo.name}`}
@ -45,10 +47,10 @@ export function CombosList({
<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"}>
<button type="button" className="checklist-delete-button nds-icon-button" data-size="sm" 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"}>
<button type="button" className="checklist-delete-button nds-icon-button" data-size="sm" data-variant="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>
@ -88,7 +90,9 @@ export function CombosList({
<div className="checklist-section-header combos-category-header">
<div className="combos-category-title">
<button
className="task-planner-category-drag-handle"
className="task-planner-category-drag-handle nds-icon-button"
data-size="xs"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.groupReorder.startDrag(event, { groupId: category })}
aria-label={`${textContent.reorderCategoryTitle || "Déplacer la catégorie"} ${category}`}
@ -101,7 +105,8 @@ export function CombosList({
<div className="checklist-section-actions">
<span>{group.combos.length}</span>
<button
className="checklist-section-collapse-button"
className="checklist-section-collapse-button nds-icon-button"
data-size="xs"
type="button"
onClick={() => onCategoryToggle(category)}
aria-expanded={!isCollapsed}

View file

@ -83,7 +83,8 @@ export function InlineTextInput({ value, onCommit, className, ariaLabel, placeho
return (
<input
{...inlineEdit.getInputProps({
className,
className: `${className} nds-inline-input`.trim(),
type: "text",
placeholder,
"aria-label": ariaLabel
})}

View file

@ -47,8 +47,8 @@ export function CountersModule({ toolboxId, moduleId, context, editing, textExch
{editing && (
<div className="module-add-panel">
<form className="inline-form counters-add-form" onSubmit={addCounter}>
<input {...counterDraft.getFieldProps("label", { placeholder: textContent.labelPlaceholder || "Nom du compteur" })} />
<button className="primary">{textContent.addButton || "Ajouter"}</button>
<input {...counterDraft.getFieldProps("label", { className: "nds-input", "data-size": "sm", type: "text", placeholder: textContent.labelPlaceholder || "Nom du compteur" })} />
<button className="nds-button" data-size="sm" data-variant="primary">{textContent.addButton || "Ajouter"}</button>
</form>
</div>
)}
@ -69,7 +69,9 @@ export function CountersModule({ toolboxId, moduleId, context, editing, textExch
return (
<article className={className} key={counter.id} {...getItemProps({ itemId: counter.id, parentId: moduleId })}>
<button
className="counter-drag-handle"
className="counter-drag-handle nds-icon-button"
data-size="sm"
data-variant="ghost"
type="button"
onPointerDown={(event) => itemReorder.startDrag(event, counter.id)}
aria-label={`${textContent.reorderTitle || "Déplacer"} ${counter.label}`}
@ -82,12 +84,12 @@ export function CountersModule({ toolboxId, moduleId, context, editing, textExch
<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"}>
<button className="nds-icon-button" data-size="sm" type="button" onClick={() => updateCounter(counter.id, (item) => ({ ...item, value: item.value - 1 }))} aria-label={`${textContent.decrementLabel || "Décrémenter"} ${counter.label}`}>-</button>
<button className="nds-icon-button" data-size="sm" type="button" onClick={() => updateCounter(counter.id, (item) => ({ ...item, value: item.value + 1 }))} aria-label={`${textContent.incrementLabel || "Incrémenter"} ${counter.label}`}>+</button>
<button className="nds-icon-button" data-size="sm" 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"}>
<button type="button" className="nds-icon-button" data-size="sm" data-variant="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>

View file

@ -7,13 +7,14 @@ const DEFAULT_WIDTHS = [2, 4, 8, 12];
export function DrawingControls({ textContent = {}, active, onToggleActive, eraseActive, onToggleErase, color, colors, onColorChange, width, widths = DEFAULT_WIDTHS, onWidthChange, onUndo, onClear, storageMode, onToggleStorageMode }) {
return (
<div className={`drawing-controls-panel ${active ? "is-open" : ""}`} aria-label={textContent.drawingLabel || "Dessin"}>
<button className={`drawing-control-button ${active ? "active" : ""}`} type="button" aria-pressed={active} title={textContent.drawTitle || "Dessiner"} onClick={onToggleActive}>
<button className={`drawing-control-button nds-icon-button ${active ? "active" : ""}`} data-size="sm" type="button" aria-pressed={active} title={textContent.drawTitle || "Dessiner"} onClick={onToggleActive}>
<Icon name="edit" />
</button>
{active && (
<>
<button
className={`drawing-control-button ${eraseActive ? "active" : ""}`}
className={`drawing-control-button nds-icon-button ${eraseActive ? "active" : ""}`}
data-size="sm"
type="button"
aria-pressed={eraseActive}
title={textContent.eraseTitle || "Gommer"}
@ -33,15 +34,16 @@ export function DrawingControls({ textContent = {}, active, onToggleActive, eras
value={width}
onSelect={onWidthChange}
/>
<button className="drawing-control-button" type="button" title={textContent.undoDrawingTitle || "Annuler le dernier trait"} onClick={onUndo}>
<button className="drawing-control-button nds-icon-button" data-size="sm" type="button" title={textContent.undoDrawingTitle || "Annuler le dernier trait"} onClick={onUndo}>
<Icon name="back" />
</button>
<button className="drawing-control-button danger" type="button" title={textContent.clearDrawingTitle || "Effacer les dessins"} onClick={onClear}>
<button className="drawing-control-button nds-icon-button" data-size="sm" data-variant="danger" type="button" title={textContent.clearDrawingTitle || "Effacer les dessins"} onClick={onClear}>
<Icon name="trash" />
</button>
{onToggleStorageMode && (
<button
className={`module-scroll-button drawing-storage-switch ${storageMode === "permanent" ? "active" : ""}`}
className={`module-scroll-button nds-switch drawing-storage-switch ${storageMode === "permanent" ? "active" : ""}`}
data-size="lg"
type="button"
aria-pressed={storageMode === "permanent"}
aria-label={storageMode === "permanent" ? textContent.permanentTitle || "Annotations permanentes" : textContent.temporaryTitle || "Annotations temporaires"}
@ -67,7 +69,7 @@ export function FormattingColorDropdown({ label, sections }) {
closeOnMouseLeave
preventMouseDown
renderTrigger={({ toggle }) => (
<button className="notepad-color-toggle is-rainbow" type="button" title={label} aria-label={label} onClick={toggle} />
<button className="notepad-color-toggle nds-swatch is-rainbow" data-variant="rainbow" type="button" title={label} aria-label={label} onClick={toggle} />
)}
>
{({ close }) => (
@ -79,7 +81,7 @@ export function FormattingColorDropdown({ label, sections }) {
{section.colors.map((color) => (
<button
key={`${section.label}-${color}`}
className={`notepad-swatch ${section.value === color ? "active" : ""}`}
className={`notepad-swatch nds-swatch ${section.value === color ? "active" : ""}`}
type="button"
title={`${section.label} ${color}`}
aria-label={`${section.label} ${color}`}
@ -110,7 +112,8 @@ export function DrawingColorDropdown({ label, colors, value, onSelect }) {
preventMouseDown
renderTrigger={({ toggle }) => (
<button
className="notepad-color-toggle is-rainbow"
className="notepad-color-toggle nds-swatch is-rainbow"
data-variant="rainbow"
type="button"
title={label}
aria-label={label}
@ -124,7 +127,7 @@ export function DrawingColorDropdown({ label, colors, value, onSelect }) {
{colors.map((color) => (
<button
key={color}
className={`notepad-swatch ${value === color ? "active" : ""}`}
className={`notepad-swatch nds-swatch ${value === color ? "active" : ""}`}
type="button"
title={`${label} ${color}`}
aria-label={`${label} ${color}`}
@ -151,7 +154,7 @@ export function DrawingWidthDropdown({ label, widths, value, onSelect }) {
closeOnMouseLeave
preventMouseDown
renderTrigger={({ toggle }) => (
<button className="drawing-width-toggle" type="button" title={label} aria-label={label} onClick={toggle}>
<button className="drawing-width-toggle nds-icon-button" data-size="sm" type="button" title={label} aria-label={label} onClick={toggle}>
<span style={{ "--line-width": `${value}px` }} />
</button>
)}
@ -161,7 +164,8 @@ export function DrawingWidthDropdown({ label, widths, value, onSelect }) {
{widths.map((lineWidth) => (
<button
key={lineWidth}
className={`drawing-width-button ${value === lineWidth ? "active" : ""}`}
className={`drawing-width-button nds-icon-button ${value === lineWidth ? "active" : ""}`}
data-size="sm"
type="button"
aria-pressed={value === lineWidth}
title={`${label} ${lineWidth}`}

View file

@ -81,10 +81,10 @@ export function CharacteristicsEditor({ equipment, textContent, editing, onUpdat
options={TRAIT_ICONS.map((icon) => ({ icon, label: icon }))}
onChange={(icon) => setDraft((current) => ({ ...current, icon }))}
/>
<input value={draft.category} onChange={(event) => setDraft((current) => ({ ...current, category: event.target.value }))} placeholder={textContent.categoryPlaceholder || "Catégorie"} />
<input value={draft.name} onChange={(event) => setDraft((current) => ({ ...current, name: event.target.value }))} placeholder={textContent.traitNamePlaceholder || "Nom"} />
<input value={draft.value} onChange={(event) => setDraft((current) => ({ ...current, value: event.target.value }))} placeholder={textContent.traitValuePlaceholder || "Valeur"} />
<button>{textContent.addTraitButton || "Ajouter"}</button>
<input className="nds-input" data-size="sm" type="text" value={draft.category} onChange={(event) => setDraft((current) => ({ ...current, category: event.target.value }))} placeholder={textContent.categoryPlaceholder || "Catégorie"} />
<input className="nds-input" data-size="sm" type="text" value={draft.name} onChange={(event) => setDraft((current) => ({ ...current, name: event.target.value }))} placeholder={textContent.traitNamePlaceholder || "Nom"} />
<input className="nds-input" data-size="sm" type="text" value={draft.value} onChange={(event) => setDraft((current) => ({ ...current, value: event.target.value }))} placeholder={textContent.traitValuePlaceholder || "Valeur"} />
<button className="nds-button" data-size="sm" data-variant="primary">{textContent.addTraitButton || "Ajouter"}</button>
</form>
)}
<TraitCategoryList
@ -145,12 +145,12 @@ function TraitCategoryList({ traits, categories, collapsedCategories, reorder, t
return (
<section className={className} key={category} {...reorder.getGroupProps({ groupId: category })}>
<div className="equipment-planner-trait-category-header">
<button type="button" className="task-planner-category-drag-handle" onPointerDown={(event) => reorder.groupReorder.startDrag(event, { groupId: category })} title={textContent.reorderCategoryTitle || "Déplacer la catégorie"}>
<button type="button" className="task-planner-category-drag-handle nds-icon-button" data-size="xs" data-variant="ghost" onPointerDown={(event) => reorder.groupReorder.startDrag(event, { groupId: category })} title={textContent.reorderCategoryTitle || "Déplacer la catégorie"}>
<Icon name="drag" />
</button>
<strong>{category}</strong>
<span>{categoryTraits.length}</span>
<button type="button" className="checklist-section-collapse-button" onClick={() => onToggleCategory(category)} aria-expanded={!collapsed}>
<button type="button" className="checklist-section-collapse-button nds-icon-button" data-size="xs" onClick={() => onToggleCategory(category)} aria-expanded={!collapsed}>
<Icon name={collapsed ? "chevron-down" : "chevron-up"} />
</button>
</div>
@ -182,7 +182,7 @@ function EquipmentTraitRow({ trait, className, itemProps, editing, textContent,
<li className={className} {...itemProps}>
{editing ? (
<>
<button type="button" className="task-planner-drag-handle" onPointerDown={onStartDrag} title={textContent.reorderTraitTitle || "Déplacer"}>
<button type="button" className="task-planner-drag-handle nds-icon-button" data-size="xs" data-variant="ghost" onPointerDown={onStartDrag} title={textContent.reorderTraitTitle || "Déplacer"}>
<Icon name="drag" />
</button>
<IconChoiceDropdown
@ -191,10 +191,10 @@ function EquipmentTraitRow({ trait, className, itemProps, editing, textContent,
options={TRAIT_ICONS.map((icon) => ({ icon, label: icon }))}
onChange={(icon) => onUpdateTrait(trait.id, (current) => ({ ...current, icon }))}
/>
<input {...categoryEdit.getInputProps({ placeholder: textContent.categoryPlaceholder || "Catégorie" })} />
<input {...nameEdit.getInputProps({ placeholder: textContent.traitNamePlaceholder || "Nom" })} />
<input {...valueEdit.getInputProps({ placeholder: textContent.traitValuePlaceholder || "Valeur" })} />
<button type="button" className="task-planner-delete-button danger" onClick={() => onUpdateTrait(trait.id, () => ({ ...trait, name: "" }))} title={textContent.deleteTraitTitle || "Supprimer"}>
<input {...categoryEdit.getInputProps({ className: "nds-inline-input", type: "text", placeholder: textContent.categoryPlaceholder || "Catégorie" })} />
<input {...nameEdit.getInputProps({ className: "nds-inline-input", type: "text", placeholder: textContent.traitNamePlaceholder || "Nom" })} />
<input {...valueEdit.getInputProps({ className: "nds-inline-input", type: "text", placeholder: textContent.traitValuePlaceholder || "Valeur" })} />
<button type="button" className="task-planner-delete-button nds-icon-button" data-size="xs" data-variant="danger" onClick={() => onUpdateTrait(trait.id, () => ({ ...trait, name: "" }))} title={textContent.deleteTraitTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</>

View file

@ -30,7 +30,9 @@ export function EquipmentItem({ equipment, reorder, textContent, onUpdate, onDel
<li className={className} {...reorder.getItemProps({ itemId: equipment.id })}>
<div className="equipment-planner-line">
<button
className="task-planner-drag-handle"
className="task-planner-drag-handle nds-icon-button"
data-size="sm"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.itemReorder.startDrag(event, equipment.id)}
aria-label={`${textContent.reorderEquipmentTitle || "Déplacer"} ${equipment.name}`}
@ -42,9 +44,9 @@ export function EquipmentItem({ equipment, reorder, textContent, onUpdate, onDel
<span className="equipment-planner-type-icon" aria-hidden="true">
<Icon name={equipment.icon || "shield"} />
</span>
<strong {...nameEdit.getContentEditableProps({ className: "equipment-planner-name", title: textContent.renameEquipmentTitle || "Renommer" })}>{equipment.name}</strong>
<strong {...nameEdit.getContentEditableProps({ className: "equipment-planner-name nds-inline-input", title: textContent.renameEquipmentTitle || "Renommer" })}>{equipment.name}</strong>
</div>
<label className="equipment-planner-include-switch">
<label className="equipment-planner-include-switch nds-native-switch">
<input
type="checkbox"
checked={equipment.active}
@ -55,21 +57,22 @@ export function EquipmentItem({ equipment, reorder, textContent, onUpdate, onDel
</label>
<button
type="button"
className={`task-planner-settings-button equipment-planner-row-edit-button ${detailEditing ? "active" : ""}`}
className={`task-planner-settings-button equipment-planner-row-edit-button nds-icon-button ${detailEditing ? "active" : ""}`}
data-size="sm"
onClick={() => setDetailEditing((value) => !value)}
aria-pressed={detailEditing}
title={detailEditing ? textContent.viewModeTitle || "Mode affichage" : textContent.editModeTitle || "Mode édition"}
>
<Icon name="edit" />
</button>
<button type="button" className="task-planner-delete-button danger" onClick={onDelete} aria-label={`${textContent.deleteEquipmentTitle || "Supprimer"} ${equipment.name}`} title={textContent.deleteEquipmentTitle || "Supprimer"}>
<button type="button" className="task-planner-delete-button nds-icon-button" data-size="sm" data-variant="danger" onClick={onDelete} aria-label={`${textContent.deleteEquipmentTitle || "Supprimer"} ${equipment.name}`} title={textContent.deleteEquipmentTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</div>
<div className="equipment-planner-details">
{detailEditing ? (
<>
<div className="equipment-planner-tabs" role="tablist" aria-label={textContent.tabsLabel || "Ajout de caractéristiques"}>
<div className="equipment-planner-tabs nds-radio-button-group" data-size="sm" role="tablist" aria-label={textContent.tabsLabel || "Ajout de caractéristiques"}>
{DETAIL_TABS.map((tab) => (
<button
key={tab}
@ -91,7 +94,7 @@ export function EquipmentItem({ equipment, reorder, textContent, onUpdate, onDel
<section className="equipment-planner-craft-tab">
<label className="equipment-planner-field">
<span>{textContent.obtainLabel || "Obtention"}</span>
<textarea {...obtainEdit.getInputProps({ placeholder: textContent.obtainPlaceholder || "Comment obtenir ou créer cet équipement" })} />
<textarea {...obtainEdit.getInputProps({ className: "nds-textarea", "data-size": "sm", placeholder: textContent.obtainPlaceholder || "Comment obtenir ou créer cet équipement" })} />
</label>
<MaterialsEditor equipment={equipment} textContent={textContent} editing onUpdate={onUpdate} />
</section>

View file

@ -23,9 +23,9 @@ export function MaterialsEditor({ equipment, textContent, editing, onUpdate }) {
<h4>{textContent.materialsTitle || "Matériaux"}</h4>
{editing && (
<form className="inline-form equipment-planner-material-form" onSubmit={addMaterial}>
<input value={draft.name} onChange={(event) => setDraft((current) => ({ ...current, name: event.target.value }))} placeholder={textContent.materialNamePlaceholder || "Matériau"} />
<input type="number" min="1" value={draft.qty} onChange={(event) => setDraft((current) => ({ ...current, qty: event.target.value }))} aria-label={textContent.materialQtyLabel || "Quantité"} />
<button>{textContent.addMaterialButton || "Ajouter"}</button>
<input className="nds-input" data-size="sm" type="text" value={draft.name} onChange={(event) => setDraft((current) => ({ ...current, name: event.target.value }))} placeholder={textContent.materialNamePlaceholder || "Matériau"} />
<input className="nds-input" data-size="sm" type="number" min="1" value={draft.qty} onChange={(event) => setDraft((current) => ({ ...current, qty: event.target.value }))} aria-label={textContent.materialQtyLabel || "Quantité"} />
<button className="nds-button" data-size="sm" data-variant="primary">{textContent.addMaterialButton || "Ajouter"}</button>
</form>
)}
<ul className="equipment-planner-materials">
@ -54,9 +54,9 @@ function MaterialRow({ material, editing, textContent, onUpdate }) {
<li>
{editing ? (
<>
<input {...nameEdit.getInputProps()} />
<input type="number" min="1" value={material.qty} onChange={(event) => onUpdate((current) => ({ ...current, materials: current.materials.map((item) => item.id === material.id ? { ...item, qty: Math.max(1, Number.parseInt(event.target.value, 10) || 1) } : item) }))} />
<button type="button" className="task-planner-delete-button danger" onClick={() => onUpdate((current) => ({ ...current, materials: current.materials.filter((item) => item.id !== material.id) }))} title={textContent.deleteMaterialTitle || "Supprimer"}>
<input {...nameEdit.getInputProps({ className: "nds-inline-input", type: "text" })} />
<input className="nds-input" data-size="xs" type="number" min="1" value={material.qty} onChange={(event) => onUpdate((current) => ({ ...current, materials: current.materials.map((item) => item.id === material.id ? { ...item, qty: Math.max(1, Number.parseInt(event.target.value, 10) || 1) } : item) }))} />
<button type="button" className="task-planner-delete-button nds-icon-button" data-size="xs" data-variant="danger" onClick={() => onUpdate((current) => ({ ...current, materials: current.materials.filter((item) => item.id !== material.id) }))} title={textContent.deleteMaterialTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</>

View file

@ -109,8 +109,8 @@ export function EquipmentPlannerModule({ toolboxId, moduleId, context, editing,
options={EQUIPMENT_ICONS}
onChange={setEquipmentIcon}
/>
<input value={equipmentName} onChange={(event) => setEquipmentName(event.target.value)} placeholder={textContent.equipmentPlaceholder || "Nouvel équipement"} />
<button className="primary">{textContent.addEquipmentButton || "Ajouter"}</button>
<input className="nds-input" data-size="sm" type="text" value={equipmentName} onChange={(event) => setEquipmentName(event.target.value)} placeholder={textContent.equipmentPlaceholder || "Nouvel équipement"} />
<button className="nds-button" data-size="sm" data-variant="primary">{textContent.addEquipmentButton || "Ajouter"}</button>
</form>
</div>
)}

View file

@ -21,7 +21,9 @@ export function IconChoiceDropdown({ className = "", label, value, options, onCh
columns={Math.min(options.length, 5)}
renderTrigger={({ open, toggle }) => (
<button
className={`equipment-planner-picker-trigger ${open ? "active" : ""}`}
className={`equipment-planner-picker-trigger nds-select-trigger ${open ? "active" : ""}`}
data-size="sm"
data-variant="icon-picker"
type="button"
onClick={toggle}
aria-label={label}
@ -29,7 +31,6 @@ export function IconChoiceDropdown({ className = "", label, value, options, onCh
title={selected.label}
>
<Icon name={selected.icon} />
<Icon name="dropdown" />
</button>
)}
>
@ -63,9 +64,8 @@ export function ShapeChoiceDropdown({ label, value, onChange }) {
label={label}
columns={SOCKET_SHAPES.length}
renderTrigger={({ open, toggle }) => (
<button className={`equipment-planner-picker-trigger ${open ? "active" : ""}`} type="button" onClick={toggle} aria-label={label} aria-expanded={open} title={label}>
<button className={`equipment-planner-picker-trigger nds-select-trigger ${open ? "active" : ""}`} data-size="sm" data-variant="icon-picker" type="button" onClick={toggle} aria-label={label} aria-expanded={open} title={label}>
<SocketShapeIcon shape={value} />
<Icon name="dropdown" />
</button>
)}
>
@ -98,9 +98,8 @@ export function ColorChoiceDropdown({ label, value, onChange }) {
label={label}
columns={6}
renderTrigger={({ open, toggle }) => (
<button className={`equipment-planner-picker-trigger ${open ? "active" : ""}`} type="button" onClick={toggle} aria-label={label} aria-expanded={open} title={value}>
<button className={`equipment-planner-picker-trigger nds-select-trigger ${open ? "active" : ""}`} data-size="sm" data-variant="icon-picker" type="button" onClick={toggle} aria-label={label} aria-expanded={open} title={value}>
<span className={`equipment-planner-color-swatch color-${value}`} aria-hidden="true" />
<Icon name="dropdown" />
</button>
)}
>

View file

@ -53,10 +53,10 @@ export function SocketItemsEditor({ equipment, textContent, editing, onUpdate })
<h4>{textContent.socketItemsTitle || "Objets sertis"}</h4>
{editing && (
<form className="inline-form equipment-planner-socket-form" onSubmit={addSocketItem}>
<input value={draft.name} onChange={(event) => setDraft((current) => ({ ...current, name: event.target.value }))} placeholder={textContent.socketNamePlaceholder || "Joyau, materia..."} />
<input className="nds-input" data-size="sm" type="text" value={draft.name} onChange={(event) => setDraft((current) => ({ ...current, name: event.target.value }))} placeholder={textContent.socketNamePlaceholder || "Joyau, materia..."} />
<ShapeChoiceDropdown label={textContent.socketShapeLabel || "Forme"} value={draft.shape} onChange={(shape) => setDraft((current) => ({ ...current, shape }))} />
<ColorChoiceDropdown label={textContent.socketColorLabel || "Couleur"} value={draft.color} onChange={(color) => setDraft((current) => ({ ...current, color }))} />
<button>{textContent.addSocketButton || "Ajouter"}</button>
<button className="nds-button" data-size="sm" data-variant="primary">{textContent.addSocketButton || "Ajouter"}</button>
</form>
)}
<div className="equipment-planner-socket-grid">
@ -104,8 +104,8 @@ function SocketItemEditor({ equipment, socketItem, textContent, editing, onUpdat
<SocketShapeIcon shape={socketItem.shape} color={socketItem.color} />
{editing ? (
<>
<input {...nameEdit.getInputProps()} />
<button type="button" className="task-planner-delete-button danger" onClick={onDelete} title={textContent.deleteSocketTitle || "Supprimer"}>
<input {...nameEdit.getInputProps({ className: "nds-inline-input", type: "text" })} />
<button type="button" className="task-planner-delete-button nds-icon-button" data-size="xs" data-variant="danger" onClick={onDelete} title={textContent.deleteSocketTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</>
@ -119,16 +119,16 @@ function SocketItemEditor({ equipment, socketItem, textContent, editing, onUpdat
<ShapeChoiceDropdown label={textContent.socketShapeLabel || "Forme"} value={socketItem.shape} onChange={(shape) => onUpdate((current) => ({ ...current, shape }))} />
<ColorChoiceDropdown label={textContent.socketColorLabel || "Couleur"} value={socketItem.color} onChange={(color) => onUpdate((current) => ({ ...current, color }))} />
{equipment.socketItems.length > 1 && (
<select defaultValue="" onChange={(event) => { onAddLink(event.target.value); event.target.value = ""; }} aria-label={textContent.addSocketLinkLabel || "Lier à"}>
<select className="nds-select" data-size="xs" defaultValue="" onChange={(event) => { onAddLink(event.target.value); event.target.value = ""; }} aria-label={textContent.addSocketLinkLabel || "Lier à"}>
<option value="">{textContent.addSocketLinkLabel || "Lier à"}</option>
{equipment.socketItems.filter((item) => item.id !== socketItem.id).map((item) => <option key={item.id} value={item.id}>{item.name}</option>)}
</select>
)}
</div>
<form className="inline-form equipment-planner-bonus-form" onSubmit={addBonus}>
<input value={bonusDraft.name} onChange={(event) => setBonusDraft((current) => ({ ...current, name: event.target.value }))} placeholder={textContent.bonusNamePlaceholder || "Bonus"} />
<input value={bonusDraft.value} onChange={(event) => setBonusDraft((current) => ({ ...current, value: event.target.value }))} placeholder={textContent.traitValuePlaceholder || "Valeur"} />
<button aria-label={textContent.addBonusButton || "Ajouter"} title={textContent.addBonusButton || "Ajouter"}>+</button>
<input className="nds-input" data-size="sm" type="text" value={bonusDraft.name} onChange={(event) => setBonusDraft((current) => ({ ...current, name: event.target.value }))} placeholder={textContent.bonusNamePlaceholder || "Bonus"} />
<input className="nds-input" data-size="sm" type="text" value={bonusDraft.value} onChange={(event) => setBonusDraft((current) => ({ ...current, value: event.target.value }))} placeholder={textContent.traitValuePlaceholder || "Valeur"} />
<button className="nds-button" data-size="sm" data-variant="primary" aria-label={textContent.addBonusButton || "Ajouter"} title={textContent.addBonusButton || "Ajouter"}>+</button>
</form>
</>
)}
@ -138,7 +138,7 @@ function SocketItemEditor({ equipment, socketItem, textContent, editing, onUpdat
<span>{bonus.category ? `${bonus.category} · ` : ""}{bonus.name}</span>
<b>{formatValue(bonus.value)}</b>
{editing && (
<button type="button" className="task-planner-delete-button danger" onClick={() => onUpdate((current) => ({ ...current, bonuses: current.bonuses.filter((item) => item.id !== bonus.id) }))} title={textContent.deleteBonusTitle || "Supprimer"}>
<button type="button" className="task-planner-delete-button nds-icon-button" data-size="xs" data-variant="danger" onClick={() => onUpdate((current) => ({ ...current, bonuses: current.bonuses.filter((item) => item.id !== bonus.id) }))} title={textContent.deleteBonusTitle || "Supprimer"}>
<Icon name="trash" />
</button>
)}

View file

@ -49,17 +49,19 @@ export function GoalTreeBranch({ nodes, parentId, selectedNodeId, editing, reord
<article className={`goal-tree-node-card ${editing ? "is-editing" : "is-viewing"} ${hasNodeActions ? "has-actions" : ""}`} data-diagram-node={node.id} onClick={selectFromCard}>
{!editing && (
<div className="goal-tree-node-hover-actions" aria-label={textContent.cardActionsLabel || "Actions de l'objectif"}>
<button type="button" onClick={() => onEditNode(node.id)} aria-label={`${textContent.editTitle || "Modifier"} ${node.title}`} title={textContent.editTitle || "Modifier"}>
<button className="nds-icon-button" data-size="tiny" type="button" onClick={() => onEditNode(node.id)} aria-label={`${textContent.editTitle || "Modifier"} ${node.title}`} title={textContent.editTitle || "Modifier"}>
<Icon name="edit" />
</button>
<button className="danger" type="button" onClick={() => onDeleteNode(node.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${node.title}`} title={textContent.deleteTitle || "Supprimer"}>
<button className="nds-icon-button" data-size="tiny" data-variant="danger" type="button" onClick={() => onDeleteNode(node.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${node.title}`} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</div>
)}
{editing && (
<button
className="goal-tree-drag-handle"
className="goal-tree-drag-handle nds-icon-button"
data-size="micro"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.itemReorder.startDrag(event, node.id)}
aria-label={`${textContent.reorderTitle || "Déplacer"} ${node.title}`}
@ -109,13 +111,14 @@ export function GoalTreeBranch({ nodes, parentId, selectedNodeId, editing, reord
{hasNodeActions && (
<div className="goal-tree-node-actions">
{editing && (
<button type="button" onClick={() => onAdd(node.id)} aria-label={`${textContent.addChildTitle || "Ajouter une sous-étape"} ${node.title}`} title={textContent.addChildTitle || "Ajouter une sous-étape"}>
<button className="nds-icon-button" data-size="micro" type="button" onClick={() => onAdd(node.id)} aria-label={`${textContent.addChildTitle || "Ajouter une sous-étape"} ${node.title}`} title={textContent.addChildTitle || "Ajouter une sous-étape"}>
<Icon name="add" />
</button>
)}
{hasBranchToggle && (
<button
className="goal-tree-branch-toggle"
className="goal-tree-branch-toggle nds-icon-button"
data-size="micro"
type="button"
onClick={() => onToggleBranch(node.id)}
aria-expanded={!branchCollapsed}
@ -126,7 +129,7 @@ export function GoalTreeBranch({ nodes, parentId, selectedNodeId, editing, reord
</button>
)}
{editing && (
<button className="danger" type="button" onClick={() => onDeleteNode(node.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${node.title}`} title={textContent.deleteTitle || "Supprimer"}>
<button className="nds-icon-button" data-size="sm" data-variant="danger" type="button" onClick={() => onDeleteNode(node.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${node.title}`} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />
</button>
)}

View file

@ -94,11 +94,11 @@ export function GoalTreeEditor({
)}
<div className="goal-tree-form-actions">
<button type="button" onClick={onClose}>
<button className="nds-button" data-size="sm" type="button" onClick={onClose}>
<Icon name="close" />
<span>{textContent.closeButton || "Fermer"}</span>
</button>
<button className="danger" type="button" onClick={() => onDeleteNode(selectedNode.id)}>
<button className="nds-button" data-size="sm" data-variant="danger" type="button" onClick={() => onDeleteNode(selectedNode.id)}>
<Icon name="trash" />
<span>{textContent.deleteButton || "Supprimer la branche"}</span>
</button>
@ -113,7 +113,7 @@ function GoalTreeBasePanel({ selectedNode, selectedNodeIsStep, textContent, onSe
<div className="goal-tree-form-panel">
<label>
<span>{textContent.kindLabel || "Type de l'objectif"}</span>
<select value={selectedNode.kind} onChange={(event) => onSetKind(selectedNode.id, event.target.value)}>
<select className="nds-select" data-size="sm" value={selectedNode.kind} onChange={(event) => onSetKind(selectedNode.id, event.target.value)}>
{GOAL_TREE_KINDS.map((kind) => <option key={kind} value={kind}>{textContent[`${kind}Kind`] || KIND_LABELS[kind]}</option>)}
</select>
</label>
@ -139,7 +139,7 @@ function GoalTreeBasePanel({ selectedNode, selectedNodeIsStep, textContent, onSe
{GOAL_TREE_COLORS.map((color) => (
<button
key={color}
className={`goal-tree-color-swatch ${selectedNode.color === color ? "active" : ""}`}
className={`goal-tree-color-swatch nds-swatch ${selectedNode.color === color ? "active" : ""}`}
type="button"
onClick={() => onUpdateNode(selectedNode.id, (node) => ({ ...node, color }))}
aria-pressed={selectedNode.color === color}
@ -164,20 +164,20 @@ function GoalTreeAdvancedPanel({ selectedNode, selectedNodeIsStep, selectedProgr
<div className="goal-tree-form-panel">
<label>
<span>{textContent.resetTypeLabel || "Type de reset"}</span>
<select value={selectedNode.resetType} onChange={(event) => onUpdateNode(selectedNode.id, (node) => ({ ...node, resetType: event.target.value }))}>
<select className="nds-select" data-size="sm" value={selectedNode.resetType} onChange={(event) => onUpdateNode(selectedNode.id, (node) => ({ ...node, resetType: event.target.value }))}>
{GOAL_TREE_RESET_TYPES.map((resetType) => <option key={resetType} value={resetType}>{textContent[`${resetType}Reset`] || RESET_LABELS[resetType]}</option>)}
</select>
</label>
{selectedNode.resetType !== "none" && (
<label>
<span>{textContent.resetTimeLabel || "Heure de reset"}</span>
<input type="time" value={selectedNode.resetTime || "00:00"} onChange={(event) => onUpdateNode(selectedNode.id, (node) => ({ ...node, resetTime: event.target.value || "00:00" }))} />
<input className="nds-time" data-size="sm" type="time" value={selectedNode.resetTime || "00:00"} onChange={(event) => onUpdateNode(selectedNode.id, (node) => ({ ...node, resetTime: event.target.value || "00:00" }))} />
</label>
)}
{selectedNode.resetType === "weekly" && (
<label>
<span>{textContent.weeklyResetDayLabel || "Jour de reset"}</span>
<select value={selectedNode.weeklyResetDay ?? 1} onChange={(event) => onUpdateNode(selectedNode.id, (node) => ({ ...node, weeklyResetDay: Number(event.target.value) }))}>
<select className="nds-select" data-size="sm" value={selectedNode.weeklyResetDay ?? 1} onChange={(event) => onUpdateNode(selectedNode.id, (node) => ({ ...node, weeklyResetDay: Number(event.target.value) }))}>
{GOAL_TREE_WEEK_DAYS.map((day) => <option key={day.value} value={day.value}>{day.label}</option>)}
</select>
</label>
@ -199,7 +199,8 @@ function GoalTreeAdvancedPanel({ selectedNode, selectedNodeIsStep, selectedProgr
</label>
<div className="goal-tree-form-section-title">{textContent.progressSectionTitle || "Progression"}</div>
<button
className={`tool-split-scroll-toggle goal-tree-progress-mode-toggle ${selectedProgressMode === "auto" ? "active" : ""}`}
className={`tool-split-scroll-toggle goal-tree-progress-mode-toggle nds-switch ${selectedProgressMode === "auto" ? "active" : ""}`}
data-size="lg"
type="button"
onClick={() => onUpdateNode(selectedNode.id, (node) => ({ ...node, progressMode: selectedProgressMode === "auto" ? "manual" : "auto" }))}
aria-pressed={selectedProgressMode === "auto"}

View file

@ -22,13 +22,13 @@ export function GoalTreePrerequisitePicker({ nodes, node, textContent, onToggle
<div className="goal-tree-prerequisite-list themed-scrollbar">
{primaryParent && (
<label className="goal-tree-prerequisite-item is-primary">
<input type="checkbox" checked disabled readOnly />
<input className="nds-checkbox" data-size="xs" type="checkbox" checked disabled readOnly />
<span>{primaryParent.title}</span>
</label>
)}
{options.map((candidate) => (
<label className="goal-tree-prerequisite-item" key={candidate.id}>
<input type="checkbox" checked={parentIds.has(candidate.id)} onChange={() => onToggle(candidate.id)} />
<input className="nds-checkbox" data-size="xs" type="checkbox" checked={parentIds.has(candidate.id)} onChange={() => onToggle(candidate.id)} />
<span>{candidate.title}</span>
</label>
))}
@ -41,6 +41,7 @@ export function GoalTreeTitleInput({ value, onChange }) {
const [draft, setDraft] = useState(value || "");
return (
<input
type="text"
value={draft}
onChange={(event) => {
const title = event.target.value;
@ -54,7 +55,7 @@ export function GoalTreeTitleInput({ value, onChange }) {
export function GoalTreeFormulaInput({ value, onChange, hasParent, textContent, placeholder, title, className = "", "aria-label": ariaLabel }) {
const normalizedValue = String(value ?? "");
if (!hasParent) {
return <input aria-label={ariaLabel} className={className} inputMode="decimal" onChange={(event) => onChange(event.target.value.replaceAll(",", "."))} placeholder={placeholder} title={title} value={normalizedValue} />;
return <input type="text" aria-label={ariaLabel} className={`${className} nds-inline-input`.trim()} inputMode="decimal" onChange={(event) => onChange(event.target.value.replaceAll(",", "."))} placeholder={placeholder} title={title} value={normalizedValue} />;
}
return (
@ -87,7 +88,7 @@ export function GoalTreeContentEditor({ node, textContent, onAdd, onUpdate, onDe
<div className="goal-tree-form-section-title">{textContent.contentSectionTitle || "Contenu"}</div>
{(node.items || []).map((item) => (
<div className={`goal-tree-content-editor-row is-${node.kind}`} key={item.id}>
<input className="goal-tree-content-label-input" value={item.label} placeholder={textContent.itemLabelPlaceholder || "Élément"} onChange={(event) => onUpdate(item.id, (current) => ({ ...current, label: event.target.value }))} aria-label={textContent.itemLabelLabel || "Libellé"} />
<input className="goal-tree-content-label-input nds-input" data-size="sm" type="text" value={item.label} placeholder={textContent.itemLabelPlaceholder || "Élément"} onChange={(event) => onUpdate(item.id, (current) => ({ ...current, label: event.target.value }))} aria-label={textContent.itemLabelLabel || "Libellé"} />
{node.kind === "checklist" && (
<GoalTreeFormulaInput
aria-label={textContent.itemTotalLabel || "Total"}
@ -100,12 +101,12 @@ export function GoalTreeContentEditor({ node, textContent, onAdd, onUpdate, onDe
title={textContent.itemTotalHelp || "Saisissez un nombre ou une formule avec base pour référencer la cible du parent."}
/>
)}
<button className="danger" type="button" onClick={() => onDelete(item.id)} aria-label={textContent.deleteTitle || "Supprimer"} title={textContent.deleteTitle || "Supprimer"}>
<button className="nds-icon-button" data-size="xs" data-variant="danger" type="button" onClick={() => onDelete(item.id)} aria-label={textContent.deleteTitle || "Supprimer"} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</div>
))}
<button className="goal-tree-content-add-button" type="button" onClick={onAdd}>
<button className="goal-tree-content-add-button nds-button" data-size="xs" data-variant="dashed" type="button" onClick={onAdd}>
<Icon name="add" />
<span>{textContent.addContentItemButton || "Ajouter une ligne"}</span>
</button>
@ -122,12 +123,9 @@ export function GoalTreeIconPicker({ label, value, textContent, onChange }) {
menuClassName="goal-tree-icon-picker-menu"
label={label}
renderTrigger={({ open, toggle }) => (
<button className="goal-tree-icon-picker-button" type="button" onClick={toggle} aria-label={label} aria-expanded={open} title={label}>
<span className="goal-tree-icon-preview" aria-hidden="true">
<Icon name={value} />
</span>
<button className="goal-tree-icon-picker-button nds-select-trigger" data-size="sm" data-variant="icon-picker" type="button" onClick={toggle} aria-label={label} aria-expanded={open} title={label}>
<Icon name={value} />
<span>{textContent[`icon${value}`] || value}</span>
<Icon name="dropdown" />
</button>
)}
>
@ -136,7 +134,8 @@ export function GoalTreeIconPicker({ label, value, textContent, onChange }) {
{GOAL_TREE_ICON_NAMES.map((icon) => (
<button
key={icon}
className={icon === value ? "active" : ""}
className={`nds-icon-button ${icon === value ? "active" : ""}`.trim()}
data-size="sm"
type="button"
onClick={() => {
onChange(icon);

View file

@ -7,10 +7,10 @@ function GoalTreeChecklistInlineControl({ item, textContent, onUpdate }) {
const target = Math.max(1, item.qtyTarget || 1);
const current = Math.min(Math.max(0, item.qtyCurrent || 0), target);
if (target <= 1) {
return <input className="goal-tree-node-check" type="checkbox" checked={current >= 1} onChange={(event) => onUpdate((entry) => ({ ...entry, qtyCurrent: event.target.checked ? 1 : 0 }))} aria-label={textContent.itemCheckedLabel || "État"} />;
return <input className="goal-tree-node-check nds-checkbox" data-size="xs" type="checkbox" checked={current >= 1} onChange={(event) => onUpdate((entry) => ({ ...entry, qtyCurrent: event.target.checked ? 1 : 0 }))} aria-label={textContent.itemCheckedLabel || "État"} />;
}
return (
<span className="goal-tree-content-qty-stepper">
<span className="goal-tree-content-qty-stepper nds-quantity-stepper" data-size="sm">
<button type="button" onClick={() => onUpdate((entry) => ({ ...entry, qtyCurrent: Math.max(0, (entry.qtyCurrent || 0) - 1) }))} aria-label={textContent.decrementItemTitle || "Retirer"} title={textContent.decrementItemTitle || "Retirer"}>
<span aria-hidden="true">-</span>
</button>
@ -26,10 +26,10 @@ export function GoalTreeStepLeafControl({ node, textContent, onChange }) {
const target = Math.max(1, node.computedQtyTarget || node.qtyTarget || 1);
const current = Math.min(Math.max(0, node.qtyCurrent || 0), target);
if (target <= 1) {
return <input className="goal-tree-step-check" type="checkbox" checked={current >= 1} onChange={(event) => onChange(event.target.checked ? 1 : 0)} aria-label={textContent.itemCheckedLabel || "État"} />;
return <input className="goal-tree-step-check nds-checkbox" data-size="xs" type="checkbox" checked={current >= 1} onChange={(event) => onChange(event.target.checked ? 1 : 0)} aria-label={textContent.itemCheckedLabel || "État"} />;
}
return (
<span className="goal-tree-step-qty-stepper goal-tree-content-qty-stepper">
<span className="goal-tree-step-qty-stepper goal-tree-content-qty-stepper nds-quantity-stepper" data-size="xs">
<button type="button" onClick={() => onChange(Math.max(0, current - 1))} aria-label={textContent.decrementItemTitle || "Retirer"} title={textContent.decrementItemTitle || "Retirer"}>
<span aria-hidden="true">-</span>
</button>

View file

@ -87,7 +87,8 @@ export function GoalTreeView({
return (
<section className={`goal-tree-view tool-split-results ${verticalView ? "is-vertical" : "is-horizontal"} ${editing ? "is-editing" : ""}`} aria-label={textContent.treeTitle || "Arbre d'objectifs"}>
<button
className={`module-scroll-button goal-tree-orientation-switch ${verticalView ? "active" : ""}`}
className={`module-scroll-button goal-tree-orientation-switch nds-switch ${verticalView ? "active" : ""}`}
data-size="lg"
type="button"
onClick={onToggleOrientation}
aria-label={verticalView ? textContent.horizontalViewTitle || "Afficher horizontalement" : textContent.verticalViewTitle || "Afficher verticalement"}
@ -100,7 +101,7 @@ export function GoalTreeView({
<div className="goal-tree-scroll-area themed-scrollbar">
<div className="goal-tree-view-controls">
{editing && (
<button className="goal-tree-add-root-button" type="button" onClick={() => onAdd("")}>
<button className="goal-tree-add-root-button nds-button" data-size="sm" data-variant="dashed" type="button" onClick={() => onAdd("")}>
<Icon name="add" />
<span>{textContent.addRootSchemaButton || "Ajouter un objectif a la racine"}</span>
</button>

View file

@ -123,7 +123,8 @@ export function ImageAnnotationModule({ toolboxId, moduleId, context, editing, t
<img src={data.image} alt={textContent.imageAlt || "Image annotée"} draggable="false" />
<div className="annotation-preview-actions">
<button
className="tool-split-action-button"
className="tool-split-action-button nds-icon-button"
data-size="sm"
type="button"
onClick={() => context.setImage({
dataUrl: data.image,
@ -164,7 +165,7 @@ export function ImageAnnotationModule({ toolboxId, moduleId, context, editing, t
<Icon name="zoom" />
</button>
{editing && (
<button className="tool-split-action-button danger" type="button" onClick={removeImage} aria-label={textContent.deleteImageTitle || "Supprimer l'image"} title={textContent.deleteImageTitle || "Supprimer l'image"}>
<button className="tool-split-action-button nds-icon-button" data-size="sm" data-variant="danger" type="button" onClick={removeImage} aria-label={textContent.deleteImageTitle || "Supprimer l'image"} title={textContent.deleteImageTitle || "Supprimer l'image"}>
<Icon name="trash" />
</button>
)}

View file

@ -104,7 +104,9 @@ export function ImagesModule({ toolboxId, moduleId, context, editing, textExchan
return (
<figure key={image.id} className={className} {...getItemProps({ itemId: image.id, parentId: moduleId })}>
<button
className="image-drag-handle"
className="image-drag-handle nds-icon-button"
data-size="sm"
data-variant="ghost"
type="button"
onPointerDown={(event) => itemReorder.startDrag(event, image.id)}
aria-label={textContent.reorderAriaLabel || "Déplacer l'image"}
@ -117,7 +119,8 @@ export function ImagesModule({ toolboxId, moduleId, context, editing, textExchan
</button>
{editing ? (
<input
className="image-label-input"
className="image-label-input nds-inline-input"
type="text"
value={image.label || ""}
placeholder={textContent.labelPlaceholder || "Libellé de l'image"}
onChange={(event) => updateImage(image.id, { label: event.target.value })}
@ -128,7 +131,8 @@ export function ImagesModule({ toolboxId, moduleId, context, editing, textExchan
) : null}
<div className="image-actions">
<button
className="image-annotate-button"
className="image-annotate-button nds-icon-button"
data-size="sm"
type="button"
onClick={() => context.createImageAnnotationModule?.(image.dataUrl)}
aria-label={textContent.annotateAriaLabel || "Annoter l'image"}
@ -136,7 +140,7 @@ export function ImagesModule({ toolboxId, moduleId, context, editing, textExchan
>
<Icon name="map" />
</button>
<button className="image-delete-button danger" type="button" onClick={() => {
<button className="image-delete-button nds-icon-button" data-size="sm" data-variant="danger" type="button" onClick={() => {
context.setModuleData(toolboxId, moduleId, { images: data.images.filter((item) => item.id !== image.id) });
}} aria-label={textContent.deleteAriaLabel || "Supprimer l'image"} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />

View file

@ -59,9 +59,9 @@ export function LinksModule({ toolboxId, moduleId, context, editing, textExchang
{editing && (
<div className="module-add-panel">
<form className="inline-form links-add-form" onSubmit={addLink}>
<input {...linkDraft.getFieldProps("title", { placeholder: textContent.titlePlaceholder || "Nom du lien" })} />
<input {...linkDraft.getFieldProps("url", { placeholder: textContent.urlPlaceholder || "https://..." })} />
<button className="primary">{textContent.addButton || "Ajouter"}</button>
<input {...linkDraft.getFieldProps("title", { className: "nds-input", "data-size": "sm", type: "text", placeholder: textContent.titlePlaceholder || "Nom du lien" })} />
<input {...linkDraft.getFieldProps("url", { className: "nds-input", "data-size": "sm", type: "url", placeholder: textContent.urlPlaceholder || "https://..." })} />
<button className="nds-button" data-size="sm" data-variant="primary">{textContent.addButton || "Ajouter"}</button>
</form>
</div>
)}
@ -82,7 +82,9 @@ export function LinksModule({ toolboxId, moduleId, context, editing, textExchang
return (
<li className={className} key={link.id} {...getItemProps({ itemId: link.id, parentId: moduleId })}>
<button
className="link-drag-handle"
className="link-drag-handle nds-icon-button"
data-size="sm"
data-variant="ghost"
type="button"
onPointerDown={(event) => itemReorder.startDrag(event, link.id)}
aria-label={`${textContent.reorderTitle || "Déplacer"} ${link.title || link.url}`}
@ -95,10 +97,10 @@ export function LinksModule({ toolboxId, moduleId, context, editing, textExchang
<span>{link.url}</span>
</a>
<div>
<button type="button" onClick={() => copyUrl(link)} aria-label={`${textContent.copyTitle || "Copier"} ${link.title || link.url}`} title={copiedId === link.id ? textContent.copiedTitle || "Copié" : textContent.copyTitle || "Copier"}>
<button type="button" className="nds-icon-button" data-size="sm" onClick={() => copyUrl(link)} aria-label={`${textContent.copyTitle || "Copier"} ${link.title || link.url}`} title={copiedId === link.id ? textContent.copiedTitle || "Copié" : textContent.copyTitle || "Copier"}>
<Icon name="copy" />
</button>
<button type="button" className="danger" onClick={() => save(data.links.filter((item) => item.id !== link.id))} aria-label={`${textContent.deleteTitle || "Supprimer"} ${link.title || link.url}`} title={textContent.deleteTitle || "Supprimer"}>
<button type="button" className="nds-icon-button" data-size="sm" data-variant="danger" onClick={() => save(data.links.filter((item) => item.id !== link.id))} aria-label={`${textContent.deleteTitle || "Supprimer"} ${link.title || link.url}`} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</div>

View file

@ -40,7 +40,7 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang
function resizeMemoTextarea(element) {
if (!element) return;
element.style.height = "auto";
element.style.height = `${element.scrollHeight}px`;
element.style.height = `${element.scrollHeight + 2}px`;
}
function save(nextData) {
@ -93,7 +93,7 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang
{editing && (
<div className="module-add-panel">
<button className="primary memos-add-button" type="button" onClick={addMemo}>
<button className="memos-add-button nds-button" data-size="sm" data-variant="primary" type="button" onClick={addMemo}>
<Icon name="add" />
<span>{textContent.addButton || "Ajouter"}</span>
</button>
@ -102,7 +102,8 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang
<div className="memos-toolbar">
<button
className={`memos-ticker-toggle ${data.tickerEnabled ? "active" : ""}`}
className={`memos-ticker-toggle nds-icon-button ${data.tickerEnabled ? "active" : ""}`}
data-size="sm"
type="button"
onClick={() => save({ ...data, tickerEnabled: !data.tickerEnabled })}
aria-pressed={data.tickerEnabled}
@ -135,7 +136,9 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang
return (
<article className={className} key={memo.id} style={{ "--memo-color": memo.color }} {...getItemProps({ itemId: memo.id, parentId: moduleId })}>
<button
className="memo-drag-handle"
className="memo-drag-handle nds-icon-button"
data-size="sm"
data-variant="ghost"
type="button"
onPointerDown={(event) => itemReorder.startDrag(event, memo.id)}
aria-label={`${textContent.reorderTitle || "Déplacer"} ${memo.text}`}
@ -144,6 +147,9 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang
<Icon name="drag" />
</button>
<textarea
className="nds-textarea"
data-size="sm"
data-variant="ghost"
ref={(element) => {
if (element) {
textareaRefs.current.set(memo.id, element);
@ -169,7 +175,7 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang
label={textContent.colorLabel || "Couleur du mémo"}
renderTrigger={({ toggle }) => (
<button
className="notepad-color-toggle"
className="notepad-color-toggle nds-swatch"
type="button"
style={{ "--swatch-color": memo.color }}
onClick={toggle}
@ -183,7 +189,7 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang
{TEXT_COLORS.map((color) => (
<button
key={color}
className={`notepad-swatch ${memo.color === color ? "active" : ""}`}
className={`notepad-swatch nds-swatch ${memo.color === color ? "active" : ""}`}
type="button"
style={{ "--swatch-color": color }}
onClick={() => {
@ -196,14 +202,14 @@ export function MemosModule({ toolboxId, moduleId, context, editing, textExchang
</>
)}
</CompactDropdown>
<button className="memo-delete-button danger" type="button" onClick={() => deleteMemo(memo.id)} aria-label={textContent.deleteTitle || "Supprimer le mémo"} title={textContent.deleteTitle || "Supprimer"}>
<button className="memo-delete-button nds-icon-button" data-size="sm" data-variant="danger" type="button" onClick={() => deleteMemo(memo.id)} aria-label={textContent.deleteTitle || "Supprimer le mémo"} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</footer>
</article>
);
}) : (
<button className="memos-empty" type="button" onClick={addMemo}>
<button className="memos-empty nds-button" data-size="sm" data-variant="dashed" type="button" onClick={addMemo}>
<Icon name="add" />
<span>{textContent.emptyLabel || "Ajouter un premier mémo"}</span>
</button>

View file

@ -295,7 +295,11 @@ export function NotepadModule({ toolboxId, moduleId, context, textExchangeOpen }
label={textContent.pagesMenuLabel || "Pages du bloc note"}
renderTrigger={({ open, toggle }) => (
<button
className={`notepad-pages-toggle ${open ? "active" : ""}`}
className={`notepad-pages-toggle nds-icon-button ${open ? "active" : ""}`}
data-size="sm"
data-variant="panel-toggle"
data-edge="right"
data-border="joined"
type="button"
onClick={toggle}
aria-label={textContent.pagesToggleLabel || "Afficher les pages du bloc note"}
@ -311,10 +315,12 @@ export function NotepadModule({ toolboxId, moduleId, context, textExchangeOpen }
<div className="notepad-pages-list">
{normalizedData.pages.map((page) => (
<div className={`notepad-page-row ${page.id === normalizedData.activePageId ? "active" : ""}`} key={page.id}>
<div className="notepad-page-main">
<div className="notepad-page-main nds-list-row">
{editingPageId === page.id ? (
<input
className="notepad-page-title-input"
className="notepad-page-title-input nds-input"
data-size="xs"
type="text"
value={pageTitleDrafts[page.id] ?? page.title}
aria-label={`${textContent.renamePageLabel || "Renommer"} ${page.title}`}
autoFocus
@ -339,7 +345,8 @@ export function NotepadModule({ toolboxId, moduleId, context, textExchangeOpen }
</div>
<div className="notepad-page-actions">
<button
className="notepad-page-edit"
className="notepad-page-edit nds-icon-button"
data-size="xxs"
type="button"
onClick={() => {
setPageTitleDrafts((current) => ({ ...current, [page.id]: page.title }));
@ -351,7 +358,9 @@ export function NotepadModule({ toolboxId, moduleId, context, textExchangeOpen }
<Icon name="edit" />
</button>
<button
className="notepad-page-delete danger"
className="notepad-page-delete nds-icon-button"
data-size="xxs"
data-variant="danger"
type="button"
onClick={() => deletePage(page.id, close)}
disabled={normalizedData.pages.length <= 1}
@ -364,7 +373,7 @@ export function NotepadModule({ toolboxId, moduleId, context, textExchangeOpen }
</div>
))}
</div>
<button className="notepad-page-add" type="button" onClick={() => addPage(close)}>
<button className="notepad-page-add nds-button" data-size="xs" data-variant="dashed" type="button" onClick={() => addPage(close)}>
<Icon name="add" />
<span>{textContent.addPageLabel || "Ajouter une page"}</span>
</button>

View file

@ -52,7 +52,8 @@ export function NotepadToolbar({
function ToolbarButton({ active, title, children, onClick }) {
return (
<button
className={`notepad-toolbar-button ${active ? "active" : ""}`}
className={`notepad-toolbar-button nds-icon-button ${active ? "active" : ""}`}
data-size="sm"
type="button"
aria-pressed={active}
title={title}
@ -74,7 +75,8 @@ function ListTypeDropdown({ textContent, activeFormats, onCommand }) {
preventMouseDown
renderTrigger={({ toggle }) => (
<button
className={`notepad-toolbar-button ${activeFormats.bulletList || activeFormats.numberList ? "active" : ""}`}
className={`notepad-toolbar-button nds-icon-button ${activeFormats.bulletList || activeFormats.numberList ? "active" : ""}`}
data-size="sm"
type="button"
aria-pressed={activeFormats.bulletList || activeFormats.numberList}
title={textContent.listGroupLabel || "Listes"}
@ -87,7 +89,8 @@ function ListTypeDropdown({ textContent, activeFormats, onCommand }) {
{({ close }) => (
<>
<button
className={`notepad-list-option ${activeFormats.bulletList ? "active" : ""}`}
className={`notepad-list-option nds-icon-button ${activeFormats.bulletList ? "active" : ""}`}
data-size="sm"
type="button"
aria-pressed={activeFormats.bulletList}
onClick={() => {
@ -98,7 +101,8 @@ function ListTypeDropdown({ textContent, activeFormats, onCommand }) {
•
</button>
<button
className={`notepad-list-option ${activeFormats.numberList ? "active" : ""}`}
className={`notepad-list-option nds-icon-button ${activeFormats.numberList ? "active" : ""}`}
data-size="sm"
type="button"
aria-pressed={activeFormats.numberList}
onClick={() => {

View file

@ -33,6 +33,7 @@ export function TableRow({ rowIndex, columns, cells, evaluatedCells, editingCell
return (
<input
type="text"
key={address}
ref={(element) => registerInput(address, element)}
className={className}
@ -82,6 +83,7 @@ export function HeaderLabelInput({ className, value, fallback, onSave, ...props
<input
{...inlineEdit.getInputProps({
...props,
type: "text",
className,
onFocus: (event) => {
if (value === fallback) window.requestAnimationFrame(() => event.target.select());

View file

@ -8,7 +8,8 @@ export function TableToolbar({ rows, columns, textContent, onResize, onCopy }) {
<div className="notepad-format-controls table-controls">
<div className="notepad-toolbar-group" role="group" aria-label={textContent.rowActionsLabel || "Lignes"}>
<button
className="notepad-toolbar-button"
className="notepad-toolbar-button nds-icon-button"
data-size="sm"
type="button"
onClick={() => onResize(Math.min(MAX_ROWS, rows + 1), columns)}
disabled={rows >= MAX_ROWS}
@ -18,7 +19,9 @@ export function TableToolbar({ rows, columns, textContent, onResize, onCopy }) {
<Icon name="add-row" />
</button>
<button
className="notepad-toolbar-button danger"
className="notepad-toolbar-button nds-icon-button"
data-size="sm"
data-variant="danger"
type="button"
onClick={() => onResize(Math.max(1, rows - 1), columns)}
disabled={rows <= 1}
@ -30,7 +33,8 @@ export function TableToolbar({ rows, columns, textContent, onResize, onCopy }) {
</div>
<div className="notepad-toolbar-group" role="group" aria-label={textContent.columnActionsLabel || "Colonnes"}>
<button
className="notepad-toolbar-button"
className="notepad-toolbar-button nds-icon-button"
data-size="sm"
type="button"
onClick={() => onResize(rows, Math.min(MAX_COLUMNS, columns + 1))}
disabled={columns >= MAX_COLUMNS}
@ -40,7 +44,9 @@ export function TableToolbar({ rows, columns, textContent, onResize, onCopy }) {
<Icon name="add-column" />
</button>
<button
className="notepad-toolbar-button danger"
className="notepad-toolbar-button nds-icon-button"
data-size="sm"
data-variant="danger"
type="button"
onClick={() => onResize(rows, Math.max(1, columns - 1))}
disabled={columns <= 1}
@ -56,7 +62,8 @@ export function TableToolbar({ rows, columns, textContent, onResize, onCopy }) {
</div>
<div className="notepad-toolbar-group table-copy-group">
<button
className="notepad-toolbar-button table-copy-button"
className="notepad-toolbar-button table-copy-button nds-button"
data-size="sm"
type="button"
onClick={onCopy}
aria-label={textContent.copyTitle || "Copier les valeurs"}

View file

@ -78,6 +78,8 @@ export function TaskPlannerBranch({
<li className="task-planner-hidden-parent" key={`${task.id}:visible-children`}>
<div className="task-planner-hidden-parent-line">
<input
className="nds-checkbox"
data-size="sm"
type="checkbox"
checked={task.checked}
onChange={(event) => onUpdateTask(task.id, (current) => ({
@ -158,7 +160,9 @@ export function TaskPlannerBranch({
<div className="checklist-section-header task-planner-category-header">
<div className="task-planner-category-title">
<button
className="task-planner-category-drag-handle"
className="task-planner-category-drag-handle nds-icon-button"
data-size="xs"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.groupReorder.startDrag(event, { groupId: group.category, parentId })}
aria-label={`${textContent.categoryReorderTitle || "Déplacer la catégorie"} ${group.category}`}
@ -171,7 +175,8 @@ export function TaskPlannerBranch({
<div className="task-planner-category-actions">
<span>{groupCompletedCount} / {groupCount}</span>
<button
className="checklist-section-collapse-button"
className="checklist-section-collapse-button nds-icon-button"
data-size="xs"
type="button"
onClick={() => onToggleCategoryCollapsed(group.category)}
aria-expanded={!isCollapsed}

View file

@ -65,7 +65,9 @@ export function TaskPlannerItem({
<li className={className} {...reorder.getItemProps({ itemId: task.id, groupId: getTaskCategoryDataset(task, tasks, parentMap), parentId })}>
<div className="task-planner-line">
<button
className="task-planner-drag-handle"
className="task-planner-drag-handle nds-icon-button"
data-size="xs"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.itemReorder.startDrag(event, task.id)}
aria-label={`${textContent.reorderTitle || "Déplacer"} ${task.title}`}
@ -74,6 +76,8 @@ export function TaskPlannerItem({
<Icon name="drag" />
</button>
<input
className="nds-checkbox"
data-size="sm"
type="checkbox"
checked={task.checked}
onChange={updateChecked}
@ -81,12 +85,14 @@ export function TaskPlannerItem({
/>
<input
{...titleEdit.getInputProps({
className: "tool-split-entry-label task-planner-title-input",
className: "tool-split-entry-label task-planner-title-input nds-inline-input",
type: "text",
"aria-label": textContent.titleLabel || "Titre"
})}
/>
<select
className="task-planner-type-select"
className="task-planner-type-select nds-select"
data-size="xs"
value={task.type}
onChange={(event) => onUpdateTask(task.id, (current) => {
const nextType = event.target.value;
@ -104,7 +110,8 @@ export function TaskPlannerItem({
)}
<div className="task-planner-actions">
<button
className={`task-planner-dropdown-button ${descriptionOpen ? "active" : ""}`}
className={`task-planner-dropdown-button nds-icon-button ${descriptionOpen ? "active" : ""}`}
data-size="xs"
type="button"
onClick={onToggleDescription}
aria-expanded={descriptionOpen}
@ -114,7 +121,8 @@ export function TaskPlannerItem({
<Icon name={descriptionOpen ? "chevron-up" : "chevron-down"} />
</button>
<button
className={`task-planner-settings-button ${settingsOpen ? "active" : ""}`}
className={`task-planner-settings-button nds-icon-button ${settingsOpen ? "active" : ""}`}
data-size="xs"
type="button"
onClick={onToggleSettings}
aria-expanded={settingsOpen}
@ -123,7 +131,7 @@ export function TaskPlannerItem({
>
<Icon name="settings" />
</button>
<button className="task-planner-delete-button danger" type="button" onClick={() => onDeleteTask(task.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${task.title}`} title={textContent.deleteTitle || "Supprimer"}>
<button className="task-planner-delete-button nds-icon-button" data-size="xs" data-variant="danger" type="button" onClick={() => onDeleteTask(task.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${task.title}`} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</div>

View file

@ -110,7 +110,9 @@ function TaskDescriptionPanel({ task, tasks, parentMap, parentId, textContent, o
<span>{textContent.categoryLabel || "Catégorie"}</span>
<input
{...categoryEdit.getInputProps({
className: "nds-inline-input",
disabled: !categoryEditable,
type: "text",
placeholder: textContent.categoryPlaceholder || "Nom de catégorie",
"aria-label": `${textContent.categoryLabel || "Catégorie"} ${task.title}`
})}
@ -120,7 +122,8 @@ function TaskDescriptionPanel({ task, tasks, parentMap, parentId, textContent, o
<span>{textContent.descriptionLabel || "Description"}</span>
<textarea
{...descriptionEdit.getInputProps({
className: "task-planner-description",
className: "task-planner-description nds-textarea",
"data-size": "sm",
placeholder: textContent.descriptionPlaceholder || "Description",
"aria-label": `${textContent.descriptionLabel || "Description"} ${task.title}`
})}
@ -138,6 +141,8 @@ function TaskResetSettings({ task, data, textContent, onUpdateTask }) {
<span>{textContent.taskDailyResetTimeLabel || "Heure quotidienne"}</span>
<div className="task-planner-reset-control">
<input
className="nds-time"
data-size="xs"
type="time"
value={task.dailyResetTime || data.resetTime}
onChange={(event) => onUpdateTask(task.id, (current) => {
@ -149,6 +154,8 @@ function TaskResetSettings({ task, data, textContent, onUpdateTask }) {
aria-label={`${textContent.taskDailyResetTimeLabel || "Heure quotidienne"} ${task.title}`}
/>
<button
className="task-planner-reset-button nds-icon-button"
data-size="xs"
type="button"
onClick={() => onUpdateTask(task.id, (current) => {
const nextTask = { ...current };
@ -171,6 +178,8 @@ function TaskResetSettings({ task, data, textContent, onUpdateTask }) {
<label className="task-planner-reset-override">
<span>{textContent.taskWeeklyResetDayLabel || "Jour hebdo"}</span>
<select
className="nds-select"
data-size="xs"
value={task.weeklyResetDay ?? ""}
onChange={(event) => onUpdateTask(task.id, (current) => {
const nextTask = { ...current };
@ -212,6 +221,8 @@ function LinksEditor({ label, relations, task, tasks, parentMap, relationTargets
<div className="task-planner-relations">
<span>{label}</span>
<select
className="nds-select"
data-size="sm"
value=""
onChange={(event) => {
onAddLinkTarget(task.id, event.target.value);
@ -228,6 +239,8 @@ function LinksEditor({ label, relations, task, tasks, parentMap, relationTargets
<span>{getTaskTitle(tasks, relation.toTaskId)}</span>
<label title={prerequisiteLabel}>
<input
className="nds-checkbox"
data-size="sm"
type="checkbox"
checked={relation.prerequisite}
onChange={(event) => onSetPrerequisite(task.id, relation.toTaskId, event.target.checked)}
@ -235,7 +248,7 @@ function LinksEditor({ label, relations, task, tasks, parentMap, relationTargets
/>
<span>{prerequisiteLabel}</span>
</label>
<button type="button" onClick={() => onRemoveLinkTarget(task.id, relation.toTaskId)} title={removeLabel} aria-label={`${removeLabel} ${getTaskTitle(tasks, relation.toTaskId)}`}>
<button className="nds-icon-button" data-size="xs" data-variant="danger" type="button" onClick={() => onRemoveLinkTarget(task.id, relation.toTaskId)} title={removeLabel} aria-label={`${removeLabel} ${getTaskTitle(tasks, relation.toTaskId)}`}>
<Icon name="close" />
</button>
</div>

View file

@ -7,17 +7,19 @@ export function TaskPlannerSettings({ data, textContent, onWeeklyResetDayChange,
<section className="task-planner-settings" aria-label={textContent.settingsTitle || "Réglages du planificateur"}>
<label>
<span>{textContent.globalWeeklyResetDayLabel || "Reset hebdo"}</span>
<select value={data.weeklyResetDay} onChange={(event) => onWeeklyResetDayChange(Number(event.target.value))}>
<select className="nds-select" data-size="sm" value={data.weeklyResetDay} onChange={(event) => onWeeklyResetDayChange(Number(event.target.value))}>
{WEEK_DAYS.map((day) => <option key={day.value} value={day.value}>{day.label}</option>)}
</select>
</label>
<label>
<span>{textContent.resetTimeLabel || "Heure de reset"}</span>
<input type="time" value={data.resetTime} onChange={(event) => onResetTimeChange(event.target.value || "00:00")} />
<input className="nds-time" data-size="sm" type="time" value={data.resetTime} onChange={(event) => onResetTimeChange(event.target.value || "00:00")} />
</label>
<div className="task-planner-settings-actions">
<button
className="task-planner-clear-completed-button danger"
className="task-planner-clear-completed-button nds-button"
data-size="sm"
data-variant="danger"
type="button"
onClick={onClearCompletedUniqueTasks}
disabled={!data.tasks.some((task) => task.type === "unique" && task.checked)}

View file

@ -23,7 +23,8 @@ export function TaskPlannerTopbar({
<div className="task-planner-topbar">
<div className="task-planner-global-controls">
<button
className={`tool-split-scroll-toggle task-planner-hide-completed-toggle ${data.hideCompleted ? "active" : ""}`}
className={`tool-split-scroll-toggle task-planner-hide-completed-toggle nds-switch ${data.hideCompleted ? "active" : ""}`}
data-size="lg"
type="button"
onClick={onHideCompletedToggle}
aria-pressed={data.hideCompleted}
@ -34,7 +35,8 @@ export function TaskPlannerTopbar({
<i aria-hidden="true" />
</button>
<button
className={`task-planner-settings-toggle ${settingsOpen ? "active" : ""}`}
className={`task-planner-settings-toggle nds-button ${settingsOpen ? "active" : ""}`}
data-size="sm"
type="button"
onClick={onSettingsToggle}
aria-expanded={settingsOpen}
@ -48,11 +50,11 @@ export function TaskPlannerTopbar({
{editing && (
<div className="task-planner-add-stack">
<form className="inline-form task-planner-add-form" onSubmit={onAddTask}>
<input {...taskDraft.getFieldProps("title", { placeholder: textContent.titlePlaceholder || "Nouvelle tâche" })} />
<select {...taskDraft.getFieldProps("type", { "aria-label": textContent.typeLabel || "Type" })}>
<input {...taskDraft.getFieldProps("title", { className: "nds-input", "data-size": "sm", type: "text", placeholder: textContent.titlePlaceholder || "Nouvelle tâche" })} />
<select {...taskDraft.getFieldProps("type", { className: "nds-select", "data-size": "sm", "aria-label": textContent.typeLabel || "Type" })}>
{TASK_TYPES.map((taskType) => <option key={taskType} value={taskType}>{getTaskTypeLabel(taskType, textContent)}</option>)}
</select>
<button className="primary">{textContent.addButton || "Ajouter"}</button>
<button className="nds-button" data-size="sm" data-variant="primary">{textContent.addButton || "Ajouter"}</button>
</form>
</div>
)}

View file

@ -9,11 +9,11 @@ export function TextExchangeActions({ type, data, context, onImport, textContent
return (
<>
<div className="text-import-actions">
<button type="button" onClick={exchange.openImport}>
<button className="nds-button" data-size="sm" type="button" onClick={exchange.openImport}>
<Icon name="import" />
<span>{textContent.importOpenButton || "Importer du texte"}</span>
</button>
<button type="button" onClick={exchange.openExport} disabled={!exchange.canExport}>
<button className="nds-button" data-size="sm" type="button" onClick={exchange.openExport} disabled={!exchange.canExport}>
<Icon name="export" />
<span>{textContent.exportOpenButton || exportLabel || "Exporter en format texte"}</span>
</button>

View file

@ -24,14 +24,15 @@ export function TextImportModal({ title, value, placeholder, submitLabel, readOn
<section className="confirm-modal text-import-modal-dialog" role="dialog" aria-modal="true" aria-labelledby="text-import-title">
<header>
<h2 id="text-import-title">{title}</h2>
<button className="drawer-close-button" type="button" onClick={onClose} aria-label="Fermer" title="Fermer">
<button className="drawer-close-button nds-icon-button" data-size="sm" type="button" onClick={onClose} aria-label="Fermer" title="Fermer">
<Icon name="close" />
</button>
</header>
<form className="text-import-modal-form" onSubmit={onSubmit}>
<textarea
autoFocus
className={error ? "has-error" : ""}
className={`nds-textarea ${error ? "has-error" : ""}`.trim()}
data-size="sm"
readOnly={readOnly}
value={value}
onChange={(event) => onChange?.(event.target.value)}
@ -53,11 +54,11 @@ export function TextImportModal({ title, value, placeholder, submitLabel, readOn
</p>
)}
<footer>
<button type="button" onClick={onClose}>Annuler</button>
<button className="nds-button" data-size="sm" type="button" onClick={onClose}>Annuler</button>
{readOnly ? (
<button className="primary" type="button" onClick={onCopy}>{submitLabel}</button>
<button className="nds-button" data-size="sm" data-variant="primary" type="button" onClick={onCopy}>{submitLabel}</button>
) : (
<button className="primary" type="submit">{submitLabel}</button>
<button className="nds-button" data-size="sm" data-variant="primary" type="submit">{submitLabel}</button>
)}
</footer>
</form>

View file

@ -18,11 +18,11 @@ export function CountdownControls({ textContent, form, error, onChange, onSubmit
<form className="timer-control-panel timer-countdown-form" role="tabpanel" onSubmit={onSubmit} aria-describedby={error ? "timer-countdown-error" : undefined}>
<label>
<span>{textContent.countdownLabel || "Libellé"}</span>
<input value={form.label} onChange={(event) => onChange({ label: event.target.value })} placeholder={textContent.countdownLabelPlaceholder || "Boss, event, craft..."} />
<input className="nds-input" data-size="sm" type="text" value={form.label} onChange={(event) => onChange({ label: event.target.value })} placeholder={textContent.countdownLabelPlaceholder || "Boss, event, craft..."} />
</label>
<label>
<span>{textContent.countdownTypeLabel || "Type"}</span>
<select value={form.type} onChange={(event) => onChange({ type: event.target.value })}>
<select className="nds-select" data-size="sm" value={form.type} onChange={(event) => onChange({ type: event.target.value })}>
<option value="duration">{textContent.durationType || "Durée"}</option>
<option value="daily_time">{textContent.dailyTimeType || "Heure précise"}</option>
<option value="time_pattern">{textContent.timePatternType || "Pattern horaire"}</option>
@ -68,7 +68,7 @@ export function CountdownControls({ textContent, form, error, onChange, onSubmit
<span>{error}</span>
</p>
)}
<button className="primary" type="submit">{textContent.addCountdownButton || "Ajouter"}</button>
<button className="nds-button" data-size="sm" data-variant="primary" type="submit">{textContent.addCountdownButton || "Ajouter"}</button>
</form>
);
}
@ -102,7 +102,8 @@ export function CountdownList({ countdowns, nowMs, textContent, reorder, reorder
<div className={`tool-split-entry timer-entry has-single-action ${reorderEnabled ? "has-drag-handle" : ""} ${editingId !== countdown.id ? "has-inline-controls" : ""} ${(canReset || canToggleRefresh) && editingId !== countdown.id ? "has-inline-reset" : ""}`}>
{reorderEnabled && (
<button
className="timer-drag-handle"
className="timer-drag-handle nds-icon-button"
data-size="xs"
type="button"
onPointerDown={(event) => reorder.itemReorder.startDrag(event, countdown.id)}
aria-label={`${textContent.reorderTitle || "Déplacer"} ${countdown.label}`}
@ -135,7 +136,8 @@ export function CountdownList({ countdowns, nowMs, textContent, reorder, reorder
<span className="timer-inline-controls">
{canReset && (
<button
className="timer-inline-reset"
className="timer-inline-reset nds-icon-button"
data-size="xxs"
type="button"
onClick={(event) => {
event.stopPropagation();
@ -149,7 +151,8 @@ export function CountdownList({ countdowns, nowMs, textContent, reorder, reorder
)}
{canToggleRefresh && (
<button
className={`timer-inline-refresh-toggle ${countdown.autoRefresh ? "active" : ""} ${refreshBlocked ? "is-blocked" : ""}`}
className={`timer-inline-refresh-toggle nds-switch ${countdown.autoRefresh ? "active" : ""} ${refreshBlocked ? "is-blocked" : ""}`}
data-size="xs"
type="button"
onClick={(event) => {
event.stopPropagation();
@ -174,7 +177,7 @@ export function CountdownList({ countdowns, nowMs, textContent, reorder, reorder
</span>
</span>
)}
<button className="danger" type="button" onClick={() => onDelete(countdown.id)} aria-label={textContent.deleteTitle || "Supprimer"} title={textContent.deleteTitle || "Supprimer"}>
<button className="nds-icon-button" data-size="xs" data-variant="danger" type="button" onClick={() => onDelete(countdown.id)} aria-label={textContent.deleteTitle || "Supprimer"} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</div>

View file

@ -286,12 +286,13 @@ export function TimerModule({ toolboxId, moduleId, context, textExchangeOpen })
</section>
<section className={`tool-split-results timer-results-card ${data.scrollResults ? "is-scrollable" : ""}`}>
<div className="tool-split-actions">
<button className="tool-split-action-button danger" type="button" onClick={resetActiveResults} aria-label={textContent.resetTitle || "Réinitialiser"} title={textContent.resetTitle || "Réinitialiser"}>
<button className="tool-split-action-button nds-icon-button" data-size="sm" data-variant="danger" type="button" onClick={resetActiveResults} aria-label={textContent.resetTitle || "Réinitialiser"} title={textContent.resetTitle || "Réinitialiser"}>
<Icon name="rubber" />
</button>
{activeTab === "countdown" && (
<button
className={`tool-split-scroll-toggle timer-sort-toggle ${data.sortResults ? "active" : ""}`}
className={`tool-split-scroll-toggle timer-sort-toggle nds-switch ${data.sortResults ? "active" : ""}`}
data-size="md"
type="button"
onClick={() => setSortResults(!data.sortResults)}
aria-pressed={data.sortResults}
@ -303,7 +304,8 @@ export function TimerModule({ toolboxId, moduleId, context, textExchangeOpen })
</button>
)}
<button
className={`tool-split-scroll-toggle timer-scroll-toggle ${data.scrollResults ? "active" : ""}`}
className={`tool-split-scroll-toggle timer-scroll-toggle nds-switch ${data.scrollResults ? "active" : ""}`}
data-size="md"
type="button"
onClick={() => setScrollResults(!data.scrollResults)}
aria-pressed={data.scrollResults}

View file

@ -26,7 +26,8 @@ export function TimerAlertModeSwitch({ mode, blocked, textContent, onChange }) {
return (
<div
className={`timer-alert-mode-switch ${blocked ? "is-blocked" : ""}`}
className={`timer-alert-mode-switch nds-radio-button-group ${blocked ? "is-blocked" : ""}`}
data-size="xs"
role="group"
aria-label={textContent.alertModeTitle || "Mode d'alerte"}
title={blocked ? blockedTitle : undefined}
@ -63,11 +64,11 @@ export function TimePartsInput({ value, onChange, allowWildcard = false }) {
return (
<div className="time-parts-input" role="group" aria-label="Format heure minute seconde">
<input value={value.hours} onChange={(event) => updatePart("hours", event.target.value)} placeholder="hh" inputMode={allowWildcard ? "text" : "numeric"} />
<input type="text" value={value.hours} onChange={(event) => updatePart("hours", event.target.value)} placeholder="hh" inputMode={allowWildcard ? "text" : "numeric"} />
<span>:</span>
<input value={value.minutes} onChange={(event) => updatePart("minutes", event.target.value)} placeholder="mm" inputMode={allowWildcard ? "text" : "numeric"} />
<input type="text" value={value.minutes} onChange={(event) => updatePart("minutes", event.target.value)} placeholder="mm" inputMode={allowWildcard ? "text" : "numeric"} />
<span>:</span>
<input value={value.seconds} onChange={(event) => updatePart("seconds", event.target.value)} placeholder="ss" inputMode={allowWildcard ? "text" : "numeric"} />
<input type="text" value={value.seconds} onChange={(event) => updatePart("seconds", event.target.value)} placeholder="ss" inputMode={allowWildcard ? "text" : "numeric"} />
</div>
);
}
@ -87,6 +88,7 @@ export function EditableTimerLabel({ value, placeholder, ariaLabel, children, on
<input
{...inlineEdit.getInputProps({
className: "tool-split-entry-label timer-lap-label",
type: "text",
placeholder,
"aria-label": ariaLabel
})}

View file

@ -10,13 +10,13 @@ export function StopwatchControls({ textContent, elapsedMs, running, onStart, on
<span>{textContent.stopwatchTitle || "Chronomètre à étapes"}</span>
<strong><TimerValue ms={elapsedMs} compact /></strong>
<div className="timer-actions">
<button className="primary timer-action-button" type="button" onClick={running ? onPause : onStart} aria-label={running ? textContent.pauseButton || "Pause" : textContent.startButton || "Lancer"} title={running ? textContent.pauseButton || "Pause" : textContent.startButton || "Lancer"}>
<button className="timer-action-button nds-icon-button" data-size="sm" data-variant="primary" type="button" onClick={running ? onPause : onStart} aria-label={running ? textContent.pauseButton || "Pause" : textContent.startButton || "Lancer"} title={running ? textContent.pauseButton || "Pause" : textContent.startButton || "Lancer"}>
<Icon name={running ? "pause-circle" : "play-circle"} />
</button>
<button className="timer-action-button" type="button" onClick={onLap} disabled={!elapsedMs} aria-label={textContent.lapButton || "Étape"} title={textContent.lapButton || "Étape"}>
<button className="timer-action-button nds-icon-button" data-size="sm" type="button" onClick={onLap} disabled={!elapsedMs} aria-label={textContent.lapButton || "Étape"} title={textContent.lapButton || "Étape"}>
<Icon name="record-circle" />
</button>
<button className="danger timer-action-button" type="button" onClick={onReset} aria-label={textContent.resetTitle || "Réinitialiser"} title={textContent.resetTitle || "Réinitialiser"}>
<button className="timer-action-button nds-icon-button" data-size="sm" data-variant="danger" type="button" onClick={onReset} aria-label={textContent.resetTitle || "Réinitialiser"} title={textContent.resetTitle || "Réinitialiser"}>
<Icon name="stop-circle" />
</button>
</div>
@ -56,7 +56,7 @@ export function StopwatchLapList({ laps, textContent, onRename, onDelete }) {
</span>
</span>
)}
<button className="danger" type="button" onClick={() => onDelete(lap.id)} aria-label={textContent.deleteTitle || "Supprimer"} title={textContent.deleteTitle || "Supprimer"}>
<button className="nds-icon-button" data-size="xs" data-variant="danger" type="button" onClick={() => onDelete(lap.id)} aria-label={textContent.deleteTitle || "Supprimer"} title={textContent.deleteTitle || "Supprimer"}>
<Icon name="trash" />
</button>
</div>

View file

@ -188,7 +188,9 @@ function ModuleShell({ toolbox, module, context, reorder, itemGroup = "", onRena
<header>
<div>
<button
className="module-drag-handle"
className="module-drag-handle nds-icon-button"
data-size="md"
data-variant="ghost"
type="button"
onPointerDown={(event) => reorder.itemReorder.startDrag(event, module.id)}
aria-label={`Déplacer ${module.title || label}`}
@ -204,7 +206,8 @@ function ModuleShell({ toolbox, module, context, reorder, itemGroup = "", onRena
<div>
{definition.scrollable && (
<button
className={`module-scroll-button ${scrollable ? "active" : ""}`}
className={`module-scroll-button nds-switch ${scrollable ? "active" : ""}`}
data-size="lg"
onClick={() => onUpdateModule(module.id, (currentModule) => {
const nextModule = { ...currentModule };
if (scrollable) {
@ -224,7 +227,8 @@ function ModuleShell({ toolbox, module, context, reorder, itemGroup = "", onRena
)}
{hasAddPanel && (
<button
className={`module-edit-button ${editing ? "active" : ""}`}
className={`module-edit-button nds-icon-button ${editing ? "active" : ""}`}
data-size="md"
onClick={() => setEditing((value) => {
const nextEditing = !value;
if (nextEditing) setTextExchangeOpen(false);
@ -239,7 +243,8 @@ function ModuleShell({ toolbox, module, context, reorder, itemGroup = "", onRena
)}
{hasTextExchange && (
<button
className={`module-exchange-button ${textExchangeOpen ? "active" : ""}`}
className={`module-exchange-button nds-icon-button ${textExchangeOpen ? "active" : ""}`}
data-size="md"
onClick={() => setTextExchangeOpen((value) => {
const nextOpen = !value;
if (nextOpen) setEditing(false);
@ -252,7 +257,7 @@ function ModuleShell({ toolbox, module, context, reorder, itemGroup = "", onRena
<Icon name="import" />
</button>
)}
<button className="module-delete-button danger" onClick={() => onDelete(module.id)} aria-label={`Retirer ${module.title || label}`} data-toolbox-tutorial-target={module.type === "links" ? "deleteTool" : undefined} title="Retirer">
<button className="module-delete-button nds-icon-button" data-size="md" data-variant="danger" onClick={() => onDelete(module.id)} aria-label={`Retirer ${module.title || label}`} data-toolbox-tutorial-target={module.type === "links" ? "deleteTool" : undefined} title="Retirer">
<span className="ui-icon ui-icon-trash" aria-hidden="true" />
</button>
</div>

View file

@ -6,7 +6,7 @@ export const HOUR_HALF_SLOT_COUNT = 2;
export const WEEK_SLOT_COUNT = DAY_HOUR_COUNT * HOUR_HALF_SLOT_COUNT;
export const WEEK_HOUR_ROW_HEIGHT = 27;
export const WEEK_HIDDEN_ROW_HEIGHT = 20;
export const WEEK_COMPACT_EVENT_HEIGHT = WEEK_HOUR_ROW_HEIGHT / HOUR_HALF_SLOT_COUNT;
export const WEEK_COMPACT_EVENT_HEIGHT = 22;
export const WEEKDAY_LABELS = ["lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"];
function occurrencesOverlap(first, second) {

View file

@ -6,6 +6,15 @@ export const CALENDAR_MONTH_REPEAT_MODES = ["none", "weekly", "monthly"];
export const CALENDAR_ALERT_MODES = ["off", "visible", "site"];
export const CALENDAR_ALERT_OFFSETS = [10, 5, 0];
export const CALENDAR_COLORS = ["violet", "gold", "cyan", "pink", "green", "gray"];
export const CALENDAR_COLOR_VALUES = {
violet: "#8b5cf6",
gold: "#f6c453",
cyan: "#22d3ee",
pink: "#d946ef",
green: "#22c55e",
gray: "#7d879f"
};
export const CALENDAR_DARK_TEXT_COLORS = new Set(["gold", "cyan", "green"]);
export const WEEK_SLOT_MINUTES = 30;
export const WEEK_TIME_INPUT_STEP_MINUTES = 5;
export const WEEK_SLOT_COUNT = 48;
@ -13,6 +22,14 @@ export const DAY_MS = 24 * 60 * 60 * 1000;
export const MINUTE_MS = 60 * 1000;
export const WEEK_DAY_COUNT = 7;
export function getCalendarColorValue(color) {
return CALENDAR_COLOR_VALUES[color] || CALENDAR_COLOR_VALUES.violet;
}
export function getCalendarEventSurfaceVariant(color) {
return CALENDAR_DARK_TEXT_COLORS.has(color) ? "dark" : "default";
}
export function pad2(value) {
return String(value).padStart(2, "0");
}