@sia-ui/cli 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @sia-ui/cli
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Aligne tous les packages publics SIA UI sur la version 0.4.0 afin de fournir
8
+ un ensemble cohérent à installer, documenter et maintenir.
9
+
3
10
  ## 0.3.2
4
11
 
5
12
  ### Patch Changes
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
- <p align="center"><img src="https://beatjo.github.io/sia-ui-site/brand/sia-ui-logo.png" alt="SIA UI" width="320"></p>
2
-
1
+ <p align="center"><img src="https://beatjo.github.io/sia-ui-site/brand/sia-ui-logo.png" alt="SIA UI" width="320"></p>
2
+
3
3
  # @sia-ui/cli
4
4
 
5
5
  Installer les composants SIA UI dans une application, comme shadcn/ui.
@@ -51,22 +51,42 @@ add src/components/DataTable/styles.css
51
51
 
52
52
  Une chose s'écrit au niveau le plus bas où elle a du sens, et une seule fois.
53
53
  Voir [la documentation](https://beatjo.github.io/sia-ui-site).
54
-
55
- ## Contribuer
56
-
57
- Le code source principal de SIA UI est privé pour le moment. Les retours publics passent par le dépôt `BeatJo/sia-ui-site` : bugs, demandes de composants, corrections de documentation et questions d'usage.
58
-
59
- - Signaler un bug : https://github.com/BeatJo/sia-ui-site/issues
60
- - Lire la documentation : https://beatjo.github.io/sia-ui-site/
61
- - Voir la démonstration : https://beatjo.github.io/sia-ui-site/preview/
62
-
63
- Les contributions directes au code des packages se font sur invitation dans le dépôt source privé.
64
- ## Journal des changements
65
-
66
- Le contenu ci-dessous reprend intégralement `CHANGELOG.md` pour rester visible sur npm.
67
-
54
+
55
+ ## Contribuer
56
+
57
+ Le code source principal de SIA UI est privé pour le moment. Les retours publics passent par le dépôt `BeatJo/sia-ui-site` : bugs, demandes de composants, corrections de documentation et questions d'usage.
58
+
59
+ - Signaler un bug : https://github.com/BeatJo/sia-ui-site/issues
60
+ - Lire la documentation : https://beatjo.github.io/sia-ui-site/
61
+ - Voir la démonstration : https://beatjo.github.io/sia-ui-site/preview/
62
+
63
+ Les contributions directes au code des packages se font sur invitation dans le dépôt source privé.
64
+ ## Journal des changements
65
+
66
+ Le contenu ci-dessous reprend intégralement `CHANGELOG.md` pour rester visible sur npm.
67
+
68
+ <!-- sia:changelog:start -->
68
69
  # @sia-ui/cli
69
70
 
71
+ ## 0.4.0
72
+
73
+ ### Minor Changes
74
+
75
+ - Aligne tous les packages publics SIA UI sur la version 0.4.0 afin de fournir
76
+ un ensemble cohérent à installer, documenter et maintenir.
77
+
78
+ ## 0.3.2
79
+
80
+ ### Patch Changes
81
+
82
+ - Ajoute l’identité visuelle officielle de SIA UI et améliore la documentation publique, les changelogs et les ressources de contribution.
83
+
84
+ ## 0.3.1
85
+
86
+ ### Patch Changes
87
+
88
+ - Expose public contribution flow on npm
89
+
70
90
  ## 0.3.0
71
91
 
72
92
  ### Minor Changes
@@ -226,8 +246,9 @@ Le contenu ci-dessous reprend intégralement `CHANGELOG.md` pour rester visible
226
246
 
227
247
  ### Patch Changes
228
248
 
229
- - Include component styles when installing registry components
230
-
231
- ## Licence
249
+ - Include component styles when installing registry components
250
+ <!-- sia:changelog:end -->
251
+
252
+ ## Licence
232
253
 
233
254
  MIT
@@ -3,6 +3,8 @@ import {
3
3
  useMemo,
4
4
  useState,
5
5
  type CSSProperties,
6
+ type ElementType,
7
+ type HTMLAttributes,
6
8
  type ReactNode,
7
9
  } from "react";
8
10
  import { cn } from "@sia-ui/utils";
@@ -53,6 +55,87 @@ export type AppShellMobileNav = "drawer" | "tabs" | "both";
53
55
 
54
56
  export type AppShellContentWidth = "wide" | "narrow" | "full";
55
57
 
58
+ export interface AppShellBrandProps extends HTMLAttributes<HTMLDivElement> {
59
+ /** Symbole conservé lorsque la navigation devient un rail. */
60
+ icon: ReactNode;
61
+ /** Nom du produit, masqué automatiquement dans le rail. */
62
+ name: ReactNode;
63
+ /** Ligne secondaire facultative, masquée avec le nom. */
64
+ description?: ReactNode;
65
+ }
66
+
67
+ /**
68
+ * Identité de produit adaptée au rail de l'`AppShell`.
69
+ *
70
+ * L'application fournit le symbole et le texte une seule fois. Le composant
71
+ * masque sa copie dès que son parent reçoit `data-collapsed`.
72
+ */
73
+ export function AppShellBrand({
74
+ icon,
75
+ name,
76
+ description,
77
+ className,
78
+ ...props
79
+ }: AppShellBrandProps) {
80
+ return (
81
+ <div className={cn("sia-shell-brand", className)} {...props}>
82
+ <span className="sia-shell-brand__icon" aria-hidden="true">
83
+ {icon}
84
+ </span>
85
+ <span className="sia-shell-brand__copy">
86
+ <span className="sia-shell-brand__name">{name}</span>
87
+ {description !== undefined && (
88
+ <span className="sia-shell-brand__description">{description}</span>
89
+ )}
90
+ </span>
91
+ </div>
92
+ );
93
+ }
94
+
95
+ export interface AppShellRailItemProps extends HTMLAttributes<HTMLElement> {
96
+ /** Élément rendu : contenu simple, action ou lien. */
97
+ as?: "div" | "button" | "a";
98
+ /** Symbole toujours visible dans le rail. */
99
+ icon: ReactNode;
100
+ /** Libellé masqué visuellement dans le rail et conservé comme nom accessible. */
101
+ label: string;
102
+ href?: string;
103
+ type?: "button" | "submit" | "reset";
104
+ disabled?: boolean;
105
+ }
106
+
107
+ /** Contenu icône + libellé qui s'adapte automatiquement au rail. */
108
+ export function AppShellRailItem({
109
+ as = "div",
110
+ icon,
111
+ label,
112
+ className,
113
+ href,
114
+ type = "button",
115
+ disabled,
116
+ ...props
117
+ }: AppShellRailItemProps) {
118
+ const Component: ElementType = as;
119
+ const elementProps = {
120
+ ...props,
121
+ ...(as === "a" && href ? { href } : {}),
122
+ ...(as === "button" ? { type, disabled } : {}),
123
+ };
124
+
125
+ return (
126
+ <Component
127
+ className={cn("sia-shell-rail-item", className)}
128
+ aria-label={props["aria-label"] ?? (as !== "div" ? label : undefined)}
129
+ {...elementProps}
130
+ >
131
+ <span className="sia-shell-rail-item__icon" aria-hidden="true">
132
+ {icon}
133
+ </span>
134
+ <span className="sia-shell-rail-item__label">{label}</span>
135
+ </Component>
136
+ );
137
+ }
138
+
56
139
  export interface AppShellProps {
57
140
  nav: SidebarEntry[];
58
141
  children: ReactNode;
@@ -97,8 +180,8 @@ export interface AppShellProps {
97
180
  /**
98
181
  * En tête de barre latérale : logo, nom du produit, sélecteur d'entité.
99
182
  *
100
- * Reçoit `data-collapsed` en mode rail, comme `sidebarFooter` : sans quoi
101
- * un nom de produit écrit en toutes lettres déborde des quatre rem du rail.
183
+ * `AppShellBrand` masque son texte automatiquement dans le rail. Un contenu
184
+ * entièrement personnalisé reçoit toujours `data-collapsed` sur son parent.
102
185
  */
103
186
  brand?: ReactNode;
104
187
 
@@ -107,8 +190,8 @@ export interface AppShellProps {
107
190
  *
108
191
  * C'est la place de la déconnexion et des réglages du compte — ce qui sort
109
192
  * de l'application, par opposition à l'en-tête, réservé à ce qui agit sur
110
- * l'écran courant. Reçoit `data-collapsed` en mode rail, de quoi masquer un
111
- * libellé sans changer de composant.
193
+ * l'écran courant. `AppShellRailItem` masque son libellé automatiquement ;
194
+ * un contenu personnalisé peut encore lire `data-collapsed` sur son parent.
112
195
  */
113
196
  sidebarFooter?: ReactNode;
114
197
 
@@ -306,7 +389,9 @@ export function AppShell({
306
389
  {/* Sur petit écran, la barre latérale n'a pas la place de rester
307
390
  ouverte : elle devient un tiroir. Sans lui, l'arborescence complète
308
391
  ne serait atteignable que par « Plus », ce qui ne se devine pas. */}
309
- {!compact && !masquee && <aside className="sia-shell__aside">{barre}</aside>}
392
+ {!compact && !masquee && (
393
+ <aside className="sia-shell__aside">{barre}</aside>
394
+ )}
310
395
 
311
396
  <div className="sia-shell__main">
312
397
  {(header || collapsible !== false || (compact && montrerTiroir)) && (
@@ -348,7 +433,12 @@ export function AppShell({
348
433
  </header>
349
434
  )}
350
435
 
351
- <main className={cn("sia-shell__content", `sia-shell__content--${contentWidth}`)}>
436
+ <main
437
+ className={cn(
438
+ "sia-shell__content",
439
+ `sia-shell__content--${contentWidth}`,
440
+ )}
441
+ >
352
442
  {page && <div className="sia-shell__page">{page}</div>}
353
443
  {children}
354
444
  </main>
@@ -374,7 +464,11 @@ export function AppShell({
374
464
  size="sm"
375
465
  className="sia-shell__drawer"
376
466
  >
377
- {barre}
467
+ <Drawer.Header>
468
+ <Drawer.Title>Navigation</Drawer.Title>
469
+ <Drawer.Close />
470
+ </Drawer.Header>
471
+ <Drawer.Body>{barre}</Drawer.Body>
378
472
  </Drawer>
379
473
  )}
380
474
  </div>
@@ -57,6 +57,92 @@
57
57
  padding: .5rem;
58
58
  }
59
59
 
60
+ /* Les contenus standards de la barre connaissent le rail. L'application
61
+ fournit une icône et un texte une seule fois ; AppShell porte l'état sur le
62
+ parent, et ces primitives prennent en charge la présentation. */
63
+ .sia-shell-brand,
64
+ .sia-shell-rail-item {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: .625rem;
68
+ min-width: 0;
69
+ }
70
+
71
+ .sia-shell-brand { padding: .25rem; }
72
+
73
+ .sia-shell-brand__icon {
74
+ display: grid;
75
+ place-items: center;
76
+ flex: none;
77
+ width: 2.75rem;
78
+ height: 2.75rem;
79
+ }
80
+
81
+ .sia-shell-brand__icon > img,
82
+ .sia-shell-brand__icon > svg {
83
+ max-width: 100%;
84
+ max-height: 100%;
85
+ object-fit: contain;
86
+ }
87
+
88
+ .sia-shell-brand__copy { min-width: 0; }
89
+ .sia-shell-brand__name,
90
+ .sia-shell-brand__description {
91
+ display: block;
92
+ overflow: hidden;
93
+ text-overflow: ellipsis;
94
+ white-space: nowrap;
95
+ }
96
+ .sia-shell-brand__name { font-weight: 700; }
97
+ .sia-shell-brand__description {
98
+ margin-top: .1rem;
99
+ color: var(--sia-muted);
100
+ font-size: .72rem;
101
+ }
102
+
103
+ .sia-shell-rail-item {
104
+ width: 100%;
105
+ padding: .375rem;
106
+ border: 0;
107
+ border-radius: var(--sia-radius);
108
+ background: transparent;
109
+ color: var(--sia-muted);
110
+ font: inherit;
111
+ font-size: .75rem;
112
+ text-decoration: none;
113
+ }
114
+ .sia-shell-rail-item:is(button, a) { cursor: pointer; }
115
+ .sia-shell-rail-item:is(button, a):hover {
116
+ background: var(--sia-surface-raised, var(--sia-surface));
117
+ color: var(--sia-foreground);
118
+ }
119
+ .sia-shell-rail-item:disabled { opacity: .55; cursor: not-allowed; }
120
+ .sia-shell-rail-item:is(button, a):focus-visible {
121
+ outline: 2px solid var(--sia-primary);
122
+ outline-offset: 2px;
123
+ }
124
+ .sia-shell-rail-item__icon {
125
+ display: grid;
126
+ place-items: center;
127
+ flex: none;
128
+ width: 1.5rem;
129
+ height: 1.5rem;
130
+ }
131
+ .sia-shell-rail-item__label {
132
+ min-width: 0;
133
+ overflow: hidden;
134
+ text-overflow: ellipsis;
135
+ white-space: nowrap;
136
+ }
137
+
138
+ [data-collapsed] .sia-shell-brand,
139
+ [data-collapsed] .sia-shell-rail-item {
140
+ justify-content: center;
141
+ gap: 0;
142
+ }
143
+ [data-collapsed] .sia-shell-brand__copy,
144
+ [data-collapsed] .sia-shell-rail-item__label { display: none; }
145
+
60
146
  .sia-shell__main {
61
147
  display: flex;
62
148
  flex-direction: column;
@@ -154,6 +240,7 @@
154
240
  padding-bottom: calc(var(--sia-shell-tabs) + env(safe-area-inset-bottom, 0px));
155
241
  }
156
242
 
243
+ .sia-shell__drawer .sia-drawer-body { flex:1; min-height:0; padding:.5rem; overflow:hidden; }
157
244
  .sia-shell__drawer .sia-sidebar { height: 100%; }
158
245
 
159
246
  /* ── La barre d'onglets ─────────────────────────────────────────────────── */
@@ -12,6 +12,7 @@ import "./styles.css";
12
12
  import { useOutsideClick } from "@sia-ui/react";
13
13
  import { DrawerContext } from "./context";
14
14
  import { overlayStack, useDisclosureState } from "@sia-ui/headless";
15
+ import { XIcon } from "../Icons";
15
16
  /** Ce que la réf impérative expose — ouvrir un tiroir depuis l'extérieur. */
16
17
  export type DrawerRef = {
17
18
  open: () => void;
@@ -65,9 +66,10 @@ export const Drawer = forwardRef<DrawerRef, IDrawerProps>(
65
66
  closeOnOutsideClick = true,
66
67
  closeOnEsc = true,
67
68
  children,
69
+ className,
68
70
  ...rest
69
71
  },
70
- ref
72
+ ref,
71
73
  ) => {
72
74
  // Ouverture, fermeture et phase de sortie vivent dans @sia-ui/headless :
73
75
  // rien là-dedans n'est propre au DOM, et le pendant mobile réutilisera la
@@ -96,7 +98,7 @@ export const Drawer = forwardRef<DrawerRef, IDrawerProps>(
96
98
  toggle: () => (isOpen ? doClose() : doOpen()),
97
99
  isOpen: () => isOpen,
98
100
  }),
99
- [isOpen, open]
101
+ [isOpen, open],
100
102
  );
101
103
 
102
104
  /* Overlay stack */
@@ -150,6 +152,7 @@ export const Drawer = forwardRef<DrawerRef, IDrawerProps>(
150
152
  <DrawerContext.Provider value={{ close: doClose }}>
151
153
  <div
152
154
  className="sia-drawer-root"
155
+ data-state={state}
153
156
  style={{ zIndex: zIndexRef.current ?? undefined }}
154
157
  >
155
158
  <div className="sia-drawer-overlay" />
@@ -163,7 +166,8 @@ export const Drawer = forwardRef<DrawerRef, IDrawerProps>(
163
166
  "sia-drawer",
164
167
  `sia-drawer-${position}`,
165
168
  `sia-drawer-${size}`,
166
- state === "closing" && "is-closing"
169
+ state === "closing" && "is-closing",
170
+ className,
167
171
  )}
168
172
  {...rest}
169
173
  >
@@ -173,7 +177,7 @@ export const Drawer = forwardRef<DrawerRef, IDrawerProps>(
173
177
  </DrawerContext.Provider>
174
178
  </Portal>
175
179
  );
176
- }
180
+ },
177
181
  ) as DrawerComponent;
178
182
 
179
183
  Drawer.displayName = "SiaDrawer";
@@ -205,7 +209,7 @@ export function DrawerClose() {
205
209
  onClick={ctx.close}
206
210
  aria-label="Fermer"
207
211
  >
208
- ✕
212
+ <XIcon aria-hidden="true" />
209
213
  </button>
210
214
  );
211
215
  }
@@ -7,17 +7,24 @@
7
7
  .sia-drawer-overlay {
8
8
  position: absolute;
9
9
  inset: 0;
10
- background: rgba(0, 0, 0, 0.4);
11
- animation: fadeIn var(--sia-duration-base) var(--sia-easing-entrance);
10
+ z-index: 0;
11
+ background: rgb(2 6 23 / .52);
12
+ animation: sia-drawer-fade-in var(--sia-duration-base) var(--sia-easing-entrance) both;
12
13
  }
14
+ .sia-drawer-root[data-state="closing"] .sia-drawer-overlay { animation:sia-drawer-fade-out var(--sia-duration-base) var(--sia-easing-exit) both; }
13
15
 
14
16
  .sia-drawer {
15
17
  position: absolute;
16
- background: var(--sia-color-white);
18
+ z-index: 1;
19
+ color: var(--sia-foreground, #0f172a);
20
+ background: var(--sia-background, #fff);
21
+ border: 1px solid var(--sia-border, #e2e8f0);
17
22
  display: flex;
18
23
  flex-direction: column;
19
- width: var(--sia-drawer-width);
20
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
24
+ width: min(var(--sia-drawer-width), calc(100vw - 2rem));
25
+ overflow: hidden;
26
+ isolation: isolate;
27
+ box-shadow: 0 20px 50px rgb(2 6 23 / .24);
21
28
  will-change: transform;
22
29
  }
23
30
 
@@ -27,7 +34,7 @@
27
34
  right: 0;
28
35
  top: 0;
29
36
  height: 100%;
30
- animation: slideInRight var(--sia-duration-slow) var(--sia-easing-entrance);
37
+ animation: sia-drawer-in-right var(--sia-duration-slow) var(--sia-easing-entrance) both;
31
38
  }
32
39
 
33
40
  /* LEFT */
@@ -35,7 +42,7 @@
35
42
  left: 0;
36
43
  top: 0;
37
44
  height: 100%;
38
- animation: slideInLeft var(--sia-duration-slow) var(--sia-easing-entrance);
45
+ animation: sia-drawer-in-left var(--sia-duration-slow) var(--sia-easing-entrance) both;
39
46
  }
40
47
 
41
48
  /* TOP */
@@ -43,9 +50,11 @@
43
50
  top: 0;
44
51
  left: 0;
45
52
  right: 0;
46
- height: auto;
53
+ width: 100%;
54
+ max-width: 100%;
55
+ max-height: min(80vh, 48rem);
47
56
  --sia-drawer-width: 100% !important;
48
- animation: slideInTop var(--sia-duration-slow) var(--sia-easing-entrance);
57
+ animation: sia-drawer-in-top var(--sia-duration-slow) var(--sia-easing-entrance) both;
49
58
  }
50
59
 
51
60
  /* BOTTOM */
@@ -53,9 +62,11 @@
53
62
  bottom: 0;
54
63
  left: 0;
55
64
  right: 0;
56
- height: auto;
65
+ width: 100%;
66
+ max-width: 100%;
67
+ max-height: min(80vh, 48rem);
57
68
  --sia-drawer-width: 100% !important;
58
- animation: slideInBottom var(--sia-duration-slow) var(--sia-easing-entrance);
69
+ animation: sia-drawer-in-bottom var(--sia-duration-slow) var(--sia-easing-entrance) both;
59
70
  }
60
71
 
61
72
 
@@ -68,14 +79,15 @@
68
79
  .sia-drawer-header,
69
80
  .sia-drawer-footer {
70
81
  padding: 16px;
71
- border-bottom: 1px solid var(--sia-color-border);
82
+ border-bottom: 1px solid var(--sia-border, #e2e8f0);
72
83
  display: flex;
73
84
  align-items: center;
74
85
  justify-content: space-between;
75
86
  }
76
87
 
77
88
  .sia-drawer-footer {
78
- border-top: 1px solid var(--sia-color-border);
89
+ border-top: 1px solid var(--sia-border, #e2e8f0);
90
+ border-bottom: 0;
79
91
  margin-top: auto;
80
92
  }
81
93
 
@@ -85,67 +97,80 @@
85
97
  }
86
98
 
87
99
  .sia-drawer-title {
100
+ margin: 0;
88
101
  font-size: 1.1rem;
89
102
  font-weight: 600;
90
103
  }
91
104
 
92
105
  .sia-drawer-close {
106
+ display: grid;
107
+ place-items: center;
108
+ flex: none;
109
+ width: 2.25rem;
110
+ height: 2.25rem;
93
111
  margin-left: auto;
94
- background: none;
95
- border: none;
112
+ padding: 0;
113
+ color: var(--sia-muted, #64748b);
114
+ background: transparent;
115
+ border: 0;
116
+ border-radius: var(--sia-radius, .375rem);
96
117
  cursor: pointer;
97
- font-size: 1.2rem;
98
118
  }
119
+ .sia-drawer-close:hover { color:var(--sia-foreground,#0f172a); background:var(--sia-surface,#f8fafc); }
120
+ .sia-drawer-close:focus-visible { outline:2px solid var(--sia-primary,#2563eb); outline-offset:2px; }
121
+ .sia-drawer-close svg { width:1.125rem; height:1.125rem; }
99
122
 
100
123
  .sia-drawer-right.is-closing {
101
- animation: slideOutRight var(--sia-duration-base) var(--sia-easing-exit) forwards;
124
+ animation: sia-drawer-out-right var(--sia-duration-base) var(--sia-easing-exit) both;
102
125
  }
103
126
 
104
127
  .sia-drawer-left.is-closing {
105
- animation: slideOutLeft var(--sia-duration-base) var(--sia-easing-exit) forwards;
128
+ animation: sia-drawer-out-left var(--sia-duration-base) var(--sia-easing-exit) both;
106
129
  }
107
130
 
108
131
  .sia-drawer-top.is-closing {
109
- animation: slideOutTop var(--sia-duration-base) var(--sia-easing-exit) forwards;
132
+ animation: sia-drawer-out-top var(--sia-duration-base) var(--sia-easing-exit) both;
110
133
  }
111
134
 
112
135
  .sia-drawer-bottom.is-closing {
113
- animation: slideOutBottom var(--sia-duration-base) var(--sia-easing-exit) forwards;
136
+ animation: sia-drawer-out-bottom var(--sia-duration-base) var(--sia-easing-exit) both;
114
137
  }
115
138
 
116
139
  /* Animations */
117
- @keyframes slideInRight {
140
+ @keyframes sia-drawer-fade-in { from { opacity:0; } to { opacity:1; } }
141
+ @keyframes sia-drawer-fade-out { to { opacity:0; } }
142
+ @keyframes sia-drawer-in-right {
118
143
  from { transform: translateX(100%); }
119
144
  to { transform: translateX(0); }
120
145
  }
121
146
 
122
- @keyframes slideInLeft {
147
+ @keyframes sia-drawer-in-left {
123
148
  from { transform: translateX(-100%); }
124
149
  to { transform: translateX(0); }
125
150
  }
126
151
 
127
- @keyframes slideInTop {
152
+ @keyframes sia-drawer-in-top {
128
153
  from { transform: translateY(-100%); }
129
154
  to { transform: translateY(0); }
130
155
  }
131
156
 
132
- @keyframes slideInBottom {
157
+ @keyframes sia-drawer-in-bottom {
133
158
  from { transform: translateY(100%); }
134
159
  to { transform: translateY(0); }
135
160
  }
136
161
 
137
- @keyframes slideOutRight {
162
+ @keyframes sia-drawer-out-right {
138
163
  to { transform: translateX(100%); }
139
164
  }
140
165
 
141
- @keyframes slideOutLeft {
166
+ @keyframes sia-drawer-out-left {
142
167
  to { transform: translateX(-100%); }
143
168
  }
144
169
 
145
- @keyframes slideOutTop {
170
+ @keyframes sia-drawer-out-top {
146
171
  to { transform: translateY(-100%); }
147
172
  }
148
173
 
149
- @keyframes slideOutBottom {
174
+ @keyframes sia-drawer-out-bottom {
150
175
  to { transform: translateY(100%); }
151
176
  }
@@ -182,6 +182,15 @@ export function InfoIcon(props: IconProps) {
182
182
  </IconBase>
183
183
  );
184
184
  }
185
+
186
+ export function SettingsIcon(props: IconProps) {
187
+ return (
188
+ <IconBase {...props}>
189
+ <circle cx="12" cy="12" r="3" />
190
+ <path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1A1.7 1.7 0 0 0 9 4.6 1.7 1.7 0 0 0 10 3v-.2h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9 1.7 1.7 0 0 0 1.6 1h.2v4H21a1.7 1.7 0 0 0-1.6 1Z" />
191
+ </IconBase>
192
+ );
193
+ }
185
194
  export function EyeIcon(props: IconProps) {
186
195
  return (
187
196
  <IconBase {...props}>
@@ -358,7 +358,8 @@ function Ligne({
358
358
  aDesEnfants: boolean;
359
359
  }) {
360
360
  const porteUnePastille =
361
- collapsed && navSome(item, (e) => e.badge !== undefined && e.badge !== null);
361
+ collapsed &&
362
+ navSome(item, (e) => e.badge !== undefined && e.badge !== null);
362
363
 
363
364
  return (
364
365
  <>
@@ -370,7 +371,16 @@ function Ligne({
370
371
  <span className="sia-sidebar__label">{item.label}</span>
371
372
 
372
373
  {item.badge !== undefined && !collapsed && (
373
- <span className="sia-sidebar__badge">{item.badge}</span>
374
+ <span
375
+ className="sia-sidebar__badge"
376
+ data-sia-badge={
377
+ typeof item.badge === "string" || typeof item.badge === "number"
378
+ ? String(item.badge)
379
+ : undefined
380
+ }
381
+ >
382
+ {item.badge}
383
+ </span>
374
384
  )}
375
385
 
376
386
  {/* Le compte exact ne tient pas sur un rail : il devient un point, et se
@@ -571,7 +581,17 @@ function NoeudRail(props: NoeudProps) {
571
581
  <div className="sia-sidebar-flyout__title">
572
582
  {item.label}
573
583
  {item.badge !== undefined && (
574
- <span className="sia-sidebar__badge">{item.badge}</span>
584
+ <span
585
+ className="sia-sidebar__badge"
586
+ data-sia-badge={
587
+ typeof item.badge === "string" ||
588
+ typeof item.badge === "number"
589
+ ? String(item.badge)
590
+ : undefined
591
+ }
592
+ >
593
+ {item.badge}
594
+ </span>
575
595
  )}
576
596
  </div>
577
597
 
@@ -8,7 +8,7 @@
8
8
  font-size: .875rem;
9
9
  }
10
10
 
11
- .sia-sidebar__body { flex: 1; min-height: 0; overflow-y: auto; display: grid; gap: .125rem; align-content: start; }
11
+ .sia-sidebar__body { flex:1; min-height:0; padding-inline-end:.375rem; overflow-y:auto; display:grid; gap:.125rem; align-content:start; scrollbar-gutter:stable; }
12
12
  /* Rognés plutôt que débordants : en mode rail, un en-tête ou un pied qui
13
13
  n'a pas été adapté déborderait sur le contenu. Le `data-collapsed` posé
14
14
  par `AppShell` permet de l'adapter; ceci garantit que ne pas l'adapter
@@ -130,17 +130,7 @@
130
130
  padding-block: .375rem;
131
131
  font-size: .825rem;
132
132
  }
133
- /* Le filet qui relie les sous-entrées à leur parent. */
134
133
  .sia-sidebar__sub { position: relative; }
135
- .sia-sidebar__sub::before {
136
- content: "";
137
- position: absolute;
138
- top: .375rem;
139
- bottom: .375rem;
140
- left: 1.25rem;
141
- width: 1px;
142
- background: var(--sia-border);
143
- }
144
134
 
145
135
  /* ── Mode réduit ───────────────────────────────────────────────────────── */
146
136
 
@@ -197,8 +187,6 @@
197
187
  font-weight: 650;
198
188
  }
199
189
  .sia-sidebar-flyout__body { display: grid; gap: .125rem; }
200
- /* Dans le volet, le filet vertical du parent n'a plus de parent à relier. */
201
- .sia-sidebar-flyout .sia-sidebar__sub::before { display: none; }
202
190
  .sia-sidebar-flyout .sia-sidebar__item--child { padding-left: calc(.625rem + var(--sia-sidebar-depth, 1) * .6rem); }
203
191
 
204
192
  /* ── Variantes ─────────────────────────────────────────────────────────── */
@@ -63,9 +63,10 @@
63
63
  --sia-toggle-color: var(--sia-primary);
64
64
  display: inline-flex;
65
65
  padding: .2rem;
66
- border: 1px solid var(--sia-border);
66
+ border: 1px solid color-mix(in srgb, var(--sia-muted) 28%, var(--sia-border));
67
67
  border-radius: calc(var(--sia-radius) + 2px);
68
- background: var(--sia-surface);
68
+ background: color-mix(in srgb, var(--sia-muted) 12%, var(--sia-background));
69
+ box-shadow: inset 0 1px 2px rgb(15 23 42 / .05);
69
70
  }
70
71
 
71
72
  .sia-toggle-group__item {
@@ -144,6 +144,17 @@
144
144
  --sia-elevation-modal: 0 24px 60px rgb(15 23 42 / 0.22);
145
145
  }
146
146
  /* @end:theme */
147
+ /* Scrollbars SIA UI globales : faibles en spécificité, donc surchargeables. */
148
+ :root {
149
+ --sia-scrollbar-size:.625rem;
150
+ --sia-scrollbar-thumb:color-mix(in srgb,var(--sia-muted) 52%,transparent);
151
+ --sia-scrollbar-thumb-hover:color-mix(in srgb,var(--sia-muted) 76%,transparent);
152
+ }
153
+ :where(*) { scrollbar-width:thin; scrollbar-color:var(--sia-scrollbar-thumb) transparent; }
154
+ :where(*)::-webkit-scrollbar { width:var(--sia-scrollbar-size); height:var(--sia-scrollbar-size); }
155
+ :where(*)::-webkit-scrollbar-track,:where(*)::-webkit-scrollbar-corner { background:transparent; }
156
+ :where(*)::-webkit-scrollbar-thumb { min-width:2.5rem; min-height:2.5rem; border:2px solid transparent; border-radius:var(--sia-radius-full,9999px); background:var(--sia-scrollbar-thumb); background-clip:content-box; }
157
+ :where(*)::-webkit-scrollbar-thumb:hover { border:2px solid transparent; background:var(--sia-scrollbar-thumb-hover); background-clip:content-box; }
147
158
  .sia-radius--default { border-radius:var(--sia-radius); }
148
159
  .sia-radius--none { border-radius:0; }
149
160
  .sia-radius--sm { border-radius:.25rem; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sia-ui/cli",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Install SIA UI components progressively into an application.",
5
5
  "keywords": [
6
6
  "sia-ui",
@@ -39,10 +39,10 @@
39
39
  "react-dom": "^19.2.3",
40
40
  "typescript": "^5.9.3",
41
41
  "vitest": "^4.0.16",
42
- "@sia-ui/headless": "0.3.2",
43
- "@sia-ui/react": "0.3.2",
44
- "@sia-ui/tokens": "0.3.2",
45
- "@sia-ui/utils": "0.3.2"
42
+ "@sia-ui/headless": "0.4.0",
43
+ "@sia-ui/utils": "0.4.0",
44
+ "@sia-ui/tokens": "0.4.0",
45
+ "@sia-ui/react": "0.4.0"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "node scripts/build.mjs",