Extend NDS patterns and migrate shared UI surfaces
All checks were successful
Deploy Sokko G / deploy (push) Successful in 17s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 17s
This commit is contained in:
parent
fef7735a1d
commit
9aa98c892d
41 changed files with 1035 additions and 405 deletions
|
|
@ -63,21 +63,22 @@ Animations :
|
||||||
- `--duration-normal: 180ms`
|
- `--duration-normal: 180ms`
|
||||||
- `--ease-standard: cubic-bezier(0.2, 0.8, 0.2, 1)`
|
- `--ease-standard: cubic-bezier(0.2, 0.8, 0.2, 1)`
|
||||||
|
|
||||||
## 3. API Des Contrôles NDS
|
## 3. API Publique NDS
|
||||||
|
|
||||||
Le Nebula Design System expose ses contrôles publics avec une classe de famille et des attributs `data-*`.
|
Le Nebula Design System expose ses primitives publiques avec une classe de famille et des attributs `data-*`.
|
||||||
|
|
||||||
Règles :
|
Règles :
|
||||||
|
|
||||||
- la classe décrit uniquement la famille : `nds-button`, `nds-icon-button`, `nds-input`, `nds-select`, `nds-select-trigger`, `nds-time`, `nds-textarea`, `nds-inline-input`, `nds-switch`, `nds-radio-button-group`, `nds-checkbox`, `nds-quantity-stepper`, `nds-list-row`, `nds-large-icon-picker-trigger`, `nds-event-surface`, `nds-scrollbox` ;
|
- la classe décrit uniquement la famille : `nds-button`, `nds-icon-button`, `nds-input`, `nds-select`, `nds-select-trigger`, `nds-time`, `nds-textarea`, `nds-inline-input`, `nds-switch`, `nds-radio-button-group`, `nds-checkbox`, `nds-quantity-stepper`, `nds-list-row`, `nds-large-icon-picker-trigger`, `nds-event-surface`, `nds-scrollbox` ;
|
||||||
|
- les patterns transverses utilisent aussi une classe de famille : `nds-panel`, `nds-card`, `nds-card-cover`, `nds-card-body`, `nds-card-actions`, `nds-tool-panel`, `nds-toolbar`, `nds-section-heading`, `nds-page-hero`, `nds-badge`, `nds-chip`, `nds-empty-state`, `nds-inline-notice`, `nds-progress`, `nds-filter-panel`, `nds-reorder-indicator`, `nds-modal`, `nds-modal-backdrop`, `nds-modal-footer`, `nds-drawer-panel`, `nds-drawer-resize-handle`, `nds-popover`, `nds-toast-stack`, `nds-toast` ;
|
||||||
- les variations passent par attributs : `data-size`, `data-variant`, `data-edge`, `data-border`, `data-axis` ;
|
- les variations passent par attributs : `data-size`, `data-variant`, `data-edge`, `data-border`, `data-axis` ;
|
||||||
- la page `/design-system` affiche aussi `data-state` comme axe de comparaison visuelle des états ;
|
- la page `/design-system` affiche aussi `data-state` comme axe de comparaison visuelle des états ;
|
||||||
- `data-size` accepte seulement les tailles prévues par la famille (`xs`, `sm`, `md`, `lg`, `xxs`, `micro` selon le contrôle) ; les checkboxes utilisent `md` 20 px, `sm` 18 px et `xs` 14 px ;
|
- `data-size` accepte seulement les tailles prévues par la famille (`xs`, `sm`, `md`, `lg`, `xxs`, `micro` selon le contrôle) ; les checkboxes utilisent `md` 20 px, `sm` 18 px et `xs` 14 px ;
|
||||||
- `data-variant` couvre les intentions visuelles partagées : `primary`, `secondary`, `ghost`, `dashed`, `danger` et les variantes spécialisées documentées dans `/design-system`, dont `panel-toggle` pour les boutons icones accolés à un panneau et `icon-picker` pour les triggers de sélection compacts à icône ;
|
- `data-variant` couvre les intentions visuelles partagées : `primary`, `secondary`, `ghost`, `dashed`, `danger` et les variantes spécialisées documentées dans `/design-system`, dont `panel-toggle` pour les boutons icones accolés à un panneau, `icon-picker` pour les triggers de sélection compacts à icône, `interactive|tool|filter|modal` pour les surfaces, et `neutral|gold|success|warning|danger` pour les badges, notices et progress ;
|
||||||
- `data-edge="left|middle|right|top|center|bottom|top-left|top-right|bottom-right|bottom-left"` décrit la position d'un bouton dans un groupe ou contre un bord, et ajuste uniquement les coins ;
|
- `data-edge="left|middle|right|top|center|bottom|top-left|top-right|bottom-right|bottom-left"` décrit la position d'un bouton dans un groupe ou contre un bord, et ajuste uniquement les coins ;
|
||||||
- `data-border="joined"` masque les bordures de jonction déduites depuis `data-edge` ;
|
- `data-border="joined"` masque les bordures de jonction déduites depuis `data-edge` ;
|
||||||
- `data-border="none"` masque toutes les bordures sans changer la taille, le fond, le hover ou l'état actif ;
|
- `data-border="none"` masque toutes les bordures sans changer la taille, le fond, le hover ou l'état actif ;
|
||||||
- `data-axis="horizontal|vertical"` reste réservé aux composants dont l'axe est métier, comme `nds-scrollbox` ;
|
- `data-axis="horizontal|vertical"` reste réservé aux composants dont l'axe est métier, comme `nds-scrollbox` et `nds-reorder-indicator` ;
|
||||||
- les boutons icônes de réorganisation utilisent `nds-icon-button` avec `data-variant="secondary"` et une classe métier contenant `drag-handle` ; le curseur `grab/grabbing` est alors géré par le NDS ;
|
- les boutons icônes de réorganisation utilisent `nds-icon-button` avec `data-variant="secondary"` et une classe métier contenant `drag-handle` ; le curseur `grab/grabbing` est alors géré par le NDS ;
|
||||||
- les états fonctionnels restent portés par le HTML et l'application : `disabled`, `aria-pressed="true"`, `.active`, `.is-active`.
|
- les états fonctionnels restent portés par le HTML et l'application : `disabled`, `aria-pressed="true"`, `.active`, `.is-active`.
|
||||||
|
|
||||||
|
|
@ -89,10 +90,29 @@ Exemples :
|
||||||
<button class="nds-icon-button" data-variant="panel-toggle" data-edge="right" data-border="joined" aria-pressed="true" aria-label="Pages"></button>
|
<button class="nds-icon-button" data-variant="panel-toggle" data-edge="right" data-border="joined" aria-pressed="true" aria-label="Pages"></button>
|
||||||
<select class="nds-select" data-size="sm"></select>
|
<select class="nds-select" data-size="sm"></select>
|
||||||
<button class="nds-select-trigger" data-size="sm" aria-pressed="false"></button>
|
<button class="nds-select-trigger" data-size="sm" aria-pressed="false"></button>
|
||||||
|
<article class="nds-card" data-variant="interactive"><div class="nds-card-cover"></div><div class="nds-card-body"></div></article>
|
||||||
|
<section class="nds-panel"></section>
|
||||||
|
<span class="nds-badge" data-variant="gold">Rare</span>
|
||||||
|
<p class="nds-inline-notice" data-variant="danger">Erreur compacte</p>
|
||||||
```
|
```
|
||||||
|
|
||||||
Les anciennes classes de variation `nds-*--*` ne doivent plus etre ajoutees. Les anciennes classes globales `.button`, `.primary`, `.danger`, `.ghost` et `.import-button` ne sont plus des APIs visuelles publiques : utiliser `nds-button` / `nds-icon-button` avec `data-variant`. Dans les outils, les classes métier peuvent rester pour le layout, la grille, les largeurs, le positionnement et les états fonctionnels. Le style visuel d'un contrôle générique doit venir de `website/src/styles/design-system/_controls.scss` ou de l'API publique `website/src/styles/design-system/_attributes.scss`.
|
Les anciennes classes de variation `nds-*--*` ne doivent plus etre ajoutees. Les anciennes classes globales `.button`, `.primary`, `.danger`, `.ghost` et `.import-button` ne sont plus des APIs visuelles publiques : utiliser `nds-button` / `nds-icon-button` avec `data-variant`. Dans les outils, les classes métier peuvent rester pour le layout, la grille, les largeurs, le positionnement et les états fonctionnels. Le style visuel d'un contrôle générique doit venir de `website/src/styles/design-system/_controls.scss` ou de l'API publique `website/src/styles/design-system/_attributes.scss`.
|
||||||
|
|
||||||
|
### Surfaces Et Patterns
|
||||||
|
|
||||||
|
Les surfaces NDS couvrent les patterns transverses, pas les composants metier complets.
|
||||||
|
|
||||||
|
- `nds-panel` : panneau de lecture non repete, sans hover par defaut.
|
||||||
|
- `nds-card` : element repete ou ouvrable ; utiliser `nds-card-cover`, `nds-card-body` et `nds-card-actions` quand la structure cover/contenu/actions est necessaire.
|
||||||
|
- `nds-tool-panel` : surface de travail d'outil, plus discrete qu'une card, avec accent dore bas/droite.
|
||||||
|
- `nds-filter-panel` : panneau sticky ou lateral de filtres ; les options utilisent `nds-chip`.
|
||||||
|
- `nds-empty-state` : etat vide court, avec action optionnelle.
|
||||||
|
- `nds-inline-notice` : feedback compact inline, avec `data-variant="info|success|warning|danger"`.
|
||||||
|
- `nds-progress` : rail de progression ; le remplissage est le premier `span` enfant ou `.nds-progress-bar`.
|
||||||
|
- `nds-modal`, `nds-drawer-panel`, `nds-popover`, `nds-toast` : surfaces d'overlay. Le comportement React reste gere par les composants existants.
|
||||||
|
|
||||||
|
Les classes metier existantes peuvent coexister avec ces classes pendant la migration. Quand une classe metier reste necessaire, elle doit porter le layout, la grille ou le positionnement, pas recreer les fonds, bordures, hovers et focus partages.
|
||||||
|
|
||||||
## 4. Fond Nebula
|
## 4. Fond Nebula
|
||||||
|
|
||||||
Le fond global est defini dans `website/src/styles/_nebula.scss`.
|
Le fond global est defini dans `website/src/styles/_nebula.scss`.
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,28 @@ test("style entrypoint and theme layers are wired", async () => {
|
||||||
const styleShell = await readFile("website/src/styles/_shell.scss", "utf8");
|
const styleShell = await readFile("website/src/styles/_shell.scss", "utf8");
|
||||||
const styleCards = await readFile("website/src/styles/_cards.scss", "utf8");
|
const styleCards = await readFile("website/src/styles/_cards.scss", "utf8");
|
||||||
const styleSurfaces = await readFile("website/src/styles/design-system/_surfaces.scss", "utf8");
|
const styleSurfaces = await readFile("website/src/styles/design-system/_surfaces.scss", "utf8");
|
||||||
|
const styleControls = await readFile("website/src/styles/design-system/_controls.scss", "utf8");
|
||||||
|
const styleAttributes = await readFile("website/src/styles/design-system/_attributes.scss", "utf8");
|
||||||
|
const styleControlsPatterns = await readFile("website/src/styles/design-system/controls/_patterns.scss", "utf8");
|
||||||
|
const styleAttributesPatterns = await readFile("website/src/styles/design-system/attributes/_patterns.scss", "utf8");
|
||||||
const styleControlsButtons = await readFile("website/src/styles/design-system/controls/_buttons.scss", "utf8");
|
const styleControlsButtons = await readFile("website/src/styles/design-system/controls/_buttons.scss", "utf8");
|
||||||
const styleAttributesButtons = await readFile("website/src/styles/design-system/attributes/_buttons.scss", "utf8");
|
const styleAttributesButtons = await readFile("website/src/styles/design-system/attributes/_buttons.scss", "utf8");
|
||||||
const goalTreeBranch = await readFile("website/src/features/toolboxes/modules/GoalTreeBranch.jsx", "utf8");
|
const goalTreeBranch = await readFile("website/src/features/toolboxes/modules/GoalTreeBranch.jsx", "utf8");
|
||||||
|
const ndsPatternConsumers = await readJoinedStyles([
|
||||||
|
"website/src/pages/HomePage.jsx",
|
||||||
|
"website/src/pages/LibraryPage.jsx",
|
||||||
|
"website/src/pages/AboutPage.jsx",
|
||||||
|
"website/src/features/games/GamesPage.jsx",
|
||||||
|
"website/src/features/games/GameFiltersPanel.jsx",
|
||||||
|
"website/src/features/games/diablo4/Diablo4AffixCard.jsx",
|
||||||
|
"website/src/features/toolboxes/ToolboxCard.jsx",
|
||||||
|
"website/src/features/toolboxes/ToolboxPages.jsx",
|
||||||
|
"website/src/components/AppOverlays.jsx",
|
||||||
|
"website/src/components/StorageQuota.jsx",
|
||||||
|
"website/src/components/ToolboxModals.jsx",
|
||||||
|
"website/src/features/toolboxes/modules/AddToolControls.jsx",
|
||||||
|
"website/src/features/toolboxes/modules/TextImportModal.jsx"
|
||||||
|
]);
|
||||||
const styleHome = await readJoinedStyles([
|
const styleHome = await readJoinedStyles([
|
||||||
"website/src/styles/_home.scss",
|
"website/src/styles/_home.scss",
|
||||||
"website/src/styles/home/_shared.scss",
|
"website/src/styles/home/_shared.scss",
|
||||||
|
|
@ -152,7 +171,7 @@ test("style entrypoint and theme layers are wired", async () => {
|
||||||
assert.match(styleHome, /\.dialogue-line i/);
|
assert.match(styleHome, /\.dialogue-line i/);
|
||||||
assert.match(styleHome, /font-style: italic/);
|
assert.match(styleHome, /font-style: italic/);
|
||||||
assert.match(styleHome, /\.about-page/);
|
assert.match(styleHome, /\.about-page/);
|
||||||
assert.match(styleHome, /\.nebula-panel/);
|
assert.doesNotMatch(styleHome, /\.nebula-panel\s*\{/);
|
||||||
assert.match(styleHome, /\.home-onboarding-layout/);
|
assert.match(styleHome, /\.home-onboarding-layout/);
|
||||||
assert.match(styleHome, /\.home-onboarding-slider figcaption/);
|
assert.match(styleHome, /\.home-onboarding-slider figcaption/);
|
||||||
assert.match(styleHome, /\.home-onboarding-dots/);
|
assert.match(styleHome, /\.home-onboarding-dots/);
|
||||||
|
|
@ -223,8 +242,38 @@ test("style entrypoint and theme layers are wired", async () => {
|
||||||
assert.match(styleSurfaces, /@mixin card-surface/);
|
assert.match(styleSurfaces, /@mixin card-surface/);
|
||||||
assert.match(styleSurfaces, /@mixin card-surface-hover/);
|
assert.match(styleSurfaces, /@mixin card-surface-hover/);
|
||||||
assert.match(styleSurfaces, /@mixin card-cover-divider/);
|
assert.match(styleSurfaces, /@mixin card-cover-divider/);
|
||||||
assert.match(styleCards, /@use "design-system\/surfaces"/);
|
assert.match(styleControls, /@forward "controls\/patterns"/);
|
||||||
assert.match(styleCards, /surfaces\.card-surface/);
|
assert.match(styleAttributes, /@use "attributes\/patterns"/);
|
||||||
|
assert.match(styleControlsPatterns, /@mixin panel-surface/);
|
||||||
|
assert.match(styleControlsPatterns, /@mixin toast-pattern/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-panel/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-card/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-tool-panel/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-badge/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-chip/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-empty-state/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-inline-notice/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-progress/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-filter-panel/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-modal/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-drawer-panel/);
|
||||||
|
assert.match(styleAttributesPatterns, /\.nds-toast/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-page-hero/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-panel/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-card/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-card-cover/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-tool-panel/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-filter-panel/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-chip/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-badge/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-empty-state/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-inline-notice/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-progress/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-modal/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-drawer-panel/);
|
||||||
|
assert.match(ndsPatternConsumers, /nds-toast/);
|
||||||
|
assert.doesNotMatch(styleCards, /@use "design-system\/surfaces"/);
|
||||||
|
assert.doesNotMatch(styleCards, /controls\.card-pattern/);
|
||||||
assert.match(styleGames, /@use "\.\.\/design-system\/surfaces"/);
|
assert.match(styleGames, /@use "\.\.\/design-system\/surfaces"/);
|
||||||
assert.match(styleGames, /surfaces\.card-surface/);
|
assert.match(styleGames, /surfaces\.card-surface/);
|
||||||
assert.match(styleGames, /surfaces\.card-cover-divider/);
|
assert.match(styleGames, /surfaces\.card-cover-divider/);
|
||||||
|
|
@ -234,7 +283,7 @@ test("style entrypoint and theme layers are wired", async () => {
|
||||||
assert.match(styleMhwilds, /surfaces\.card-surface/);
|
assert.match(styleMhwilds, /surfaces\.card-surface/);
|
||||||
assert.match(styleMhwilds, /surfaces\.card-cover-divider/);
|
assert.match(styleMhwilds, /surfaces\.card-cover-divider/);
|
||||||
assert.doesNotMatch(styleMhwilds, /\.game-(home-card|layout|grid)/);
|
assert.doesNotMatch(styleMhwilds, /\.game-(home-card|layout|grid)/);
|
||||||
assert.match(styleCards, /compact-data-card/);
|
assert.doesNotMatch(styleCards, /\.compact-data-card\s*\{/);
|
||||||
assert.doesNotMatch(
|
assert.doesNotMatch(
|
||||||
[
|
[
|
||||||
goalTreeBranch,
|
goalTreeBranch,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ function scrollToTopAfterNavigation() {
|
||||||
|
|
||||||
export function InlineNotice({ className = "", iconName = "link", message, tone = "warning" }) {
|
export function InlineNotice({ className = "", iconName = "link", message, tone = "warning" }) {
|
||||||
if (!message) return null;
|
if (!message) return null;
|
||||||
const classes = ["inline-notice", `is-${tone}`, className].filter(Boolean).join(" ");
|
const classes = ["inline-notice", "nds-inline-notice", `is-${tone}`, className].filter(Boolean).join(" ");
|
||||||
return (
|
return (
|
||||||
<span className={classes} role="status">
|
<span className={classes} role="status">
|
||||||
<Icon name={iconName} />
|
<Icon name={iconName} />
|
||||||
|
|
@ -72,7 +72,7 @@ export function AppOverlays({
|
||||||
{storageError && <ConfirmModal title="Quota local atteint" message={storageError} confirmLabel="Compris" cancelLabel="Fermer" danger onClose={() => setStorageError("")} />}
|
{storageError && <ConfirmModal title="Quota local atteint" message={storageError} confirmLabel="Compris" cancelLabel="Fermer" danger onClose={() => setStorageError("")} />}
|
||||||
{image && <ImageViewer image={image} onClose={() => setImage(null)} createMarkerId={() => uid("marker")} />}
|
{image && <ImageViewer image={image} onClose={() => setImage(null)} createMarkerId={() => uid("marker")} />}
|
||||||
{notifications.length > 0 && (
|
{notifications.length > 0 && (
|
||||||
<div className={`notification-toast-stack is-${toastPosition === "left" ? "left" : "right"}`} aria-live="polite" aria-relevant="additions">
|
<div className={`notification-toast-stack nds-toast-stack is-${toastPosition === "left" ? "left" : "right"}`} data-position={toastPosition === "left" ? "left" : "right"} aria-live="polite" aria-relevant="additions">
|
||||||
{notifications.map((notification) => (
|
{notifications.map((notification) => (
|
||||||
<NotificationToast
|
<NotificationToast
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { Icon } from "./Icon.jsx";
|
import { Icon } from "./Icon.jsx";
|
||||||
import { ImportButton } from "./ImportButton.jsx";
|
import { ImportButton } from "./ImportButton.jsx";
|
||||||
|
|
||||||
export function Shell({ route, content, games, toolboxes, links, actions, children }) {
|
export function Shell({ route, content, games, toolboxes, links, drawerGameId = "", actions, children }) {
|
||||||
const gameId = route.split("/")[1] === "games" ? route.split("/")[2] || "" : "";
|
const gameId = route.split("/")[1] === "games" ? route.split("/")[2] || "" : "";
|
||||||
const game = games.find((item) => item.id === gameId);
|
const game = games.find((item) => item.id === gameId);
|
||||||
const linkedToolbox = game ? toolboxes.find((item) => item.id === links[game.id]) : null;
|
const linkedToolbox = game ? toolboxes.find((item) => item.id === links[game.id]) : null;
|
||||||
|
|
@ -45,7 +45,15 @@ export function Shell({ route, content, games, toolboxes, links, actions, childr
|
||||||
<div><strong>{topbarLabel}</strong></div>
|
<div><strong>{topbarLabel}</strong></div>
|
||||||
{game && (
|
{game && (
|
||||||
<div className="topbar-actions">
|
<div className="topbar-actions">
|
||||||
<button className="drawer-button toolbox-icon-button nds-icon-button" data-size="sm" data-variant="primary" onClick={() => actions.setDrawerGameId(game.id)} aria-label={toolboxLabel} title={toolboxLabel}>
|
<button
|
||||||
|
className={`drawer-button toolbox-icon-button nds-icon-button ${drawerGameId === game.id ? "active" : ""}`}
|
||||||
|
data-size="sm"
|
||||||
|
data-variant="primary"
|
||||||
|
onClick={() => actions.setDrawerGameId(drawerGameId === game.id ? "" : game.id)}
|
||||||
|
aria-label={toolboxLabel}
|
||||||
|
aria-pressed={drawerGameId === game.id}
|
||||||
|
title={toolboxLabel}
|
||||||
|
>
|
||||||
<img src="/static/icons/toolbox.svg" alt="" aria-hidden="true" />
|
<img src="/static/icons/toolbox.svg" alt="" aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export function StorageQuota({ usage }) {
|
||||||
return (
|
return (
|
||||||
<section className={`storage-quota storage-quota-${state}`} aria-label="Budget recommandé de stockage local" title={`${browserQuota}. Le budget affiché est une limite de confort pour préserver les performances.`}>
|
<section className={`storage-quota storage-quota-${state}`} aria-label="Budget recommandé de stockage local" title={`${browserQuota}. Le budget affiché est une limite de confort pour préserver les performances.`}>
|
||||||
<div><span>Stockage local recommandé</span><strong>{formatBytes(safeUsage.used)} / {formatBytes(APP_STORAGE_SOFT_LIMIT_BYTES)}</strong></div>
|
<div><span>Stockage local recommandé</span><strong>{formatBytes(safeUsage.used)} / {formatBytes(APP_STORAGE_SOFT_LIMIT_BYTES)}</strong></div>
|
||||||
<div className="storage-quota-track" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow={percent} aria-label="Budget recommandé utilisé"><span style={{ width: `${percent}%` }} /></div>
|
<div className="storage-quota-track nds-progress" data-variant={state === "ok" ? undefined : state} role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow={percent} aria-label="Budget recommandé utilisé"><span className="nds-progress-bar" style={{ width: `${percent}%` }} /></div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ export function ConfirmModal({ title, message, confirmLabel = "Confirmer", cance
|
||||||
useModalScrollLock();
|
useModalScrollLock();
|
||||||
return (
|
return (
|
||||||
<div className="confirm-modal-root">
|
<div className="confirm-modal-root">
|
||||||
<div className="confirm-backdrop" onClick={() => onClose(false)} />
|
<div className="confirm-backdrop nds-modal-backdrop" onClick={() => onClose(false)} />
|
||||||
<section className="confirm-modal" role="dialog" aria-modal="true" aria-labelledby="confirm-title" aria-describedby="confirm-message">
|
<section className="confirm-modal nds-modal" role="dialog" aria-modal="true" aria-labelledby="confirm-title" aria-describedby="confirm-message">
|
||||||
<header><h2 id="confirm-title">{title}</h2></header>
|
<header><h2 id="confirm-title">{title}</h2></header>
|
||||||
<p id="confirm-message">{message}</p>
|
<p id="confirm-message">{message}</p>
|
||||||
<footer>
|
<footer className="nds-modal-footer">
|
||||||
<button className="nds-button" data-size="sm" onClick={() => onClose(false)}>{cancelLabel}</button>
|
<button className="nds-button" data-size="sm" onClick={() => onClose(false)}>{cancelLabel}</button>
|
||||||
<button className="confirm-danger nds-button" data-size="sm" data-variant={danger ? "danger" : "primary"} onClick={() => onClose(true)}>{confirmLabel}</button>
|
<button className="confirm-danger nds-button" data-size="sm" data-variant={danger ? "danger" : "primary"} onClick={() => onClose(true)}>{confirmLabel}</button>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
@ -31,7 +31,7 @@ export function NotificationToast({ id, message, durationMs = 120000, onClose })
|
||||||
}, [durationMs, id, onClose]);
|
}, [durationMs, id, onClose]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="notification-toast" role="status">
|
<div className="notification-toast nds-toast" role="status">
|
||||||
<span>{message}</span>
|
<span>{message}</span>
|
||||||
<button className="nds-icon-button" data-size="xs" type="button" onClick={() => onClose(id)} aria-label="Fermer l'alerte" title="Fermer">
|
<button className="nds-icon-button" data-size="xs" type="button" onClick={() => onClose(id)} aria-label="Fermer l'alerte" title="Fermer">
|
||||||
<Icon name="close" />
|
<Icon name="close" />
|
||||||
|
|
@ -45,15 +45,15 @@ export function CreateToolboxModal({ gameId, initialName = "", onClose }) {
|
||||||
const [name, setName] = useState(initialName);
|
const [name, setName] = useState(initialName);
|
||||||
return (
|
return (
|
||||||
<div className="confirm-modal-root">
|
<div className="confirm-modal-root">
|
||||||
<div className="confirm-backdrop" onClick={() => onClose("")} />
|
<div className="confirm-backdrop nds-modal-backdrop" onClick={() => onClose("")} />
|
||||||
<section className="confirm-modal toolbox-create-modal" role="dialog" aria-modal="true" aria-labelledby="toolbox-create-title">
|
<section className="confirm-modal nds-modal toolbox-create-modal" role="dialog" aria-modal="true" aria-labelledby="toolbox-create-title">
|
||||||
<header><h2 id="toolbox-create-title">{gameId ? "Créer et associer une toolbox" : "Nouvelle toolbox"}</h2></header>
|
<header><h2 id="toolbox-create-title">{gameId ? "Créer et associer une toolbox" : "Nouvelle toolbox"}</h2></header>
|
||||||
<form onSubmit={(event) => {
|
<form onSubmit={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (name.trim()) onClose(name.trim());
|
if (name.trim()) onClose(name.trim());
|
||||||
}}>
|
}}>
|
||||||
<label className="field"><span>Nom</span><input className="nds-input" data-size="sm" name="name" type="text" autoComplete="off" placeholder="Nom de la toolbox" required value={name} onChange={(event) => setName(event.target.value)} autoFocus /></label>
|
<label className="field"><span>Nom</span><input className="nds-input" data-size="sm" name="name" type="text" autoComplete="off" placeholder="Nom de la toolbox" required value={name} onChange={(event) => setName(event.target.value)} autoFocus /></label>
|
||||||
<footer><button className="nds-button" data-size="sm" type="button" onClick={() => onClose("")}>Annuler</button><button className="nds-button" data-size="sm" data-variant="primary" type="submit">Créer</button></footer>
|
<footer className="nds-modal-footer"><button className="nds-button" data-size="sm" type="button" onClick={() => onClose("")}>Annuler</button><button className="nds-button" data-size="sm" data-variant="primary" type="submit">Créer</button></footer>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -65,15 +65,15 @@ export function LinkToolboxModal({ toolboxes, selectedId, onClose }) {
|
||||||
const [toolboxId, setToolboxId] = useState(selectedId);
|
const [toolboxId, setToolboxId] = useState(selectedId);
|
||||||
return (
|
return (
|
||||||
<div className="confirm-modal-root">
|
<div className="confirm-modal-root">
|
||||||
<div className="confirm-backdrop" onClick={() => onClose(null)} />
|
<div className="confirm-backdrop nds-modal-backdrop" onClick={() => onClose(null)} />
|
||||||
<section className="confirm-modal toolbox-link-modal" role="dialog" aria-modal="true" aria-labelledby="toolbox-link-title">
|
<section className="confirm-modal nds-modal toolbox-link-modal" role="dialog" aria-modal="true" aria-labelledby="toolbox-link-title">
|
||||||
<header><h2 id="toolbox-link-title">Lier une toolbox</h2></header>
|
<header><h2 id="toolbox-link-title">Lier une toolbox</h2></header>
|
||||||
<form onSubmit={(event) => {
|
<form onSubmit={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
onClose(toolboxId);
|
onClose(toolboxId);
|
||||||
}}>
|
}}>
|
||||||
<label className="field"><span>Toolbox associée</span><select className="nds-select" data-size="sm" value={toolboxId} onChange={(event) => setToolboxId(event.target.value)} autoFocus><option value="">Aucune</option>{toolboxes.map((toolbox) => <option key={toolbox.id} value={toolbox.id}>{toolbox.name}</option>)}</select></label>
|
<label className="field"><span>Toolbox associée</span><select className="nds-select" data-size="sm" value={toolboxId} onChange={(event) => setToolboxId(event.target.value)} autoFocus><option value="">Aucune</option>{toolboxes.map((toolbox) => <option key={toolbox.id} value={toolbox.id}>{toolbox.name}</option>)}</select></label>
|
||||||
<footer><button className="nds-button" data-size="sm" type="button" onClick={() => onClose(null)}>Annuler</button><button className="nds-button" data-size="sm" data-variant="primary" type="submit">Valider</button></footer>
|
<footer className="nds-modal-footer"><button className="nds-button" data-size="sm" type="button" onClick={() => onClose(null)}>Annuler</button><button className="nds-button" data-size="sm" data-variant="primary" type="submit">Valider</button></footer>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export function GameFiltersPanel({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="filter-panel">
|
<div className="filter-panel nds-filter-panel">
|
||||||
<div className="filter-panel-head">
|
<div className="filter-panel-head">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Filtres</p>
|
<p className="eyebrow">Filtres</p>
|
||||||
|
|
@ -63,7 +63,7 @@ export function GameFiltersPanel({
|
||||||
{options.map((option) => {
|
{options.map((option) => {
|
||||||
const optionId = getOptionId(option);
|
const optionId = getOptionId(option);
|
||||||
return (
|
return (
|
||||||
<label className={`filter-chip ${getOptionClassName(option)} ${selected.has(optionId) ? "active" : ""}`} key={optionId}>
|
<label className={`filter-chip nds-chip ${getOptionClassName(option)} ${selected.has(optionId) ? "active" : ""}`} key={optionId}>
|
||||||
<input
|
<input
|
||||||
className="nds-checkbox"
|
className="nds-checkbox"
|
||||||
data-size="sm"
|
data-size="sm"
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ export function GameListsPage({ game, lists, selectedListId, onSelectList, linke
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<section className="empty">
|
<section className="empty nds-empty-state">
|
||||||
<h2>Aucune liste</h2>
|
<h2>Aucune liste</h2>
|
||||||
<p>Aucune liste n’est encore disponible pour ce jeu.</p>
|
<p>Aucune liste n’est encore disponible pour ce jeu.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ export function GamesPage({ siteContent, games, gamesError }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className="page-hero">
|
<section className="page-hero nds-page-hero">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">{content.eyebrow}</p>
|
<p className="eyebrow">{content.eyebrow}</p>
|
||||||
<h1>{content.title}</h1>
|
<h1>{content.title}</h1>
|
||||||
|
|
@ -13,9 +13,9 @@ export function GamesPage({ siteContent, games, gamesError }) {
|
||||||
</section>
|
</section>
|
||||||
<section className="game-list">
|
<section className="game-list">
|
||||||
{games.length ? games.map((game) => (
|
{games.length ? games.map((game) => (
|
||||||
<article className="card game-card" key={game.id}>
|
<article className="card nds-card game-card" data-variant="interactive" key={game.id}>
|
||||||
<a
|
<a
|
||||||
className="card-cover game-card-cover game-card-cover-link"
|
className="card-cover nds-card-cover game-card-cover game-card-cover-link"
|
||||||
href={`#/games/${game.id}`}
|
href={`#/games/${game.id}`}
|
||||||
aria-label={`Ouvrir ${game.title}`}
|
aria-label={`Ouvrir ${game.title}`}
|
||||||
title={`Ouvrir ${game.title}`}
|
title={`Ouvrir ${game.title}`}
|
||||||
|
|
@ -27,17 +27,17 @@ export function GamesPage({ siteContent, games, gamesError }) {
|
||||||
<span className="game-card-placeholder" aria-hidden="true">{game.title}</span>
|
<span className="game-card-placeholder" aria-hidden="true">{game.title}</span>
|
||||||
)}
|
)}
|
||||||
</a>
|
</a>
|
||||||
<div className="card-body">
|
<div className="card-body nds-card-body">
|
||||||
<p className="eyebrow">{game.eyebrow || "Guide de jeu"}</p>
|
<p className="eyebrow">{game.eyebrow || "Guide de jeu"}</p>
|
||||||
<h2>{game.title}</h2>
|
<h2>{game.title}</h2>
|
||||||
<p>{game.summary}</p>
|
<p>{game.summary}</p>
|
||||||
<div className="card-actions">
|
<div className="card-actions nds-card-actions nds-toolbar">
|
||||||
<a className="nds-button" data-size="sm" data-variant="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>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
)) : (
|
)) : (
|
||||||
<div className="empty">
|
<div className="empty nds-empty-state">
|
||||||
<h2>{content.emptyTitle}</h2>
|
<h2>{content.emptyTitle}</h2>
|
||||||
<p>{gamesError || content.emptyText}</p>
|
<p>{gamesError || content.emptyText}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@ import { getCategoryIconStyle, getCategoryLabel } from "./utils.js";
|
||||||
|
|
||||||
export function Diablo4AffixCard({ affix, categoryMap }) {
|
export function Diablo4AffixCard({ affix, categoryMap }) {
|
||||||
return (
|
return (
|
||||||
<article className="compact-data-card diablo4-affix-card">
|
<article className="compact-data-card nds-card diablo4-affix-card" data-variant="interactive">
|
||||||
<div className="diablo4-affix-card-body">
|
<div className="diablo4-affix-card-body">
|
||||||
<h2>{affix.label}</h2>
|
<h2>{affix.label}</h2>
|
||||||
<div className="diablo4-category-list" aria-label="Catégories">
|
<div className="diablo4-category-list" aria-label="Catégories">
|
||||||
{(affix.categories || []).map((categoryId) => {
|
{(affix.categories || []).map((categoryId) => {
|
||||||
const category = categoryMap[categoryId] || categoryId;
|
const category = categoryMap[categoryId] || categoryId;
|
||||||
return (
|
return (
|
||||||
<span className={`tone-${category.tone || "default"}`} key={categoryId}>
|
<span className={`nds-badge tone-${category.tone || "default"}`} key={categoryId}>
|
||||||
<span className="diablo4-category-icon" aria-hidden="true" style={getCategoryIconStyle(category)} />
|
<span className="diablo4-category-icon" aria-hidden="true" style={getCategoryIconStyle(category)} />
|
||||||
{getCategoryLabel(category)}
|
{getCategoryLabel(category)}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export function Diablo4Listing({ game, diablo4, filters, setFilters }) {
|
||||||
{visible.length ? visible.map((affix) => (
|
{visible.length ? visible.map((affix) => (
|
||||||
<Diablo4AffixCard key={affix.id} affix={affix} categoryMap={diablo4.categoryMap} />
|
<Diablo4AffixCard key={affix.id} affix={affix} categoryMap={diablo4.categoryMap} />
|
||||||
)) : (
|
)) : (
|
||||||
<div className="empty">
|
<div className="empty nds-empty-state">
|
||||||
<h2>Aucun résultat</h2>
|
<h2>Aucun résultat</h2>
|
||||||
<p>Ajustez la recherche ou réinitialisez les filtres actifs.</p>
|
<p>Ajustez la recherche ou réinitialisez les filtres actifs.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export function Diablo4Page({ category, game, diablo4, filters, setFilters }) {
|
||||||
<p>Chargement des affixes et filtres associés.</p>
|
<p>Chargement des affixes et filtres associés.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="empty">
|
<section className="empty nds-empty-state">
|
||||||
<h2>Chargement</h2>
|
<h2>Chargement</h2>
|
||||||
<p>Préparation des données locales...</p>
|
<p>Préparation des données locales...</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -26,7 +26,7 @@ export function Diablo4Page({ category, game, diablo4, filters, setFilters }) {
|
||||||
|
|
||||||
if (diablo4.error) {
|
if (diablo4.error) {
|
||||||
return (
|
return (
|
||||||
<section className="empty">
|
<section className="empty nds-empty-state">
|
||||||
<h1>Impossible de charger Diablo IV</h1>
|
<h1>Impossible de charger Diablo IV</h1>
|
||||||
<p>{diablo4.error}</p>
|
<p>{diablo4.error}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ export function MhwildsListing({ category, game, mhwilds, filters, setFilters, t
|
||||||
? <MonsterCard key={item.name} monster={item} t={t} />
|
? <MonsterCard key={item.name} monster={item} t={t} />
|
||||||
: <EndemicCard key={item.name} item={item} t={t} />
|
: <EndemicCard key={item.name} item={item} t={t} />
|
||||||
)) : (
|
)) : (
|
||||||
<div className="empty">
|
<div className="empty nds-empty-state">
|
||||||
<h2>Aucun résultat</h2>
|
<h2>Aucun résultat</h2>
|
||||||
<p>Ajustez la recherche ou réinitialisez les filtres actifs.</p>
|
<p>Ajustez la recherche ou réinitialisez les filtres actifs.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export function MhwildsPage({ category, game, mhwilds, filters, setFilters, t, l
|
||||||
<p>Chargement des données de chasse, faune endémique et filtres associés.</p>
|
<p>Chargement des données de chasse, faune endémique et filtres associés.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="empty">
|
<section className="empty nds-empty-state">
|
||||||
<h2>Chargement</h2>
|
<h2>Chargement</h2>
|
||||||
<p>Préparation des données locales...</p>
|
<p>Préparation des données locales...</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -27,7 +27,7 @@ export function MhwildsPage({ category, game, mhwilds, filters, setFilters, t, l
|
||||||
|
|
||||||
if (mhwilds.error) {
|
if (mhwilds.error) {
|
||||||
return (
|
return (
|
||||||
<section className="empty">
|
<section className="empty nds-empty-state">
|
||||||
<h1>Impossible de charger MH Wilds</h1>
|
<h1>Impossible de charger MH Wilds</h1>
|
||||||
<p>{mhwilds.error}</p>
|
<p>{mhwilds.error}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ export function ToolboxCard({ toolbox, game, actions, reorder }) {
|
||||||
const coverImage = gameCoverImage || toolbox.icon || DEFAULT_TOOLBOX_ICON;
|
const coverImage = gameCoverImage || toolbox.icon || DEFAULT_TOOLBOX_ICON;
|
||||||
const className = [
|
const className = [
|
||||||
"card",
|
"card",
|
||||||
|
"nds-card",
|
||||||
"toolbox-card",
|
"toolbox-card",
|
||||||
reorder.isItemDragging(toolbox.id) ? "is-dragging" : "",
|
reorder.isItemDragging(toolbox.id) ? "is-dragging" : "",
|
||||||
reorder.isItemDropTarget(toolbox.id) ? "is-drop-target" : "",
|
reorder.isItemDropTarget(toolbox.id) ? "is-drop-target" : "",
|
||||||
|
|
@ -32,7 +33,7 @@ export function ToolboxCard({ toolbox, game, actions, reorder }) {
|
||||||
].filter(Boolean).join(" ");
|
].filter(Boolean).join(" ");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={className} {...reorder.getItemProps({ itemId: toolbox.id })}>
|
<article className={className} data-variant="interactive" {...reorder.getItemProps({ itemId: toolbox.id })}>
|
||||||
<button
|
<button
|
||||||
className="toolbox-card-drag-handle nds-icon-button"
|
className="toolbox-card-drag-handle nds-icon-button"
|
||||||
data-size="md"
|
data-size="md"
|
||||||
|
|
@ -45,7 +46,7 @@ export function ToolboxCard({ toolbox, game, actions, reorder }) {
|
||||||
<Icon name="drag" />
|
<Icon name="drag" />
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
className={`card-cover toolbox-card-cover-link ${gameCoverImage ? "toolbox-card-cover" : "toolbox-icon-cover"}`}
|
className={`card-cover nds-card-cover toolbox-card-cover-link ${gameCoverImage ? "toolbox-card-cover" : "toolbox-icon-cover"}`}
|
||||||
href={`#/toolbox/${toolbox.id}`}
|
href={`#/toolbox/${toolbox.id}`}
|
||||||
aria-label={`Ouvrir ${toolbox.name}`}
|
aria-label={`Ouvrir ${toolbox.name}`}
|
||||||
title={`Ouvrir ${toolbox.name}`}
|
title={`Ouvrir ${toolbox.name}`}
|
||||||
|
|
@ -53,11 +54,11 @@ export function ToolboxCard({ toolbox, game, actions, reorder }) {
|
||||||
>
|
>
|
||||||
<img src={coverImage} alt={game ? game.title : `Icône de ${toolbox.name}`} loading="lazy" />
|
<img src={coverImage} alt={game ? game.title : `Icône de ${toolbox.name}`} loading="lazy" />
|
||||||
</a>
|
</a>
|
||||||
<div className="card-body">
|
<div className="card-body nds-card-body">
|
||||||
<p className="eyebrow">{game ? game.title : "Toolbox libre"}</p>
|
<p className="eyebrow">{game ? game.title : "Toolbox libre"}</p>
|
||||||
<h2>{toolbox.name}</h2>
|
<h2>{toolbox.name}</h2>
|
||||||
<small>Modifiée le {formatDate(toolbox.updatedAt)}</small>
|
<small>Modifiée le {formatDate(toolbox.updatedAt)}</small>
|
||||||
<div className="card-actions">
|
<div className="card-actions nds-card-actions nds-toolbar">
|
||||||
<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>
|
<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" 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({
|
<button className="card-icon-button nds-icon-button" data-size="md" data-variant="danger" onClick={() => actions.setConfirmModal({
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ export function ToolboxesPage({ siteContent, toolboxes, getToolboxGame, actions,
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="toolbox-page">
|
<div className="toolbox-page">
|
||||||
<section className="page-hero">
|
<section className="page-hero nds-page-hero">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">{content.eyebrow}</p>
|
<p className="eyebrow">{content.eyebrow}</p>
|
||||||
<h1>{content.title}</h1>
|
<h1>{content.title}</h1>
|
||||||
|
|
@ -107,7 +107,7 @@ export function ToolboxesPage({ siteContent, toolboxes, getToolboxGame, actions,
|
||||||
<a className="hero-library-button library-link-button nds-button" data-size="sm" 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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="toolbar toolbox-actions-row">
|
<section className="toolbar nds-toolbar toolbox-actions-row">
|
||||||
<div>
|
<div>
|
||||||
<button className="nds-button" data-size="sm" data-variant="primary" onClick={() => actions.setCreateModal({})}>{content.newButton}</button>
|
<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} />
|
<ImportButton className="nds-button" data-size="sm" label={content.importOne} onFile={actions.importToolbox} />
|
||||||
|
|
@ -143,7 +143,7 @@ export function ToolboxesPage({ siteContent, toolboxes, getToolboxGame, actions,
|
||||||
reorder={{ itemReorder, getItemProps, isItemDragging, isItemDropTarget, getDropPlacement }}
|
reorder={{ itemReorder, getItemProps, isItemDragging, isItemDropTarget, getDropPlacement }}
|
||||||
/>
|
/>
|
||||||
)) : (
|
)) : (
|
||||||
<div className="empty"><h2>{content.emptyTitle}</h2><p>{content.emptyText}</p></div>
|
<div className="empty nds-empty-state"><h2>{content.emptyTitle}</h2><p>{content.emptyText}</p></div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
<StorageQuota usage={storageUsage} />
|
<StorageQuota usage={storageUsage} />
|
||||||
|
|
@ -153,8 +153,8 @@ export function ToolboxesPage({ siteContent, toolboxes, getToolboxGame, actions,
|
||||||
|
|
||||||
function StorageHelpCard({ help }) {
|
function StorageHelpCard({ help }) {
|
||||||
return (
|
return (
|
||||||
<article className="card storage-help-card" aria-labelledby="storage-help-title">
|
<article className="card nds-card storage-help-card" aria-labelledby="storage-help-title">
|
||||||
<div className="card-body">
|
<div className="card-body nds-card-body">
|
||||||
<div className="storage-help-heading">
|
<div className="storage-help-heading">
|
||||||
<span className="storage-help-mark" aria-hidden="true" />
|
<span className="storage-help-mark" aria-hidden="true" />
|
||||||
<div><p className="eyebrow">IndexedDB</p><h2 id="storage-help-title">{help.title}</h2></div>
|
<div><p className="eyebrow">IndexedDB</p><h2 id="storage-help-title">{help.title}</h2></div>
|
||||||
|
|
@ -171,7 +171,7 @@ function StorageHelpCard({ help }) {
|
||||||
export function ToolboxPage(props) {
|
export function ToolboxPage(props) {
|
||||||
const { toolboxId, toolboxes, getToolboxGame } = props;
|
const { toolboxId, toolboxes, getToolboxGame } = props;
|
||||||
const toolbox = toolboxes.find((item) => item.id === toolboxId);
|
const toolbox = toolboxes.find((item) => item.id === toolboxId);
|
||||||
if (!toolbox) return <div className="empty"><h1>Toolbox introuvable</h1><a className="nds-button" data-size="sm" href="#/toolboxes">Retour</a></div>;
|
if (!toolbox) return <div className="empty nds-empty-state"><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} />;
|
return <ToolboxView {...props} toolbox={toolbox} toolboxGame={getToolboxGame(toolbox)} embedded={false} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,7 +257,7 @@ function ToolboxView({ siteContent, toolbox, toolboxGame, embedded, actions, sto
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={embedded ? "toolbox-embedded" : "toolbox-page"}>
|
<div className={embedded ? "toolbox-embedded" : "toolbox-page"}>
|
||||||
<section className={embedded ? "toolbox-head page-hero toolbox-head-embedded" : "toolbox-head page-hero"}>
|
<section className={embedded ? "toolbox-head page-hero nds-page-hero toolbox-head-embedded" : "toolbox-head page-hero nds-page-hero"}>
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Toolbox</p>
|
<p className="eyebrow">Toolbox</p>
|
||||||
<div className="toolbox-title-row">
|
<div className="toolbox-title-row">
|
||||||
|
|
@ -279,7 +279,7 @@ function ToolboxView({ siteContent, toolbox, toolboxGame, embedded, actions, sto
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="actions">
|
<div className="actions nds-toolbar">
|
||||||
<AddToolControls onAdd={addModule} />
|
<AddToolControls onAdd={addModule} />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -385,13 +385,13 @@ export function ToolboxDrawer({ gameId, game, siteContent, toolboxes, links, act
|
||||||
return (
|
return (
|
||||||
<aside className="drawer" id="toolbox-drawer" aria-hidden="false" style={width ? { "--drawer-panel-width": `${width}px` } : undefined}>
|
<aside className="drawer" id="toolbox-drawer" aria-hidden="false" style={width ? { "--drawer-panel-width": `${width}px` } : undefined}>
|
||||||
<div className="drawer-backdrop" onClick={() => actions.setDrawerGameId("")} />
|
<div className="drawer-backdrop" onClick={() => actions.setDrawerGameId("")} />
|
||||||
<span className="drawer-resize-handle" aria-hidden="true" onPointerDown={startResize} />
|
<span className="drawer-resize-handle nds-drawer-resize-handle" aria-hidden="true" onPointerDown={startResize} />
|
||||||
<section className="drawer-panel legacy-scrollbar" ref={panelRef} style={width ? { width } : undefined}>
|
<section className="drawer-panel nds-drawer-panel legacy-scrollbar" ref={panelRef} style={width ? { width } : undefined}>
|
||||||
<div className="drawer-content">
|
<div className="drawer-content">
|
||||||
<header>
|
<header>
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Toolbox liée</p>
|
<p className="eyebrow">Toolbox liée</p>
|
||||||
<div className="drawer-toolbox-actions" aria-label="Actions toolbox liée">
|
<div className="drawer-toolbox-actions nds-toolbar" aria-label="Actions toolbox liée">
|
||||||
<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>
|
<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)} />
|
<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>
|
<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>
|
||||||
|
|
@ -400,7 +400,7 @@ export function ToolboxDrawer({ gameId, game, siteContent, toolboxes, links, act
|
||||||
<button className="drawer-close-button nds-icon-button" data-size="sm" 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>
|
</header>
|
||||||
{toolbox ? <ToolboxView siteContent={siteContent} toolbox={toolbox} toolboxGame={game} embedded actions={actions} storageUsage={storageUsage} storageReady={storageReady} getModuleData={getModuleData} updateToolbox={updateToolbox} updateModuleData={updateModuleData} addImageFiles={addImageFiles} /> : (
|
{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="nds-button" data-size="sm" data-variant="primary" onClick={() => actions.setCreateModal({ gameId })}>Créer et associer</button></div>
|
<div className="empty nds-empty-state"><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} />
|
<StorageQuota usage={storageUsage} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ export function AddToolControls({ onAdd }) {
|
||||||
</div>
|
</div>
|
||||||
{open && createPortal(
|
{open && createPortal(
|
||||||
<div className="tool-add-modal-root" role="dialog" aria-modal="true" aria-labelledby="tool-add-modal-title">
|
<div className="tool-add-modal-root" role="dialog" aria-modal="true" aria-labelledby="tool-add-modal-title">
|
||||||
<div className="confirm-backdrop" onClick={() => setOpen(false)} />
|
<div className="confirm-backdrop nds-modal-backdrop" onClick={() => setOpen(false)} />
|
||||||
<section className="confirm-modal tool-add-modal">
|
<section className="confirm-modal nds-modal tool-add-modal">
|
||||||
<header>
|
<header>
|
||||||
<h2 id="tool-add-modal-title">Ajouter un outil</h2>
|
<h2 id="tool-add-modal-title">Ajouter un outil</h2>
|
||||||
<button className="drawer-close-button nds-icon-button" data-size="sm" 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">
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ export function TextImportModal({ title, value, placeholder, submitLabel, readOn
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<div className="confirm-modal-root text-import-modal-root">
|
<div className="confirm-modal-root text-import-modal-root">
|
||||||
<div className="confirm-backdrop" onClick={onClose} />
|
<div className="confirm-backdrop nds-modal-backdrop" onClick={onClose} />
|
||||||
<section className="confirm-modal text-import-modal-dialog" role="dialog" aria-modal="true" aria-labelledby="text-import-title">
|
<section className="confirm-modal nds-modal text-import-modal-dialog" role="dialog" aria-modal="true" aria-labelledby="text-import-title">
|
||||||
<header>
|
<header>
|
||||||
<h2 id="text-import-title">{title}</h2>
|
<h2 id="text-import-title">{title}</h2>
|
||||||
<button className="drawer-close-button nds-icon-button" data-size="sm" 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">
|
||||||
|
|
@ -42,18 +42,18 @@ export function TextImportModal({ title, value, placeholder, submitLabel, readOn
|
||||||
aria-describedby={error ? "text-import-error" : undefined}
|
aria-describedby={error ? "text-import-error" : undefined}
|
||||||
/>
|
/>
|
||||||
{info && (
|
{info && (
|
||||||
<p className="text-import-info">
|
<p className="text-import-info nds-inline-notice">
|
||||||
<Icon name="info" />
|
<Icon name="info" />
|
||||||
<span>{info}</span>
|
<span>{info}</span>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{error && (
|
{error && (
|
||||||
<p className="text-import-error" id="text-import-error" role="alert">
|
<p className="text-import-error nds-inline-notice" data-variant="danger" id="text-import-error" role="alert">
|
||||||
<Icon name="close" />
|
<Icon name="close" />
|
||||||
<span>{error}</span>
|
<span>{error}</span>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<footer>
|
<footer className="nds-modal-footer">
|
||||||
<button className="nds-button" data-size="sm" type="button" onClick={onClose}>Annuler</button>
|
<button className="nds-button" data-size="sm" type="button" onClick={onClose}>Annuler</button>
|
||||||
{readOnly ? (
|
{readOnly ? (
|
||||||
<button className="nds-button" data-size="sm" data-variant="primary" type="button" onClick={onCopy}>{submitLabel}</button>
|
<button className="nds-button" data-size="sm" data-variant="primary" type="button" onClick={onCopy}>{submitLabel}</button>
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ function App() {
|
||||||
if (!siteContent) {
|
if (!siteContent) {
|
||||||
return (
|
return (
|
||||||
<main className="startup-error">
|
<main className="startup-error">
|
||||||
<section className="empty">
|
<section className="empty nds-empty-state">
|
||||||
<h1>Contenu indisponible</h1>
|
<h1>Contenu indisponible</h1>
|
||||||
<p>{siteContentError || "Chargement du contenu..."}</p>
|
<p>{siteContentError || "Chargement du contenu..."}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -102,7 +102,7 @@ function App() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Shell route={route} content={siteContent} games={games} toolboxes={store.toolboxes} links={store.links} actions={actions}>
|
<Shell route={route} content={siteContent} games={games} toolboxes={store.toolboxes} links={store.links} drawerGameId={drawerGameId} actions={actions}>
|
||||||
<RouteContent
|
<RouteContent
|
||||||
route={route}
|
route={route}
|
||||||
siteContent={siteContent}
|
siteContent={siteContent}
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,14 @@ export function AboutPage({ siteContent }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="about-page">
|
<div className="about-page">
|
||||||
<section className="page-hero">
|
<section className="page-hero nds-page-hero">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">{content.eyebrow}</p>
|
<p className="eyebrow">{content.eyebrow}</p>
|
||||||
<h1>{content.title}</h1>
|
<h1>{content.title}</h1>
|
||||||
<p><RichText text={content.description} /></p>
|
<p><RichText text={content.description} /></p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="about-story-section nebula-panel" aria-labelledby="about-story-title">
|
<section className="about-story-section nebula-panel nds-panel" aria-labelledby="about-story-title">
|
||||||
<div className="about-section-heading">
|
<div className="about-section-heading">
|
||||||
<p className="eyebrow">{content.sectionsEyebrow}</p>
|
<p className="eyebrow">{content.sectionsEyebrow}</p>
|
||||||
<h2 id="about-story-title">{content.sectionsTitle}</h2>
|
<h2 id="about-story-title">{content.sectionsTitle}</h2>
|
||||||
|
|
@ -86,7 +86,7 @@ export function AboutPage({ siteContent }) {
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
<HomeOnboardingSection content={siteContent.home.onboarding} className="about-onboarding-section" />
|
<HomeOnboardingSection content={siteContent.home.onboarding} className="about-onboarding-section" />
|
||||||
<section className="about-tools-section nebula-panel">
|
<section className="about-tools-section nebula-panel nds-panel">
|
||||||
<div className="page-heading">
|
<div className="page-heading">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Toolbox</p>
|
<p className="eyebrow">Toolbox</p>
|
||||||
|
|
@ -115,7 +115,7 @@ export function AboutPage({ siteContent }) {
|
||||||
<a className="about-tools-library-button nds-button" data-size="md" data-variant="primary" href="#/library"><Icon name="book" />{content.toolsLibraryLink}</a>
|
<a className="about-tools-library-button nds-button" data-size="md" data-variant="primary" href="#/library"><Icon name="book" />{content.toolsLibraryLink}</a>
|
||||||
</section>
|
</section>
|
||||||
{content.contribute && (
|
{content.contribute && (
|
||||||
<section className="about-contribute-section nebula-panel" id="contribuer" aria-labelledby="about-contribute-title">
|
<section className="about-contribute-section nebula-panel nds-panel" id="contribuer" aria-labelledby="about-contribute-title">
|
||||||
<div className="about-section-heading">
|
<div className="about-section-heading">
|
||||||
<p className="eyebrow">{content.contribute.eyebrow}</p>
|
<p className="eyebrow">{content.contribute.eyebrow}</p>
|
||||||
<h2 id="about-contribute-title">{content.contribute.title}</h2>
|
<h2 id="about-contribute-title">{content.contribute.title}</h2>
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ const VARIANT_VALUES = {
|
||||||
textarea: ["default", "ghost"],
|
textarea: ["default", "ghost"],
|
||||||
swatch: ["color", "rainbow"],
|
swatch: ["color", "rainbow"],
|
||||||
event: ["default", "dark"],
|
event: ["default", "dark"],
|
||||||
|
surface: ["default", "interactive", "tool", "filter", "modal"],
|
||||||
|
badge: ["primary", "neutral", "gold", "success", "warning", "danger"],
|
||||||
|
feedback: ["info", "success", "warning", "danger"],
|
||||||
|
position: ["left", "right"],
|
||||||
none: ["default"]
|
none: ["default"]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -266,9 +270,185 @@ const COMPONENTS = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const PATTERN_COMPONENTS = [
|
||||||
|
{
|
||||||
|
id: "nds-panel",
|
||||||
|
name: "Panel",
|
||||||
|
className: "nds-panel",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: ["default", "interactive"],
|
||||||
|
attributes: { "data-variant": ["default", "interactive"] },
|
||||||
|
render: ({ attributes, variant }) => (
|
||||||
|
<article className="nds-panel design-system-pattern-card" {...attributes}>
|
||||||
|
<p className="eyebrow">{variant}</p>
|
||||||
|
<strong>Surface de lecture</strong>
|
||||||
|
<span>Fond sombre, bordure Nebula et accent dore contenu.</span>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-card",
|
||||||
|
name: "Card",
|
||||||
|
className: "nds-card",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: ["default", "interactive"],
|
||||||
|
attributes: { "data-variant": ["default", "interactive"] },
|
||||||
|
render: ({ attributes }) => (
|
||||||
|
<article className="nds-card design-system-pattern-card" {...attributes}>
|
||||||
|
<div className="nds-card-cover design-system-pattern-cover"><Icon name="toolbox" /></div>
|
||||||
|
<div className="nds-card-body">
|
||||||
|
<p className="eyebrow">Card</p>
|
||||||
|
<strong>Element repete</strong>
|
||||||
|
<div className="nds-card-actions"><button className="nds-button" data-size="xs" type="button">Ouvrir</button></div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-tool-panel",
|
||||||
|
name: "Tool Panel",
|
||||||
|
className: "nds-tool-panel",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: ["tool", "interactive"],
|
||||||
|
attributes: { "data-variant": ["tool", "interactive"] },
|
||||||
|
render: ({ attributes }) => (
|
||||||
|
<section className="nds-tool-panel design-system-pattern-card" {...attributes}>
|
||||||
|
<header className="nds-section-heading"><strong>Outil</strong><button className="nds-icon-button" data-size="xs" type="button" aria-label="Ajouter"><Icon name="add" /></button></header>
|
||||||
|
<p>Surface de travail compacte pour les toolboxes.</p>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-toolbar",
|
||||||
|
name: "Toolbar",
|
||||||
|
className: "nds-toolbar",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: VARIANT_VALUES.none,
|
||||||
|
attributes: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="nds-toolbar design-system-pattern-inline">
|
||||||
|
<button className="nds-button" data-size="xs" data-variant="primary" type="button">Ajouter</button>
|
||||||
|
<button className="nds-icon-button" data-size="xs" type="button" aria-label="Parametres"><Icon name="settings" /></button>
|
||||||
|
<button className="nds-icon-button" data-size="xs" data-variant="danger" type="button" aria-label="Supprimer"><Icon name="trash" /></button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-section-heading",
|
||||||
|
name: "Section Heading",
|
||||||
|
className: "nds-section-heading",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: VARIANT_VALUES.none,
|
||||||
|
attributes: {},
|
||||||
|
render: () => (
|
||||||
|
<header className="nds-section-heading design-system-pattern-wide">
|
||||||
|
<div><p className="eyebrow">Heading</p><strong>Section</strong></div>
|
||||||
|
<button className="nds-button" data-size="xs" type="button">Action</button>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-page-hero",
|
||||||
|
name: "Page Hero",
|
||||||
|
className: "nds-page-hero",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: VARIANT_VALUES.none,
|
||||||
|
attributes: {},
|
||||||
|
render: () => (
|
||||||
|
<section className="nds-page-hero design-system-pattern-hero">
|
||||||
|
<div><p className="eyebrow">Hero</p><strong>Page concise</strong><span>Introduction courte et lisible.</span></div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-badge",
|
||||||
|
name: "Badge",
|
||||||
|
className: "nds-badge",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: VARIANT_VALUES.badge,
|
||||||
|
attributes: { "data-variant": VARIANT_VALUES.badge },
|
||||||
|
render: ({ attributes, variant }) => <span className="nds-badge" {...attributes}>{variant}</span>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-chip",
|
||||||
|
name: "Chip",
|
||||||
|
className: "nds-chip",
|
||||||
|
sizes: ["sm", "default"],
|
||||||
|
variants: VARIANT_VALUES.none,
|
||||||
|
attributes: { "data-size": ["sm", "default"], "data-state": STATE_VALUES },
|
||||||
|
render: ({ attributes, state }) => <button className="nds-chip" {...attributes} type="button" disabled={state === "disabled"} aria-pressed={state === "active"}><Icon name="checklist" />Filtre</button>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-empty-state",
|
||||||
|
name: "Empty State",
|
||||||
|
className: "nds-empty-state",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: VARIANT_VALUES.none,
|
||||||
|
attributes: {},
|
||||||
|
render: () => <div className="nds-empty-state design-system-pattern-card"><strong>Aucun contenu</strong><span>Message court et action optionnelle.</span></div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-inline-notice",
|
||||||
|
name: "Inline Notice",
|
||||||
|
className: "nds-inline-notice",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: VARIANT_VALUES.feedback,
|
||||||
|
attributes: { "data-variant": VARIANT_VALUES.feedback },
|
||||||
|
render: ({ attributes, variant }) => <p className="nds-inline-notice" {...attributes}>{variant === "info" ? "Information compacte" : `${variant} compact`}</p>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-progress",
|
||||||
|
name: "Progress",
|
||||||
|
className: "nds-progress",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: ["primary", "success", "warning", "danger"],
|
||||||
|
attributes: { "data-variant": ["primary", "success", "warning", "danger"] },
|
||||||
|
render: ({ attributes, variant }) => <span className="nds-progress" {...attributes} aria-label={`Progression ${variant}`}><span style={{ width: variant === "danger" ? "86%" : "58%" }} /></span>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-filter-panel",
|
||||||
|
name: "Filter Panel",
|
||||||
|
className: "nds-filter-panel",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: VARIANT_VALUES.none,
|
||||||
|
attributes: {},
|
||||||
|
render: () => (
|
||||||
|
<aside className="nds-filter-panel design-system-pattern-card">
|
||||||
|
<header className="nds-section-heading"><strong>Filtres</strong><button className="nds-icon-button" data-size="xs" type="button" aria-label="Reset"><Icon name="rubber" /></button></header>
|
||||||
|
<button className="nds-chip" data-size="sm" type="button" aria-pressed="true">Actif</button>
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-reorder-indicator",
|
||||||
|
name: "Reorder Indicator",
|
||||||
|
className: "nds-reorder-indicator",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: VARIANT_VALUES.none,
|
||||||
|
configurable: { "data-axis": ["vertical", "horizontal"], "data-placement": ["before", "after"] },
|
||||||
|
attributes: { "data-axis": ["vertical", "horizontal"], "data-placement": ["before", "after"] },
|
||||||
|
render: ({ attributes }) => <div className="nds-reorder-indicator design-system-pattern-reorder" {...attributes}>Drop target</div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nds-overlays",
|
||||||
|
name: "Overlay Patterns",
|
||||||
|
className: "nds-modal / nds-drawer / nds-toast / nds-popover",
|
||||||
|
sizes: SIZE_VALUES.none,
|
||||||
|
variants: ["modal", "drawer", "toast", "popover"],
|
||||||
|
attributes: { "data-variant": ["modal", "drawer", "toast", "popover"] },
|
||||||
|
render: ({ variant }) => {
|
||||||
|
if (variant === "drawer") return <div className="nds-drawer-panel design-system-pattern-overlay">Drawer panel</div>;
|
||||||
|
if (variant === "toast") return <div className="nds-toast design-system-pattern-toast"><span>Alerte globale</span><button className="nds-icon-button" data-size="xs" type="button" aria-label="Fermer"><Icon name="close" /></button></div>;
|
||||||
|
if (variant === "popover") return <div className="nds-popover design-system-pattern-overlay">Menu flottant</div>;
|
||||||
|
return <div className="nds-modal design-system-pattern-overlay"><strong>Modale</strong><footer className="nds-modal-footer"><button className="nds-button" data-size="xs" type="button">Annuler</button></footer></div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const NAV_ITEMS = [
|
const NAV_ITEMS = [
|
||||||
{ id: "foundations", label: "Foundations" },
|
{ id: "foundations", label: "Foundations" },
|
||||||
...COMPONENTS.map(({ id, name }) => ({ id, label: name })),
|
...COMPONENTS.map(({ id, name }) => ({ id, label: name })),
|
||||||
|
...PATTERN_COMPONENTS.map(({ id, name }) => ({ id, label: name })),
|
||||||
{ id: "date-picker-buttons", label: "Date Picker Buttons" },
|
{ id: "date-picker-buttons", label: "Date Picker Buttons" },
|
||||||
{ id: "specialized-components", label: "Specialized Components" },
|
{ id: "specialized-components", label: "Specialized Components" },
|
||||||
{ id: "audit", label: "Audit" }
|
{ id: "audit", label: "Audit" }
|
||||||
|
|
@ -629,6 +809,7 @@ export function DesignSystemPage() {
|
||||||
<main className="design-system-main">
|
<main className="design-system-main">
|
||||||
<FoundationsSection />
|
<FoundationsSection />
|
||||||
{COMPONENTS.map((component) => <ComponentCatalogSection key={component.id} component={component} />)}
|
{COMPONENTS.map((component) => <ComponentCatalogSection key={component.id} component={component} />)}
|
||||||
|
{PATTERN_COMPONENTS.map((component) => <ComponentCatalogSection key={component.id} component={component} />)}
|
||||||
<DatePickerButtonsSection />
|
<DatePickerButtonsSection />
|
||||||
<SpecializedComponentsSection />
|
<SpecializedComponentsSection />
|
||||||
<AuditSection />
|
<AuditSection />
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export function HomePage({ siteContent, toolboxes, actions }) {
|
||||||
<p className="eyebrow">{content.hero.eyebrow}</p>
|
<p className="eyebrow">{content.hero.eyebrow}</p>
|
||||||
<h1>{content.hero.title}</h1>
|
<h1>{content.hero.title}</h1>
|
||||||
<p>{content.hero.description}</p>
|
<p>{content.hero.description}</p>
|
||||||
<div className="actions">
|
<div className="actions nds-toolbar">
|
||||||
<a className="nds-button" data-size="md" data-variant="primary" href="#/toolboxes">{content.hero.primaryAction}</a>
|
<a className="nds-button" data-size="md" data-variant="primary" href="#/toolboxes">{content.hero.primaryAction}</a>
|
||||||
<a className="nds-button" data-size="md" href="#/games">{content.hero.secondaryAction}</a>
|
<a className="nds-button" data-size="md" href="#/games">{content.hero.secondaryAction}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -25,7 +25,7 @@ export function HomePage({ siteContent, toolboxes, actions }) {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{showOnboarding && <HomeOnboardingSection content={content.onboarding} onCreateToolbox={() => actions.setCreateModal({ tutorial: true })} />}
|
{showOnboarding && <HomeOnboardingSection content={content.onboarding} onCreateToolbox={() => actions.setCreateModal({ tutorial: true })} />}
|
||||||
<section className="origin-section nebula-panel" aria-labelledby="origin-title">
|
<section className="origin-section nebula-panel nds-panel" aria-labelledby="origin-title">
|
||||||
<div className="origin-copy">
|
<div className="origin-copy">
|
||||||
<p className="eyebrow">{content.origin.eyebrow}</p>
|
<p className="eyebrow">{content.origin.eyebrow}</p>
|
||||||
<h2 id="origin-title">{content.origin.title}</h2>
|
<h2 id="origin-title">{content.origin.title}</h2>
|
||||||
|
|
|
||||||
|
|
@ -212,14 +212,14 @@ export function LibraryPage({ siteContent, actions }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="library-page">
|
<div className="library-page">
|
||||||
<section className="page-hero library-hero">
|
<section className="page-hero nds-page-hero library-hero">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">{content.eyebrow}</p>
|
<p className="eyebrow">{content.eyebrow}</p>
|
||||||
<h1>{content.title}</h1>
|
<h1>{content.title}</h1>
|
||||||
<p>{content.description}</p>
|
<p>{content.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="library-summary nebula-panel" aria-label={content.summaryLabel}>
|
<section className="library-summary nebula-panel nds-panel" aria-label={content.summaryLabel}>
|
||||||
{content.summaryItems.map((item) => (
|
{content.summaryItems.map((item) => (
|
||||||
<article key={item.title}>
|
<article key={item.title}>
|
||||||
<span className="library-summary-icon" aria-hidden="true">
|
<span className="library-summary-icon" aria-hidden="true">
|
||||||
|
|
@ -232,8 +232,8 @@ export function LibraryPage({ siteContent, actions }) {
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
{libraryError && <section className="empty"><h2>Librairie indisponible</h2><p>{libraryError}</p></section>}
|
{libraryError && <section className="empty nds-empty-state"><h2>Librairie indisponible</h2><p>{libraryError}</p></section>}
|
||||||
{!libraryError && !libraryPayload && <section className="empty"><p>Chargement des exemples...</p></section>}
|
{!libraryError && !libraryPayload && <section className="empty nds-empty-state"><p>Chargement des exemples...</p></section>}
|
||||||
{libraryPayload && (
|
{libraryPayload && (
|
||||||
<>
|
<>
|
||||||
<div className="library-doc-layout">
|
<div className="library-doc-layout">
|
||||||
|
|
@ -288,7 +288,7 @@ export function LibraryPage({ siteContent, actions }) {
|
||||||
|
|
||||||
function LibraryDocNav({ content, categories, activeCategoryKey, onSelectCategory }) {
|
function LibraryDocNav({ content, categories, activeCategoryKey, onSelectCategory }) {
|
||||||
return (
|
return (
|
||||||
<nav className="library-doc-nav nebula-panel" aria-label={content.tocLabel || "Sommaire des outils"}>
|
<nav className="library-doc-nav nebula-panel nds-panel" aria-label={content.tocLabel || "Sommaire des outils"}>
|
||||||
<div className="library-toc-heading">
|
<div className="library-toc-heading">
|
||||||
<h2>{content.docTocTitle || content.tocTitle || "Sommaire"}</h2>
|
<h2>{content.docTocTitle || content.tocTitle || "Sommaire"}</h2>
|
||||||
<p>{content.tocDescription || "Accédez directement à un exemple d’outil dans la page."}</p>
|
<p>{content.tocDescription || "Accédez directement à un exemple d’outil dans la page."}</p>
|
||||||
|
|
@ -338,7 +338,7 @@ function LibraryToolExample({ module, sourceData, description, categoryTitle, do
|
||||||
const title = module.title || definition.label || "Outil";
|
const title = module.title || definition.label || "Outil";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id={getLibraryAnchorId(module)} className="library-tool-section">
|
<section id={getLibraryAnchorId(module)} className="library-tool-section nds-panel">
|
||||||
<div className="library-tool-intro">
|
<div className="library-tool-intro">
|
||||||
<div>
|
<div>
|
||||||
<div className="library-tool-title-row">
|
<div className="library-tool-title-row">
|
||||||
|
|
@ -347,13 +347,13 @@ function LibraryToolExample({ module, sourceData, description, categoryTitle, do
|
||||||
</span>
|
</span>
|
||||||
<h2>{title}</h2>
|
<h2>{title}</h2>
|
||||||
</div>
|
</div>
|
||||||
<span className="library-tool-badge">{categoryTitle}</span>
|
<span className="library-tool-badge nds-badge">{categoryTitle}</span>
|
||||||
{description && <p>{description}</p>}
|
{description && <p>{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`library-example-layout ${Array.isArray(docs?.controls) && docs.controls.length ? "has-legend" : ""}`}>
|
<div className={`library-example-layout ${Array.isArray(docs?.controls) && docs.controls.length ? "has-legend" : ""}`}>
|
||||||
<ToolControlLegend items={docs?.controls} heading={controlLegendHeading || "Repères"} />
|
<ToolControlLegend items={docs?.controls} heading={controlLegendHeading || "Repères"} />
|
||||||
<article className={`module library-tool-example ${scrollable ? "is-scrollable" : ""}`} data-module-type={module.type}>
|
<article className={`module nds-tool-panel library-tool-example ${scrollable ? "is-scrollable" : ""}`} data-module-type={module.type}>
|
||||||
<header>
|
<header>
|
||||||
<div>
|
<div>
|
||||||
<h2>{definition.label}</h2>
|
<h2>{definition.label}</h2>
|
||||||
|
|
@ -393,7 +393,7 @@ function LibraryToolExample({ module, sourceData, description, categoryTitle, do
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div className={`module-content ${scrollable ? "is-scrollable legacy-scrollbar" : ""}`}>
|
<div className={`module-content ${scrollable ? "is-scrollable legacy-scrollbar" : ""}`}>
|
||||||
<Suspense fallback={<div className="empty"><p>Chargement de l'outil...</p></div>}>
|
<Suspense fallback={<div className="empty nds-empty-state"><p>Chargement de l'outil...</p></div>}>
|
||||||
<Component
|
<Component
|
||||||
toolboxId={LIBRARY_TOOLBOX_ID}
|
toolboxId={LIBRARY_TOOLBOX_ID}
|
||||||
moduleId={module.id}
|
moduleId={module.id}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ const DesignSystemPage = import.meta.env.DEV
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
function RouteLoader() {
|
function RouteLoader() {
|
||||||
return <section className="empty"><p>Chargement...</p></section>;
|
return <section className="empty nds-empty-state"><p>Chargement...</p></section>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RouteContent(props) {
|
export function RouteContent(props) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Rôle : regroupe les styles de cards, panels et variantes compactes.
|
// Rôle : regroupe les styles de cards, panels et variantes compactes.
|
||||||
@use "mixins";
|
@use "mixins";
|
||||||
@use "design-system/surfaces";
|
@use "design-system/controls";
|
||||||
|
|
||||||
.section-grid,
|
.section-grid,
|
||||||
.cards,
|
.cards,
|
||||||
|
|
@ -22,33 +22,19 @@
|
||||||
|
|
||||||
.feature,
|
.feature,
|
||||||
.info-panel,
|
.info-panel,
|
||||||
.module,
|
.module {
|
||||||
.empty,
|
|
||||||
.card {
|
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
background: rgba(16, 20, 38, 0.88);
|
background: rgba(16, 20, 38, 0.88);
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
|
||||||
@include surfaces.card-surface;
|
|
||||||
}
|
|
||||||
|
|
||||||
@supports (corner-shape: scoop) {
|
|
||||||
.card {
|
|
||||||
corner-shape: scoop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature,
|
.feature,
|
||||||
.info-panel,
|
.info-panel {
|
||||||
.empty {
|
|
||||||
padding: var(--space-5);
|
padding: var(--space-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature,
|
.feature,
|
||||||
.card,
|
|
||||||
.info-panel {
|
.info-panel {
|
||||||
transition:
|
transition:
|
||||||
transform var(--duration-fast) var(--ease-standard),
|
transform var(--duration-fast) var(--ease-standard),
|
||||||
|
|
@ -57,51 +43,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature:hover,
|
.feature:hover,
|
||||||
.card:hover,
|
|
||||||
.info-panel:hover {
|
.info-panel:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
border-color: var(--color-border-hover);
|
border-color: var(--color-border-hover);
|
||||||
background: rgba(21, 26, 48, 0.96);
|
background: rgba(21, 26, 48, 0.96);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
@include surfaces.card-surface-hover;
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compact-data-card {
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
background:
|
|
||||||
linear-gradient(rgba(17, 20, 34, 0.93), rgba(17, 20, 34, 0.93)) padding-box,
|
|
||||||
radial-gradient(circle at 100% 0%, rgba(34, 211, 238, 0.24), transparent 24%) border-box,
|
|
||||||
linear-gradient(135deg, rgba(246, 196, 83, 0.34) 0%, rgba(246, 196, 83, 0.2) 54%, rgba(34, 211, 238, 0.22) 100%) border-box;
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
transition:
|
|
||||||
transform var(--duration-fast) var(--ease-standard),
|
|
||||||
border-color var(--duration-fast) var(--ease-standard),
|
|
||||||
background-color var(--duration-fast) var(--ease-standard);
|
|
||||||
}
|
|
||||||
|
|
||||||
@supports (corner-shape: scoop) {
|
|
||||||
.compact-data-card {
|
|
||||||
corner-shape: scoop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.compact-data-card:hover {
|
|
||||||
transform: none;
|
|
||||||
border-color: transparent;
|
|
||||||
background:
|
|
||||||
linear-gradient(rgba(20, 23, 39, 0.96), rgba(20, 23, 39, 0.96)) padding-box,
|
|
||||||
linear-gradient(135deg, rgba(139, 92, 246, 0.62), rgba(196, 181, 253, 0.42)) border-box;
|
|
||||||
box-shadow:
|
|
||||||
var(--shadow-sm),
|
|
||||||
0 0 14px rgba(139, 92, 246, 0.12),
|
|
||||||
0 0 22px rgba(196, 181, 253, 0.07);
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature {
|
.feature {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
@ -113,27 +60,7 @@
|
||||||
|
|
||||||
.page-heading,
|
.page-heading,
|
||||||
.toolbox-head {
|
.toolbox-head {
|
||||||
display: flex;
|
@include controls.section-heading-pattern;
|
||||||
align-items: flex-end;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-cover {
|
|
||||||
min-height: 124px;
|
|
||||||
@include surfaces.card-cover-divider;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body {
|
|
||||||
padding: var(--space-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-actions {
|
|
||||||
margin-top: var(--space-5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbox-card {
|
.toolbox-card {
|
||||||
|
|
@ -406,40 +333,14 @@
|
||||||
font-size: var(--font-size-sm);
|
font-size: var(--font-size-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.storage-quota-track {
|
|
||||||
width: 100%;
|
|
||||||
height: 9px;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid rgba(150, 165, 205, 0.14);
|
|
||||||
border-radius: var(--radius-pill);
|
|
||||||
background: rgba(5, 7, 17, 0.82);
|
|
||||||
}
|
|
||||||
|
|
||||||
.storage-quota-track span {
|
|
||||||
display: block;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: inherit;
|
|
||||||
background: var(--gradient-brand);
|
|
||||||
box-shadow: 0 0 18px rgba(99, 102, 241, 0.32);
|
|
||||||
transition: width var(--duration-normal) var(--ease-standard);
|
|
||||||
}
|
|
||||||
|
|
||||||
.storage-quota-warning {
|
.storage-quota-warning {
|
||||||
border-color: rgba(251, 191, 36, 0.34);
|
border-color: rgba(251, 191, 36, 0.34);
|
||||||
}
|
}
|
||||||
|
|
||||||
.storage-quota-warning .storage-quota-track span {
|
|
||||||
background: linear-gradient(135deg, #fbbf24, #f97316);
|
|
||||||
}
|
|
||||||
|
|
||||||
.storage-quota-danger {
|
.storage-quota-danger {
|
||||||
border-color: rgba(251, 113, 133, 0.42);
|
border-color: rgba(251, 113, 133, 0.42);
|
||||||
}
|
}
|
||||||
|
|
||||||
.storage-quota-danger .storage-quota-track span {
|
|
||||||
background: linear-gradient(135deg, #fb7185, #d946ef);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-icon-button {
|
.card-icon-button {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -452,6 +452,88 @@
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-card,
|
||||||
|
.design-system-pattern-wide,
|
||||||
|
.design-system-pattern-hero,
|
||||||
|
.design-system-pattern-overlay,
|
||||||
|
.design-system-pattern-toast {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-card {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 120px;
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-card strong,
|
||||||
|
.design-system-pattern-wide strong,
|
||||||
|
.design-system-pattern-hero strong,
|
||||||
|
.design-system-pattern-overlay strong {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-card p,
|
||||||
|
.design-system-pattern-card span,
|
||||||
|
.design-system-pattern-hero span {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-cover {
|
||||||
|
display: grid;
|
||||||
|
min-height: 74px;
|
||||||
|
place-items: center;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 50% 38%, rgba(246, 196, 83, 0.16), transparent 42%),
|
||||||
|
var(--gradient-nebula);
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-cover .ui-icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-inline {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-wide {
|
||||||
|
min-height: 72px;
|
||||||
|
padding: var(--space-3);
|
||||||
|
border: 1px dashed rgba(150, 165, 205, 0.18);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-hero {
|
||||||
|
min-height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-reorder {
|
||||||
|
display: grid;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 62px;
|
||||||
|
place-items: center;
|
||||||
|
border: 1px solid rgba(165, 180, 252, 0.12);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: rgba(5, 7, 17, 0.36);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-overlay {
|
||||||
|
min-height: 92px;
|
||||||
|
padding: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.design-system-pattern-toast {
|
||||||
|
min-width: min(300px, 100%);
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
.design-system-page .calendar-month-event-repeat-switch {
|
.design-system-page .calendar-month-event-repeat-switch {
|
||||||
width: min(230px, 100%);
|
width: min(230px, 100%);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
|
.drawer {
|
||||||
|
--drawer-top-offset: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
.app-shell {
|
.app-shell {
|
||||||
display: block;
|
display: block;
|
||||||
padding-bottom: 76px;
|
padding-bottom: 76px;
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@
|
||||||
@use "attributes/menus";
|
@use "attributes/menus";
|
||||||
@use "attributes/events";
|
@use "attributes/events";
|
||||||
@use "attributes/states";
|
@use "attributes/states";
|
||||||
|
@use "attributes/patterns";
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@
|
||||||
@forward "controls/events";
|
@forward "controls/events";
|
||||||
@forward "controls/checkboxes";
|
@forward "controls/checkboxes";
|
||||||
@forward "controls/scrollbars";
|
@forward "controls/scrollbars";
|
||||||
|
@forward "controls/patterns";
|
||||||
|
|
|
||||||
212
website/src/styles/design-system/attributes/_patterns.scss
Normal file
212
website/src/styles/design-system/attributes/_patterns.scss
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
// Rôle : expose les primitives NDS publiques pour surfaces, feedback et overlays.
|
||||||
|
@use "../controls";
|
||||||
|
@use "../surfaces";
|
||||||
|
|
||||||
|
.nds-panel {
|
||||||
|
@include controls.panel-surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-panel[data-variant="interactive"] {
|
||||||
|
@include controls.panel-surface(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-card {
|
||||||
|
@include controls.card-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (corner-shape: scoop) {
|
||||||
|
.nds-card {
|
||||||
|
corner-shape: scoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-card[data-variant="interactive"]:hover,
|
||||||
|
.nds-card[data-variant="interactive"]:focus-within {
|
||||||
|
@include controls.card-pattern-hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-card-cover {
|
||||||
|
min-height: 124px;
|
||||||
|
@include surfaces.card-cover-divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-card-body {
|
||||||
|
padding: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-card-actions {
|
||||||
|
margin-top: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-tool-panel {
|
||||||
|
@include controls.tool-panel-surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-tool-panel[data-variant="interactive"]:hover,
|
||||||
|
.nds-tool-panel[data-variant="tool"]:hover {
|
||||||
|
box-shadow:
|
||||||
|
var(--shadow-sm),
|
||||||
|
0 0 14px rgba(139, 92, 246, 0.14),
|
||||||
|
0 0 24px rgba(196, 181, 253, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-toolbar {
|
||||||
|
@include controls.toolbar-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-section-heading {
|
||||||
|
@include controls.section-heading-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-page-hero {
|
||||||
|
@include controls.page-hero-pattern(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-badge {
|
||||||
|
@include controls.badge-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-badge[data-variant="neutral"] {
|
||||||
|
@include controls.badge-pattern(neutral);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-badge[data-variant="gold"] {
|
||||||
|
@include controls.badge-pattern(gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-badge[data-variant="success"] {
|
||||||
|
@include controls.badge-pattern(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-badge[data-variant="warning"] {
|
||||||
|
@include controls.badge-pattern(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-badge[data-variant="danger"] {
|
||||||
|
@include controls.badge-pattern(danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-chip {
|
||||||
|
@include controls.chip-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-chip:hover,
|
||||||
|
.nds-chip.active,
|
||||||
|
.nds-chip.is-active,
|
||||||
|
.nds-chip[aria-pressed="true"],
|
||||||
|
.nds-chip:has(input:checked) {
|
||||||
|
@include controls.chip-active;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-chip[data-size="sm"] {
|
||||||
|
min-height: 34px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-empty-state {
|
||||||
|
@include controls.empty-state-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-inline-notice {
|
||||||
|
@include controls.inline-notice-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-inline-notice[data-variant="success"] {
|
||||||
|
@include controls.inline-notice-pattern(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-inline-notice[data-variant="warning"] {
|
||||||
|
@include controls.inline-notice-pattern(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-inline-notice[data-variant="danger"] {
|
||||||
|
@include controls.inline-notice-pattern(danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-progress {
|
||||||
|
@include controls.progress-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-progress > span,
|
||||||
|
.nds-progress-bar {
|
||||||
|
@include controls.progress-bar-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-progress[data-variant="success"] > span,
|
||||||
|
.nds-progress[data-variant="success"] .nds-progress-bar {
|
||||||
|
@include controls.progress-bar-pattern(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-progress[data-variant="warning"] > span,
|
||||||
|
.nds-progress[data-variant="warning"] .nds-progress-bar {
|
||||||
|
@include controls.progress-bar-pattern(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-progress[data-variant="danger"] > span,
|
||||||
|
.nds-progress[data-variant="danger"] .nds-progress-bar {
|
||||||
|
@include controls.progress-bar-pattern(danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-filter-panel {
|
||||||
|
@include controls.filter-panel-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-reorder-indicator,
|
||||||
|
.nds-reorder-indicator[data-axis="vertical"][data-placement="before"] {
|
||||||
|
@include controls.reorder-indicator-pattern(vertical, before);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-reorder-indicator[data-axis="vertical"][data-placement="after"] {
|
||||||
|
@include controls.reorder-indicator-pattern(vertical, after);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-reorder-indicator[data-axis="horizontal"][data-placement="before"] {
|
||||||
|
@include controls.reorder-indicator-pattern(horizontal, before);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-reorder-indicator[data-axis="horizontal"][data-placement="after"] {
|
||||||
|
@include controls.reorder-indicator-pattern(horizontal, after);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-modal {
|
||||||
|
@include controls.modal-panel-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-modal-backdrop {
|
||||||
|
@include controls.overlay-backdrop-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-modal-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-drawer-panel {
|
||||||
|
@include controls.drawer-panel-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-drawer-resize-handle {
|
||||||
|
@include controls.drawer-resize-handle-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-popover {
|
||||||
|
@include controls.popover-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-toast-stack {
|
||||||
|
@include controls.toast-stack-pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-toast-stack[data-position="right"] {
|
||||||
|
right: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-toast-stack[data-position="left"] {
|
||||||
|
left: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds-toast {
|
||||||
|
@include controls.toast-pattern;
|
||||||
|
}
|
||||||
373
website/src/styles/design-system/controls/_patterns.scss
Normal file
373
website/src/styles/design-system/controls/_patterns.scss
Normal file
|
|
@ -0,0 +1,373 @@
|
||||||
|
// Rôle : fournit les mixins publics des patterns NDS non limités aux contrôles.
|
||||||
|
@use "../../mixins";
|
||||||
|
@use "../surfaces";
|
||||||
|
|
||||||
|
@mixin panel-surface($interactive: false) {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
background: rgba(16, 20, 38, 0.42);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
|
||||||
|
@include mixins.gold-panel-frame($interactive);
|
||||||
|
|
||||||
|
> * {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin tool-panel-surface {
|
||||||
|
@include panel-surface(true);
|
||||||
|
contain: paint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin card-pattern($fill: rgba(16, 20, 38, 0.88)) {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
@include surfaces.card-surface($fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin card-pattern-hover {
|
||||||
|
@include surfaces.card-surface-hover;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin toolbar-pattern {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin section-heading-pattern {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin page-hero-pattern($compact: false) {
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
|
display: grid;
|
||||||
|
align-items: end;
|
||||||
|
gap: var(--space-6);
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: var(--radius-xl);
|
||||||
|
background:
|
||||||
|
var(--gradient-nebula),
|
||||||
|
radial-gradient(circle at 86% 18%, rgba(246, 196, 83, 0.1), transparent 28%),
|
||||||
|
linear-gradient(135deg, rgba(16, 20, 38, 0.98), rgba(5, 7, 17, 0.96));
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
|
||||||
|
@if $compact {
|
||||||
|
min-height: 180px;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
padding: var(--space-6);
|
||||||
|
} @else {
|
||||||
|
min-height: 360px;
|
||||||
|
padding: var(--space-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 2;
|
||||||
|
padding: 1px;
|
||||||
|
border-radius: inherit;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 100% 0%, rgba(246, 196, 83, 0.5), transparent 26%),
|
||||||
|
linear-gradient(135deg, rgba(246, 196, 83, 0.38), rgba(139, 92, 246, 0.22) 48%, rgba(196, 181, 253, 0.3));
|
||||||
|
@include mixins.border-mask;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, rgba(5, 7, 17, 0.1), rgba(5, 7, 17, 0.52)),
|
||||||
|
radial-gradient(circle at 82% 16%, rgba(246, 196, 83, 0.18), transparent 30%);
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin badge-pattern($tone: primary) {
|
||||||
|
display: inline-flex;
|
||||||
|
min-height: 24px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 3px 9px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
background: var(--color-primary-soft);
|
||||||
|
color: #b69cff;
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1.2;
|
||||||
|
|
||||||
|
@if $tone == gold {
|
||||||
|
border-color: rgba(246, 196, 83, 0.22);
|
||||||
|
background: rgba(246, 196, 83, 0.08);
|
||||||
|
color: var(--color-accent-gold);
|
||||||
|
} @else if $tone == success {
|
||||||
|
border-color: rgba(52, 211, 153, 0.22);
|
||||||
|
background: var(--color-success-soft);
|
||||||
|
color: var(--color-success);
|
||||||
|
} @else if $tone == warning {
|
||||||
|
border-color: rgba(251, 191, 36, 0.24);
|
||||||
|
background: var(--color-warning-soft);
|
||||||
|
color: var(--color-warning);
|
||||||
|
} @else if $tone == danger {
|
||||||
|
border-color: rgba(251, 113, 133, 0.24);
|
||||||
|
background: var(--color-danger-soft);
|
||||||
|
color: var(--color-danger);
|
||||||
|
} @else if $tone == neutral {
|
||||||
|
border-color: rgba(165, 180, 252, 0.12);
|
||||||
|
background: rgba(5, 7, 17, 0.3);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin chip-pattern {
|
||||||
|
display: inline-grid;
|
||||||
|
min-height: 40px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 7px 12px;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
background: rgba(5, 7, 17, 0.32);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
border-color var(--duration-fast) var(--ease-standard),
|
||||||
|
background-color var(--duration-fast) var(--ease-standard),
|
||||||
|
color var(--duration-fast) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin chip-active {
|
||||||
|
border-color: var(--color-primary-border);
|
||||||
|
background: var(--color-primary-soft);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin empty-state-pattern {
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
min-height: 132px;
|
||||||
|
padding: var(--space-5);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
background: rgba(16, 20, 38, 0.88);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin inline-notice-pattern($tone: info) {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border: 1px solid rgba(56, 189, 248, 0.24);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: var(--color-info-soft);
|
||||||
|
color: var(--color-info);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
font-weight: 800;
|
||||||
|
|
||||||
|
@if $tone == danger {
|
||||||
|
border-color: rgba(251, 113, 133, 0.26);
|
||||||
|
background: var(--color-danger-soft);
|
||||||
|
color: var(--color-danger);
|
||||||
|
} @else if $tone == warning {
|
||||||
|
border-color: rgba(251, 191, 36, 0.26);
|
||||||
|
background: var(--color-warning-soft);
|
||||||
|
color: var(--color-warning);
|
||||||
|
} @else if $tone == success {
|
||||||
|
border-color: rgba(52, 211, 153, 0.26);
|
||||||
|
background: var(--color-success-soft);
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin progress-pattern {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid rgba(165, 180, 252, 0.1);
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
background: rgba(5, 7, 17, 0.62);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin progress-bar-pattern($tone: primary) {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: inherit;
|
||||||
|
background: linear-gradient(90deg, var(--color-primary), var(--color-accent-cyan));
|
||||||
|
transition: width var(--duration-normal) var(--ease-standard);
|
||||||
|
|
||||||
|
@if $tone == warning {
|
||||||
|
background: linear-gradient(90deg, var(--color-warning), var(--color-accent-gold));
|
||||||
|
} @else if $tone == danger {
|
||||||
|
background: linear-gradient(90deg, var(--color-danger), var(--color-accent-pink));
|
||||||
|
} @else if $tone == success {
|
||||||
|
background: linear-gradient(90deg, var(--color-success), var(--color-accent-cyan));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin filter-panel-pattern {
|
||||||
|
@include panel-surface;
|
||||||
|
isolation: isolate;
|
||||||
|
padding: var(--space-4);
|
||||||
|
border-radius: var(--radius-xl);
|
||||||
|
background: rgba(16, 20, 38, 0.58);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin reorder-indicator-pattern($axis: vertical, $placement: before) {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
z-index: 3;
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
background: rgba(246, 196, 83, 0.9);
|
||||||
|
box-shadow: 0 0 12px rgba(246, 196, 83, 0.24);
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
@if $axis == horizontal {
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 3px;
|
||||||
|
|
||||||
|
@if $placement == after {
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
} @else {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 3px;
|
||||||
|
|
||||||
|
@if $placement == after {
|
||||||
|
top: auto;
|
||||||
|
bottom: 0;
|
||||||
|
} @else {
|
||||||
|
top: 0;
|
||||||
|
bottom: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin modal-panel-pattern {
|
||||||
|
width: min(520px, calc(100vw - 32px));
|
||||||
|
max-height: calc(100vh - 48px);
|
||||||
|
overflow: auto;
|
||||||
|
padding: var(--space-5);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-xl);
|
||||||
|
background: rgba(16, 20, 38, 0.96);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin overlay-backdrop-pattern {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(2, 3, 9, 0.62);
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin drawer-panel-pattern {
|
||||||
|
overflow: auto;
|
||||||
|
border-left: 0;
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(246, 196, 83, 0.92), rgba(139, 92, 246, 0.64) 48%, rgba(196, 181, 253, 0.72)) left top / 1px 100% no-repeat,
|
||||||
|
rgba(7, 9, 19, 0.82);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
box-shadow:
|
||||||
|
inset 8px 0 18px rgba(139, 92, 246, 0.08),
|
||||||
|
var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin drawer-resize-handle-pattern {
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
cursor: ew-resize;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 5px;
|
||||||
|
width: 4px;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: inherit;
|
||||||
|
background: var(--gradient-brand);
|
||||||
|
opacity: 0.72;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin popover-pattern {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid rgba(165, 180, 252, 0.14);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: rgba(7, 10, 24, 0.96);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin toast-stack-pattern {
|
||||||
|
position: fixed;
|
||||||
|
bottom: var(--space-5);
|
||||||
|
z-index: 360;
|
||||||
|
display: flex;
|
||||||
|
max-width: min(460px, calc(100vw - 32px));
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
gap: var(--space-3);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin toast-pattern {
|
||||||
|
--notification-toast-angle: 0deg;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) 34px;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-3);
|
||||||
|
width: 100%;
|
||||||
|
min-width: min(360px, calc(100vw - 32px));
|
||||||
|
padding: 14px 14px 14px 18px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 100% 0%, rgba(246, 196, 83, 0.12), transparent 38%) padding-box,
|
||||||
|
linear-gradient(135deg, rgba(16, 20, 38, 0.98), rgba(24, 20, 42, 0.98)) padding-box,
|
||||||
|
conic-gradient(from var(--notification-toast-angle), rgba(246, 196, 83, 0.95), rgba(139, 92, 246, 0.8), rgba(56, 189, 248, 0.62), rgba(246, 196, 83, 0.95)) border-box;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
box-shadow:
|
||||||
|
var(--shadow-md),
|
||||||
|
0 0 24px rgba(139, 92, 246, 0.22),
|
||||||
|
0 0 18px rgba(246, 196, 83, 0.1);
|
||||||
|
font-size: var(--font-size-md);
|
||||||
|
font-weight: 900;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// Rôle : regroupe les grilles, cartes, couvertures et badges des pages jeux.
|
// Rôle : regroupe les grilles, cartes, couvertures et badges des pages jeux.
|
||||||
|
@use "../design-system/controls";
|
||||||
|
|
||||||
.game-grid {
|
.game-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
@ -53,14 +54,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
display: inline-flex;
|
@include controls.badge-pattern;
|
||||||
min-height: 24px;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
border-radius: var(--radius-pill);
|
|
||||||
background: var(--color-primary-soft);
|
|
||||||
color: #b69cff;
|
|
||||||
font-size: var(--font-size-xs);
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
padding: 3px 9px;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
// Rôle : regroupe le layout, les filtres et les barres de contrôle des pages jeux.
|
// Rôle : regroupe le layout, les filtres et les barres de contrôle des pages jeux.
|
||||||
@use "../mixins";
|
|
||||||
|
|
||||||
.game-layout {
|
.game-layout {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(240px, 300px) minmax(0, 1fr);
|
grid-template-columns: minmax(240px, 300px) minmax(0, 1fr);
|
||||||
|
|
@ -14,48 +12,6 @@
|
||||||
top: 92px;
|
top: 92px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-panel {
|
|
||||||
position: relative;
|
|
||||||
isolation: isolate;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: var(--radius-xl);
|
|
||||||
background: rgba(16, 20, 38, 0.58);
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
padding: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-panel::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
z-index: -1;
|
|
||||||
background:
|
|
||||||
radial-gradient(circle at 92% 12%, rgba(246, 196, 83, 0.009), transparent 34%),
|
|
||||||
radial-gradient(circle at 0% 100%, rgba(139, 92, 246, 0.011), transparent 42%);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-panel::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
z-index: 0;
|
|
||||||
padding: 1px;
|
|
||||||
border-radius: inherit;
|
|
||||||
background:
|
|
||||||
radial-gradient(circle at 100% 0%, rgba(246, 196, 83, 0.42), transparent 34%),
|
|
||||||
linear-gradient(135deg, rgba(246, 196, 83, 0.42), rgba(139, 92, 246, 0.32) 54%, rgba(196, 181, 253, 0.24));
|
|
||||||
@include mixins.border-mask;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-panel > * {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-panel-head,
|
.filter-panel-head,
|
||||||
.filter-logic {
|
.filter-logic {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -111,22 +67,7 @@
|
||||||
.filter-chip {
|
.filter-chip {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 18px 22px 1fr;
|
grid-template-columns: 18px 22px 1fr;
|
||||||
min-height: 40px;
|
|
||||||
align-items: center;
|
|
||||||
column-gap: 6px;
|
column-gap: 6px;
|
||||||
padding: 7px 10px 7px 13px;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-pill);
|
|
||||||
background: rgba(5, 7, 17, 0.32);
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-chip:hover,
|
|
||||||
.filter-chip.active {
|
|
||||||
border-color: var(--color-primary-border);
|
|
||||||
background: var(--color-primary-soft);
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-chip img {
|
.filter-chip img {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// Rôle : regroupe les fondations partagées des pages home et sections éditoriales.
|
// Rôle : regroupe les fondations partagées des pages home et sections éditoriales.
|
||||||
@use "../mixins";
|
@use "../mixins";
|
||||||
|
@use "../design-system/controls";
|
||||||
|
|
||||||
.hero,
|
.hero,
|
||||||
.game-hero,
|
.game-hero,
|
||||||
|
|
@ -133,9 +134,7 @@ span {
|
||||||
.actions,
|
.actions,
|
||||||
.card-actions,
|
.card-actions,
|
||||||
.toolbar {
|
.toolbar {
|
||||||
display: flex;
|
@include controls.toolbar-pattern;
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-library-button {
|
.hero-library-button {
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,4 @@
|
||||||
// Rôle : gère les panneaux narratifs Nebula et les dialogues home.
|
// Rôle : gère les panneaux narratifs Nebula et les dialogues home.
|
||||||
@use "../mixins";
|
|
||||||
|
|
||||||
.nebula-panel {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
background: rgba(16, 20, 38, 0.42);
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
|
|
||||||
@include mixins.gold-panel-frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nebula-panel > * {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.origin-section {
|
.origin-section {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(360px, 0.78fr);
|
grid-template-columns: minmax(0, 1fr) minmax(360px, 0.78fr);
|
||||||
|
|
@ -75,4 +57,3 @@
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Rôle : stylise les indicateurs de synthèse de la librairie.
|
// Rôle : stylise les indicateurs de synthèse de la librairie.
|
||||||
.library-summary {
|
.library-summary {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
|
||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
padding: var(--space-5);
|
padding: var(--space-5);
|
||||||
}
|
}
|
||||||
|
|
@ -45,3 +45,10 @@
|
||||||
height: 18px;
|
height: 18px;
|
||||||
background: currentColor;
|
background: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 520px) {
|
||||||
|
.library-summary {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
.drawer-backdrop {
|
.drawer-backdrop {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: var(--drawer-top-offset, 68px) 0 0 0;
|
||||||
z-index: 40;
|
z-index: 40;
|
||||||
background: rgba(2, 3, 9, 0.24);
|
background: rgba(2, 3, 9, 0.24);
|
||||||
backdrop-filter: none;
|
backdrop-filter: none;
|
||||||
|
|
@ -13,26 +13,11 @@
|
||||||
|
|
||||||
.drawer-panel {
|
.drawer-panel {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0 0 0 auto;
|
inset: var(--drawer-top-offset, 68px) 0 0 auto;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
width: min(720px, 94vw);
|
width: min(720px, 94vw);
|
||||||
min-width: 360px;
|
min-width: 360px;
|
||||||
max-width: 94vw;
|
max-width: 94vw;
|
||||||
overflow: auto;
|
|
||||||
border-left: 0;
|
|
||||||
background:
|
|
||||||
linear-gradient(
|
|
||||||
180deg,
|
|
||||||
rgba(246, 196, 83, 0.92),
|
|
||||||
rgba(139, 92, 246, 0.64) 48%,
|
|
||||||
rgba(196, 181, 253, 0.72)
|
|
||||||
) left top / 1px 100% no-repeat,
|
|
||||||
rgba(7, 9, 19, 0.82);
|
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
box-shadow:
|
|
||||||
inset 8px 0 18px rgba(139, 92, 246, 0.08),
|
|
||||||
var(--shadow-lg);
|
|
||||||
padding: var(--space-5);
|
padding: var(--space-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,28 +35,14 @@
|
||||||
|
|
||||||
.drawer-resize-handle {
|
.drawer-resize-handle {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50%;
|
top: calc(var(--drawer-top-offset, 68px) + ((100vh - var(--drawer-top-offset, 68px)) / 2));
|
||||||
right: calc(var(--drawer-panel-width, min(720px, 94vw)) - 18px);
|
right: calc(var(--drawer-panel-width, min(720px, 94vw)) - 18px);
|
||||||
z-index: 62;
|
z-index: 62;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 72px;
|
height: 72px;
|
||||||
border-radius: var(--radius-pill);
|
|
||||||
cursor: ew-resize;
|
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer-resize-handle::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 5px;
|
|
||||||
width: 4px;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: inherit;
|
|
||||||
background: var(--gradient-brand);
|
|
||||||
opacity: 0.72;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.is-resizing-drawer,
|
body.is-resizing-drawer,
|
||||||
body.is-resizing-drawer * {
|
body.is-resizing-drawer * {
|
||||||
cursor: ew-resize !important;
|
cursor: ew-resize !important;
|
||||||
|
|
|
||||||
|
|
@ -19,23 +19,8 @@
|
||||||
overscroll-behavior: contain;
|
overscroll-behavior: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-backdrop {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
background: rgba(2, 3, 9, 0.62);
|
|
||||||
backdrop-filter: blur(6px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.confirm-modal {
|
.confirm-modal {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: min(520px, calc(100vw - 32px));
|
|
||||||
max-height: calc(100vh - 48px);
|
|
||||||
overflow: auto;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-xl);
|
|
||||||
background: rgba(16, 20, 38, 0.96);
|
|
||||||
box-shadow: var(--shadow-lg);
|
|
||||||
padding: var(--space-5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-modal h2 {
|
.confirm-modal h2 {
|
||||||
|
|
@ -46,12 +31,6 @@
|
||||||
margin-bottom: var(--space-6);
|
margin-bottom: var(--space-6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-modal footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property --notification-toast-angle {
|
@property --notification-toast-angle {
|
||||||
syntax: "<angle>";
|
syntax: "<angle>";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,5 @@
|
||||||
// Rôle : gère les toasts de notification et leurs animations.
|
// Rôle : gère les toasts de notification et leurs animations.
|
||||||
.notification-toast-stack {
|
|
||||||
position: fixed;
|
|
||||||
bottom: var(--space-5);
|
|
||||||
z-index: 360;
|
|
||||||
display: flex;
|
|
||||||
max-width: min(460px, calc(100vw - 32px));
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
gap: var(--space-3);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-toast-stack.is-right {
|
|
||||||
right: var(--space-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-toast-stack.is-left {
|
|
||||||
left: var(--space-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-toast {
|
.notification-toast {
|
||||||
--notification-toast-angle: 0deg;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: minmax(0, 1fr) 34px;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
width: 100%;
|
|
||||||
min-width: min(360px, calc(100vw - 32px));
|
|
||||||
padding: 14px 14px 14px 18px;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
background:
|
|
||||||
radial-gradient(circle at 100% 0%, rgba(246, 196, 83, 0.12), transparent 38%) padding-box,
|
|
||||||
linear-gradient(135deg, rgba(16, 20, 38, 0.98), rgba(24, 20, 42, 0.98)) padding-box,
|
|
||||||
conic-gradient(
|
|
||||||
from var(--notification-toast-angle),
|
|
||||||
rgba(246, 196, 83, 0.95),
|
|
||||||
rgba(139, 92, 246, 0.8),
|
|
||||||
rgba(56, 189, 248, 0.62),
|
|
||||||
rgba(246, 196, 83, 0.95)
|
|
||||||
) border-box;
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
box-shadow:
|
|
||||||
var(--shadow-md),
|
|
||||||
0 0 24px rgba(139, 92, 246, 0.22),
|
|
||||||
0 0 18px rgba(246, 196, 83, 0.1);
|
|
||||||
font-size: var(--font-size-md);
|
|
||||||
font-weight: 900;
|
|
||||||
pointer-events: auto;
|
|
||||||
animation:
|
animation:
|
||||||
notification-toast-enter-right 320ms var(--ease-standard),
|
notification-toast-enter-right 320ms var(--ease-standard),
|
||||||
notification-toast-border-spin 2.4s linear infinite,
|
notification-toast-border-spin 2.4s linear infinite,
|
||||||
|
|
@ -64,24 +17,6 @@
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-toast button {
|
|
||||||
display: inline-grid;
|
|
||||||
width: 34px;
|
|
||||||
min-width: 34px;
|
|
||||||
min-height: 34px;
|
|
||||||
place-items: center;
|
|
||||||
padding: 0;
|
|
||||||
border-color: rgba(165, 180, 252, 0.12);
|
|
||||||
background: rgba(5, 7, 17, 0.28);
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-toast button:hover {
|
|
||||||
border-color: rgba(196, 181, 253, 0.26);
|
|
||||||
background: rgba(21, 26, 48, 0.72);
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-toast button .ui-icon {
|
.notification-toast button .ui-icon {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: var(--space-3);
|
gap: var(--space-3);
|
||||||
padding: var(--space-4);
|
padding: var(--space-2);
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// Rôle : regroupe les colonnes, modules et zones de dépôt des toolboxes.
|
// Rôle : regroupe les colonnes, modules et zones de dépôt des toolboxes.
|
||||||
@use "../../mixins";
|
@use "../../mixins";
|
||||||
|
@use "../../design-system/controls";
|
||||||
|
|
||||||
.modules {
|
.modules {
|
||||||
align-items: start;
|
align-items: start;
|
||||||
|
|
@ -50,19 +51,9 @@
|
||||||
.module {
|
.module {
|
||||||
--reorder-drop-shadow: var(--shadow-sm);
|
--reorder-drop-shadow: var(--shadow-sm);
|
||||||
|
|
||||||
position: relative;
|
|
||||||
contain: paint;
|
|
||||||
overflow: hidden;
|
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
background: rgba(16, 20, 38, 0.42);
|
background: rgba(16, 20, 38, 0.42);
|
||||||
box-shadow: var(--shadow-sm);
|
@include controls.tool-panel-surface;
|
||||||
|
|
||||||
@include mixins.gold-panel-frame(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
.module > * {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.module:hover {
|
.module:hover {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue