@zevra/ui 0.35.0 → 0.36.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/DECISIONS.md +264 -208
- package/README.md +21 -21
- package/demo/app.html +6 -6
- package/demo/index.html +3 -3
- package/dist/actions.d.ts +8 -8
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +1 -1
- package/dist/actions.js.map +1 -1
- package/dist/catalogue.d.ts +2 -2
- package/dist/catalogue.js +67 -67
- package/dist/catalogue.js.map +1 -1
- package/dist/content.d.ts +23 -23
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +16 -16
- package/dist/content.js.map +1 -1
- package/dist/css-inline.d.ts +1 -1
- package/dist/css-inline.d.ts.map +1 -1
- package/dist/css-inline.js +4 -4
- package/dist/css-inline.js.map +1 -1
- package/dist/data-table.d.ts +1 -1
- package/dist/data.d.ts +7 -7
- package/dist/data.js +5 -5
- package/dist/email-tokens.js +1 -1
- package/dist/email.d.ts +6 -6
- package/dist/email.js +8 -8
- package/dist/email.js.map +1 -1
- package/dist/feedback.d.ts +10 -10
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +3 -3
- package/dist/forms.d.ts +12 -12
- package/dist/forms.js +5 -5
- package/dist/glyph-data.d.ts +2 -2
- package/dist/glyph-data.js +3 -3
- package/dist/glyph-urls.js +1 -1
- package/dist/glyphes.d.ts +2 -2
- package/dist/glyphes.js +4 -4
- package/dist/icon-data.d.ts +1 -1
- package/dist/icon-data.js +2 -2
- package/dist/icon.d.ts +5 -5
- package/dist/icon.js +1 -1
- package/dist/index.js +4 -4
- package/dist/layout.d.ts +21 -21
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +15 -15
- package/dist/layout.js.map +1 -1
- package/dist/navigation.d.ts +16 -16
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +9 -9
- package/dist/navigation.js.map +1 -1
- package/dist/overlays.d.ts +6 -6
- package/dist/overlays.d.ts.map +1 -1
- package/dist/overlays.js +5 -5
- package/dist/select-menu.d.ts +1 -1
- package/dist/select-menu.js +2 -2
- package/dist/surfaces.d.ts +8 -8
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +6 -6
- package/dist/surfaces.js.map +1 -1
- package/dist/typography.d.ts +4 -4
- package/dist/typography.d.ts.map +1 -1
- package/dist/typography.js +3 -3
- package/dist/typography.js.map +1 -1
- package/icons/index.json +1 -1
- package/package.json +2 -2
- package/react/actions.tsx +9 -9
- package/react/catalogue.tsx +68 -68
- package/react/content.tsx +28 -28
- package/react/css-inline.ts +4 -4
- package/react/data-table.tsx +3 -3
- package/react/data.tsx +9 -9
- package/react/email-tokens.ts +1 -1
- package/react/email.ts +10 -10
- package/react/feedback.tsx +12 -12
- package/react/forms.tsx +15 -15
- package/react/glyph-data.ts +3 -3
- package/react/glyph-urls.ts +1 -1
- package/react/glyphes.ts +4 -4
- package/react/icon-data.ts +2 -2
- package/react/icon.tsx +6 -6
- package/react/index.ts +4 -4
- package/react/layout.tsx +22 -22
- package/react/navigation.tsx +18 -18
- package/react/overlays.tsx +9 -9
- package/react/select-menu.tsx +3 -3
- package/react/surfaces.tsx +10 -10
- package/react/typography.tsx +6 -6
- package/src/base.css +10 -10
- package/src/components.css +49 -49
- package/src/data.css +41 -41
- package/src/feedback.css +55 -55
- package/src/fonts.css +2 -2
- package/src/forms.css +50 -50
- package/src/icons.css +10 -10
- package/src/index.css +1 -1
- package/src/layout.css +27 -27
- package/src/motion.css +16 -16
- package/src/motion.js +4 -4
- package/src/nav.js +5 -5
- package/src/navigation.css +62 -62
- package/src/overlays.css +14 -14
- package/src/rail.js +6 -6
- package/src/surfaces.css +11 -11
- package/src/tokens.css +14 -14
- package/tokens/index.js +8 -8
package/react/data-table.tsx
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
//
|
|
3
3
|
// ⚠️ CE FICHIER N'EST PAS DANS LE BARREL, et c'est délibéré :
|
|
4
4
|
// `@tanstack/react-table` est une dépendance OPTIONNELLE. L'importer depuis
|
|
5
|
-
// `@zevra/ui/react` casserait toutes les apps qui ne l'ont pas installée
|
|
5
|
+
// `@zevra/ui/react` casserait toutes les apps qui ne l'ont pas installée :
|
|
6
6
|
// une landing n'a aucun tableau. D'où le sous-chemin dédié :
|
|
7
7
|
//
|
|
8
8
|
// import { DataTable } from '@zevra/ui/react/table'
|
|
9
9
|
//
|
|
10
10
|
// POURQUOI TANSTACK plutôt que notre propre tri/pagination : tri multi-colonnes,
|
|
11
|
-
// filtres, groupement, virtualisation, sélection de lignes
|
|
11
|
+
// filtres, groupement, virtualisation, sélection de lignes : c'est un travail
|
|
12
12
|
// de plusieurs mois que personne ici n'a envie de refaire, et mal. TanStack est
|
|
13
13
|
// HEADLESS : il ne rend rien, il calcule des modèles de lignes. Ce fichier lui
|
|
14
14
|
// donne l'apparence du paquet, exactement comme shadcn le fait avec ses
|
|
@@ -49,7 +49,7 @@ export interface DataTableProps<T> {
|
|
|
49
49
|
* d'absence qu'on attend ici (filtre trop étroit), pas l'écran d'absence :
|
|
50
50
|
* l'en-tête doit rester visible pour qu'on puisse élargir le filtre. */
|
|
51
51
|
vide?: ReactNode
|
|
52
|
-
/** Appelé au clic sur une ligne. Sans lui, la ligne n'est pas cliquable
|
|
52
|
+
/** Appelé au clic sur une ligne. Sans lui, la ligne n'est pas cliquable :
|
|
53
53
|
* et elle ne doit pas en avoir l'air. */
|
|
54
54
|
onRowClick?: (ligne: T) => void
|
|
55
55
|
className?: string
|
package/react/data.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Données
|
|
1
|
+
// Données : tableau en grille, listes, métadonnées, clé-valeur, vignettes.
|
|
2
2
|
//
|
|
3
3
|
// Le tableau du système n'est PAS un <table> : c'est une grille CSS, parce
|
|
4
4
|
// qu'une même rangée doit se rendre en colonnes au-dessus du point de rupture
|
|
@@ -19,7 +19,7 @@ type Span = Omit<HTMLAttributes<HTMLSpanElement>, 'className'>
|
|
|
19
19
|
|
|
20
20
|
/** `--zv-cols` est un PARAMÈTRE de grille, pas un token de marque : il se
|
|
21
21
|
* pose en style inline sur le cadre, et l'en-tête comme les lignes en
|
|
22
|
-
* héritent. Une seule déclaration pour les deux
|
|
22
|
+
* héritent. Une seule déclaration pour les deux : recopié aux deux
|
|
23
23
|
* endroits, on l'oublie à l'un et les intitulés se décalent des valeurs
|
|
24
24
|
* sans que rien ne casse visiblement. */
|
|
25
25
|
function cols(gabarit: string | undefined, style: CSSProperties | undefined) {
|
|
@@ -35,7 +35,7 @@ export interface TableProps extends Div {
|
|
|
35
35
|
/** Tableau de PROSE : les cellules reviennent à la ligne au lieu de
|
|
36
36
|
* tronquer, et les colonnes s'alignent en haut.
|
|
37
37
|
*
|
|
38
|
-
* ⚠️ À employer dès qu'une cellule porte une phrase plutôt qu'une donnée
|
|
38
|
+
* ⚠️ À employer dès qu'une cellule porte une phrase plutôt qu'une donnée :
|
|
39
39
|
* une clause de contrat, un fondement juridique, une garantie de
|
|
40
40
|
* transfert. Tronquer une clause ne la raccourcit pas, ça la CACHE, et le
|
|
41
41
|
* lecteur n'a aucun moyen de savoir qu'il manque quelque chose. Le défaut
|
|
@@ -170,7 +170,7 @@ export interface MetaProps extends Span {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
/** Second registre mono du système, en BAS DE CASSE : taille de fichier,
|
|
173
|
-
* date, horodatage, latence. Ni capitales ni interlettrage
|
|
173
|
+
* date, horodatage, latence. Ni capitales ni interlettrage : c'est ce qui le
|
|
174
174
|
* distingue de `Tag` et `Mono`, et une date en capitales espacées devient
|
|
175
175
|
* illisible. */
|
|
176
176
|
export function Meta({ taille = 'normal', discret, className, ...reste }: MetaProps) {
|
|
@@ -305,7 +305,7 @@ export function CiteRow({ className, ...reste }: Div & { className?: string }) {
|
|
|
305
305
|
|
|
306
306
|
export interface FileTypeProps extends Span {
|
|
307
307
|
/** Règle de choix relevée : DOCX si le type MIME contient « word »,
|
|
308
|
-
* PDF sinon. Les couleurs ne viennent PAS de la charte
|
|
308
|
+
* PDF sinon. Les couleurs ne viennent PAS de la charte : ce sont celles
|
|
309
309
|
* que le monde associe à ces formats. */
|
|
310
310
|
format: 'pdf' | 'docx'
|
|
311
311
|
className?: string
|
|
@@ -325,7 +325,7 @@ export interface FoldProps
|
|
|
325
325
|
extends Omit<DetailsHTMLAttributes<HTMLDetailsElement>, 'className' | 'open'> {
|
|
326
326
|
titre: ReactNode
|
|
327
327
|
/** Ouverte au premier rendu. L'état vit ensuite dans le NAVIGATEUR
|
|
328
|
-
* (<details> natif) : pas de prop contrôlée, pas de JavaScript
|
|
328
|
+
* (<details> natif) : pas de prop contrôlée, pas de JavaScript, le
|
|
329
329
|
* clavier et l'annonce viennent avec. `open` est retiré des attributs :
|
|
330
330
|
* deux canaux pour le même état, et c'est le silencieux qui gagnerait. */
|
|
331
331
|
ouvert?: boolean
|
|
@@ -334,7 +334,7 @@ export interface FoldProps
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
/** Le pliage de l'applicatif : options avancées, détail d'une ligne de
|
|
337
|
-
* journal. La FAQ (contenu) reste le pliage de la VITRINE
|
|
337
|
+
* journal. La FAQ (contenu) reste le pliage de la VITRINE : question en
|
|
338
338
|
* 14px, respiration éditoriale ; celui-ci est un chrome dense. Le chevron
|
|
339
339
|
* fait partie du composant, comme la flèche de LinkArrow. */
|
|
340
340
|
export function Fold({ titre, ouvert, className, children, ...reste }: FoldProps) {
|
|
@@ -362,7 +362,7 @@ export function Fold({ titre, ouvert, className, children, ...reste }: FoldProps
|
|
|
362
362
|
|
|
363
363
|
/* ─── Touche de clavier ────────────────────────────────────────────── */
|
|
364
364
|
|
|
365
|
-
/** Une touche, une seule : « ⌘K » est DEUX touches, donc deux `Kbd
|
|
365
|
+
/** Une touche, une seule : « ⌘K » est DEUX touches, donc deux `Kbd`, les
|
|
366
366
|
* fondre dans une seule dessine une touche qui n'existe sur aucun
|
|
367
367
|
* clavier. */
|
|
368
368
|
export function Kbd({
|
|
@@ -384,7 +384,7 @@ export function Code({
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
/** Bloc de code. Chasse fixe RÉELLE (--font-code) : --font-mono est
|
|
387
|
-
* proportionnelle et n'aligne pas du code. Aucune coloration syntaxique
|
|
387
|
+
* proportionnelle et n'aligne pas du code. Aucune coloration syntaxique :
|
|
388
388
|
* elle imposerait une palette entière que la charte n'a pas. Le bloc
|
|
389
389
|
* défile en son sein : une ligne longue ne pousse jamais la page. */
|
|
390
390
|
export function CodeBlock({
|
package/react/email-tokens.ts
CHANGED
package/react/email.ts
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
// · il n'y a pas de feuille de style à charger dans un e-mail ;
|
|
9
9
|
// · Outlook et Gmail SUPPRIMENT les propriétés personnalisées : un
|
|
10
10
|
// `var(--accent)` s'y rend en couleur nulle, donc en texte noir sur fond
|
|
11
|
-
// transparent. Les valeurs sont donc écrites en clair
|
|
11
|
+
// transparent. Les valeurs sont donc écrites en clair : mais tirées du
|
|
12
12
|
// miroir JS des tokens, que tests/tokens.test.js tient aligné sur le CSS ;
|
|
13
13
|
// · ni flexbox ni grid ne sont fiables : la mise en page est en TABLES ;
|
|
14
|
-
// · les dégradés ne se rendent pas dans Outlook
|
|
14
|
+
// · les dégradés ne se rendent pas dans Outlook : le bouton plein perd le
|
|
15
15
|
// sien et devient un aplat, le filet de spectre devient trois aplats.
|
|
16
16
|
//
|
|
17
17
|
// Il vit dans react/ parce que c'est la racine de compilation du paquet
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
// stylées.
|
|
24
24
|
|
|
25
25
|
import { COULEURS, POLICES as PILES } from './email-tokens.js'
|
|
26
|
-
// Les URL publiques des glyphes
|
|
26
|
+
// Les URL publiques des glyphes, sept chaînes, aucune image inlinée : un
|
|
27
27
|
// client de messagerie ne résout pas une data-URI (Gmail les bloque), il lui
|
|
28
28
|
// faut une adresse absolue.
|
|
29
29
|
import { LOGOS_PRODUITS } from './glyph-urls.js'
|
|
@@ -34,7 +34,7 @@ export { BASE_RESSOURCES, LOGOS_PRODUITS } from './glyph-urls.js'
|
|
|
34
34
|
* délimité par ces mêmes guillemets : l'attribut se refermerait au premier
|
|
35
35
|
* guillemet interne, et TOUT le style du nœud serait perdu. En HTML, un
|
|
36
36
|
* guillemet simple est équivalent à l'intérieur d'une valeur de police.
|
|
37
|
-
* Cette faute ne se voit pas à la relecture
|
|
37
|
+
* Cette faute ne se voit pas à la relecture : elle se voit au rendu, où
|
|
38
38
|
* l'e-mail entier repasse en Times sans style. */
|
|
39
39
|
const pourAttribut = (pile: string) => pile.replace(/"/g, "'")
|
|
40
40
|
|
|
@@ -87,14 +87,14 @@ export function emailTitre(texte: string): string {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/** Un paragraphe. Le HTML d'enrichissement (`<strong>`, `<a>`) est admis :
|
|
90
|
-
* c'est du texte rédigé, pas une saisie utilisateur
|
|
90
|
+
* c'est du texte rédigé, pas une saisie utilisateur, l'échapper
|
|
91
91
|
* afficherait des chevrons. Passer par `echappe` toute valeur qui vient
|
|
92
92
|
* d'une base. */
|
|
93
93
|
export function emailTexte(html: string): string {
|
|
94
94
|
return `<p style="${CORPS}margin:0 0 16px;">${html}</p>`
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
/** L'action. UN SEUL bouton plein par e-mail, comme par page
|
|
97
|
+
/** L'action. UN SEUL bouton plein par e-mail, comme par page : c'est ce qui
|
|
98
98
|
* fait qu'on sait où cliquer.
|
|
99
99
|
* Construit en table : le `padding` d'un `<a>` est ignoré par Outlook, qui
|
|
100
100
|
* rendrait un lien nu au milieu du message. Aplat indigo et non dégradé,
|
|
@@ -107,7 +107,7 @@ export function emailBouton({ href, libelle }: { href: string; libelle: string }
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/** Le lien secondaire, sous le bouton : l'autre chemin, pour qui ne veut pas
|
|
110
|
-
* du premier. Souligné
|
|
110
|
+
* du premier. Souligné : dans un e-mail, la couleur seule ne suffit pas à
|
|
111
111
|
* annoncer un lien. */
|
|
112
112
|
export function emailLienSecondaire({ href, libelle }: { href: string; libelle: string }): string {
|
|
113
113
|
return `<p style="${CORPS}font-size:14px;margin:0 0 16px;"><a href="${echappe(href)}" style="color:${COULEURS.accent};text-decoration:underline;">${echappe(libelle)}</a></p>`
|
|
@@ -138,7 +138,7 @@ export interface EmailOptions {
|
|
|
138
138
|
* à l'envoi, dans l'en-tête SMTP. */
|
|
139
139
|
titre: string
|
|
140
140
|
/** Le texte gris qui suit l'objet dans la liste des messages. Sans lui,
|
|
141
|
-
* le client y affiche les premiers mots du HTML
|
|
141
|
+
* le client y affiche les premiers mots du HTML : souvent « Voir cet
|
|
142
142
|
* e-mail dans votre navigateur », ce qui gâche la seule ligne dont on
|
|
143
143
|
* dispose pour convaincre d'ouvrir. */
|
|
144
144
|
preheader?: string
|
|
@@ -146,7 +146,7 @@ export interface EmailOptions {
|
|
|
146
146
|
* un logo : une image sur deux est bloquée par défaut, et un e-mail qui
|
|
147
147
|
* ne dit pas de qui il vient part à la corbeille. */
|
|
148
148
|
marque: string
|
|
149
|
-
/** L'URL ABSOLUE du logo
|
|
149
|
+
/** L'URL ABSOLUE du logo : une image d'e-mail ne se résout pas depuis un
|
|
150
150
|
* chemin relatif, et les data-URI sont bloquées par Gmail. */
|
|
151
151
|
logo?: { src: string; hauteur?: number }
|
|
152
152
|
/** Raccourci : le glyphe d'un produit Zevra, à son URL publiée avec la
|
|
@@ -165,7 +165,7 @@ export interface EmailOptions {
|
|
|
165
165
|
*
|
|
166
166
|
* ⚠️ Le mode sombre est REFUSÉ explicitement (`color-scheme: light`) : la
|
|
167
167
|
* charte est claire par nature, et l'inversion automatique d'Apple Mail
|
|
168
|
-
* retourne les neutres sans toucher aux aplats
|
|
168
|
+
* retourne les neutres sans toucher aux aplats, l'indigo du bouton
|
|
169
169
|
* resterait sur un fond devenu noir, et les contrastes réglés dans un sens
|
|
170
170
|
* partiraient dans l'autre. C'est la même règle que pour les surfaces
|
|
171
171
|
* sombres du système : elles se font avec la bande manifeste, jamais en
|
package/react/feedback.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// Retours
|
|
1
|
+
// Retours : bandeaux d'état, étiquettes, pastilles, absence, travail en cours.
|
|
2
2
|
//
|
|
3
3
|
// Rien ici n'est interactif : pas de fermeture, pas de survol. Aucun de ces
|
|
4
4
|
// composants n'a besoin de tourner côté navigateur.
|
|
5
5
|
//
|
|
6
6
|
// ⚠️ Ce fichier ne livre NI roue de chargement, NI squelette, NI infobulle :
|
|
7
7
|
// le CSS n'en a pas, parce que le relevé de production n'en contenait aucun.
|
|
8
|
-
// Le travail en cours se dit par `Loading`, qui nomme ce qu'on attend
|
|
8
|
+
// Le travail en cours se dit par `Loading`, qui nomme ce qu'on attend : une
|
|
9
9
|
// roue dit « attendez », cette phrase dit sur quoi.
|
|
10
10
|
|
|
11
11
|
import type { HTMLAttributes, ReactNode } from 'react'
|
|
@@ -22,7 +22,7 @@ export interface AlertProps extends Div {
|
|
|
22
22
|
/** OBLIGATOIRE : sans ton, le bandeau hérite d'un filet en `currentColor`,
|
|
23
23
|
* soit un trait d'encre autour d'un fond blanc. */
|
|
24
24
|
ton: AlertTon
|
|
25
|
-
/** `sm` resserre, `lg` amplifie
|
|
25
|
+
/** `sm` resserre, `lg` amplifie : ce dernier pour le message qui REMPLACE
|
|
26
26
|
* un contenu attendu (incident, réponse impossible). */
|
|
27
27
|
taille?: 'sm' | 'normal' | 'lg'
|
|
28
28
|
className?: string
|
|
@@ -92,8 +92,8 @@ export function Badge({
|
|
|
92
92
|
export type DotEtat = 'recu' | 'ocr' | 'indexation' | 'indexe' | 'ok' | 'erreur'
|
|
93
93
|
|
|
94
94
|
export interface DotProps extends Span {
|
|
95
|
-
/** Sans état, la pastille reste GRISE : un statut inconnu
|
|
96
|
-
* serveur, pas encore côté interface
|
|
95
|
+
/** Sans état, la pastille reste GRISE : un statut inconnu (ajouté côté
|
|
96
|
+
* serveur, pas encore côté interface) ne doit pas passer pour un succès. */
|
|
97
97
|
etat?: DotEtat
|
|
98
98
|
taille?: 'sm' | 'normal' | 'lg'
|
|
99
99
|
/** CARRÉE : marque une SOURCE, pas un état. Ne pas uniformiser. */
|
|
@@ -204,7 +204,7 @@ export interface ProgressProps extends Div {
|
|
|
204
204
|
/** Entre 0 et 1. Hors bornes, la valeur est ramenée dedans : une barre à
|
|
205
205
|
* 140 % déborde de son cadre. */
|
|
206
206
|
valeur: number
|
|
207
|
-
/** Ce que la barre mesure
|
|
207
|
+
/** Ce que la barre mesure : annoncé, jamais dessiné. */
|
|
208
208
|
label?: string
|
|
209
209
|
className?: string
|
|
210
210
|
}
|
|
@@ -234,14 +234,14 @@ export interface SpinnerProps extends Span {
|
|
|
234
234
|
/** En tête d'un panneau d'attente : la roue seule, plus grande. */
|
|
235
235
|
grand?: boolean
|
|
236
236
|
/** Ce qu'on attend, annoncé au lecteur d'écran. Sans lui la roue est
|
|
237
|
-
* DÉCORATIVE et masquée
|
|
237
|
+
* DÉCORATIVE et masquée : le bon défaut quand une phrase voisine dit
|
|
238
238
|
* déjà le travail (`Loading`, ou le libellé d'un bouton `occupe`). */
|
|
239
239
|
label?: string
|
|
240
240
|
className?: string
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
/** Attente COURTE ET LOCALE : un bouton, une cellule, un panneau sans la
|
|
244
|
-
* place d'une phrase. Pour l'attente d'un écran, préférer `Loading`
|
|
244
|
+
* place d'une phrase. Pour l'attente d'un écran, préférer `Loading` : la
|
|
245
245
|
* phrase qui dit le travail réel reste le motif par défaut du système.
|
|
246
246
|
* Le libellé est un TEXTE masqué visuellement, pas un aria-label : sur un
|
|
247
247
|
* span vide, l'aria-label n'est pas annoncé par tous les lecteurs. */
|
|
@@ -270,7 +270,7 @@ export interface SkeletonProps extends Div {
|
|
|
270
270
|
className?: string
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
-
/** Il ANNONCE une structure connue (la carte, la ligne qui va venir)
|
|
273
|
+
/** Il ANNONCE une structure connue (la carte, la ligne qui va venir) : pour
|
|
274
274
|
* un contenu dont la forme est inconnue, c'est `Loading` qui dit ce cas.
|
|
275
275
|
* Le squelette est masqué aux lecteurs d'écran : c'est la zone qui charge
|
|
276
276
|
* qui porte aria-busy, pas chaque os. */
|
|
@@ -288,11 +288,11 @@ export function Skeleton({ texte, largeur, hauteur, className, style, ...reste }
|
|
|
288
288
|
/* ─── Infobulle ────────────────────────────────────────────────────── */
|
|
289
289
|
|
|
290
290
|
export interface TooltipProps extends Span {
|
|
291
|
-
/** Le complément
|
|
291
|
+
/** Le complément : un raccourci, une date exacte, une unité. JAMAIS le
|
|
292
292
|
* nom de la commande : lui vit sur la cible (libellé ou aria-label), une
|
|
293
293
|
* infobulle n'existe pas au doigt. */
|
|
294
294
|
contenu: ReactNode
|
|
295
|
-
/** Identifiant EXPLICITE, à répéter en `aria-describedby` sur la cible
|
|
295
|
+
/** Identifiant EXPLICITE, à répéter en `aria-describedby` sur la cible,
|
|
296
296
|
* le contrat du couple htmlFor/id de Field, pour la même raison : pas de
|
|
297
297
|
* useId, le composant reste rendu au serveur. */
|
|
298
298
|
id?: string
|
|
@@ -305,7 +305,7 @@ export interface TooltipProps extends Span {
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
/** Échap ferme la bulle jusqu'au blur (WCAG 1.4.13) : la classe est posée
|
|
308
|
-
* sur le nœud sans état React
|
|
308
|
+
* sur le nœud sans état React, le composant reste rendu au serveur, et
|
|
309
309
|
* sans script la bulle reste simplement au comportement survol/focus. */
|
|
310
310
|
export function Tooltip({ contenu, id, bas, className, children, ...reste }: TooltipProps) {
|
|
311
311
|
return (
|
package/react/forms.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
// Saisie
|
|
1
|
+
// Saisie : champs, sélection, bascules, puces de filtre, zone de dépôt.
|
|
2
2
|
//
|
|
3
3
|
// Aucun composant de ce fichier ne porte « use client » : ils sont tous sans
|
|
4
4
|
// état. Ceux qui prennent un gestionnaire d'événement (Switch, Seg, Drop)
|
|
5
|
-
// doivent être rendus DEPUIS un composant client, qui détient l'état
|
|
5
|
+
// doivent être rendus DEPUIS un composant client, qui détient l'état : le
|
|
6
6
|
// paquet ne le fait pas à ta place, sinon un simple champ de recherche
|
|
7
7
|
// tirerait React côté navigateur sur une page vitrine.
|
|
8
8
|
//
|
|
9
|
-
// ACCESSIBILITÉ
|
|
9
|
+
// ACCESSIBILITÉ : le liage intitulé ↔ contrôle est EXPLICITE. Field ne
|
|
10
10
|
// fabrique pas d'identifiant (il faudrait useId, donc un composant client) :
|
|
11
11
|
// passe le même `htmlFor` à Field et `id` au contrôle. Field pose l'erreur
|
|
12
12
|
// sur `${htmlFor}-err` pour que tu puisses la citer dans aria-describedby.
|
|
@@ -28,7 +28,7 @@ import { Icon } from './icon.js'
|
|
|
28
28
|
export interface FieldProps
|
|
29
29
|
extends Omit<HTMLAttributes<HTMLDivElement>, 'className'> {
|
|
30
30
|
label?: ReactNode
|
|
31
|
-
/** Identifiant du contrôle enveloppé
|
|
31
|
+
/** Identifiant du contrôle enveloppé : à répéter en `id` sur le contrôle. */
|
|
32
32
|
htmlFor?: string
|
|
33
33
|
/** Posé à droite de l'intitulé, sur la même ligne de base
|
|
34
34
|
* (« Mot de passe oublié ? »). */
|
|
@@ -89,7 +89,7 @@ export interface InputProps
|
|
|
89
89
|
/** Ni fond ni filet : c'est le CADRE qui l'entoure qui fait le champ.
|
|
90
90
|
* Lui laisser sa bordure dessinerait deux cadres imbriqués. */
|
|
91
91
|
nu?: boolean
|
|
92
|
-
/** Pose `aria-invalid`
|
|
92
|
+
/** Pose `aria-invalid` : c'est lui qui rend l'erreur audible, et le CSS le
|
|
93
93
|
* suit autant que la classe. */
|
|
94
94
|
erreur?: boolean
|
|
95
95
|
className?: string
|
|
@@ -122,7 +122,7 @@ export function Textarea({ erreur, className, ...reste }: TextareaProps) {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/** <select> NATIF habillé comme le champ : la flèche, l'ouverture au clavier
|
|
125
|
-
* et l'annonce viennent du navigateur. C'est le motif de choix par défaut
|
|
125
|
+
* et l'annonce viennent du navigateur. C'est le motif de choix par défaut :
|
|
126
126
|
* `SelectTrigger` est un menu maison, à réserver aux filtres. */
|
|
127
127
|
export function Select({
|
|
128
128
|
className,
|
|
@@ -223,7 +223,7 @@ export interface SwitchProps
|
|
|
223
223
|
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'onChange'> {
|
|
224
224
|
/** État courant. Composant CONTRÔLÉ : il ne mémorise rien. */
|
|
225
225
|
on: boolean
|
|
226
|
-
/** Libellé accolé
|
|
226
|
+
/** Libellé accolé : c'est lui qui porte le sens, la piste seule ne dit pas
|
|
227
227
|
* ce qui est activé. */
|
|
228
228
|
texte?: ReactNode
|
|
229
229
|
className?: string
|
|
@@ -389,27 +389,27 @@ export function DropInput({
|
|
|
389
389
|
export interface InputAffixProps
|
|
390
390
|
extends Omit<HTMLAttributes<HTMLDivElement>, 'className'> {
|
|
391
391
|
/** Préfixe, en mono label : « € », « HT ». Une unité est un libellé, pas
|
|
392
|
-
* une donnée
|
|
392
|
+
* une donnée : elle ne se saisit pas, elle qualifie ce qui est saisi. */
|
|
393
393
|
avant?: ReactNode
|
|
394
394
|
/** Suffixe : « JOURS », « / MOIS », « .zevra.tech ». */
|
|
395
395
|
apres?: ReactNode
|
|
396
396
|
/** Identifiants EXPLICITES des affixes, à citer dans `aria-describedby`
|
|
397
|
-
* sur le champ intérieur
|
|
397
|
+
* sur le champ intérieur, le contrat htmlFor/id de `Field`, pour la
|
|
398
398
|
* même raison : pas de useId, le composant reste rendu au serveur.
|
|
399
399
|
* ⚠️ Sans ce câblage, un lecteur d'écran en mode formulaire entend
|
|
400
|
-
* « 1200 » sans jamais apprendre qu'il s'agit d'euros HT
|
|
400
|
+
* « 1200 » sans jamais apprendre qu'il s'agit d'euros HT : exactement le
|
|
401
401
|
* défaut que le composant existe pour corriger. */
|
|
402
402
|
idAvant?: string
|
|
403
403
|
idApres?: string
|
|
404
404
|
className?: string
|
|
405
|
-
/** Le champ lui-même
|
|
405
|
+
/** Le champ lui-même, un `<Input nu>` : c'est le CADRE qui fait le champ,
|
|
406
406
|
* un `<Input>` bordé dessinerait deux cadres imbriqués. */
|
|
407
407
|
children?: ReactNode
|
|
408
408
|
}
|
|
409
409
|
|
|
410
410
|
/** Cadre à affixe : « 30 », seul dans un champ, ne dit pas s'il s'agit de
|
|
411
411
|
* jours ou d'euros. Même grammaire que `Search` (cadre + champ nu) ; à ne
|
|
412
|
-
* pas employer quand l'unité est déjà dans l'intitulé du champ
|
|
412
|
+
* pas employer quand l'unité est déjà dans l'intitulé du champ : deux fois
|
|
413
413
|
* la même unité se lisent comme deux informations. */
|
|
414
414
|
export function InputAffix({
|
|
415
415
|
avant,
|
|
@@ -446,7 +446,7 @@ export interface RangeProps
|
|
|
446
446
|
|
|
447
447
|
/** Curseur natif habillé (`<input type="range">`) : clavier, annonce et
|
|
448
448
|
* bornes viennent du navigateur. Le remplissage de la piste est calculé
|
|
449
|
-
* ici depuis value/min/max
|
|
449
|
+
* ici depuis value/min/max : une géométrie, comme la largeur de
|
|
450
450
|
* `Progress`, pas une valeur de charte. En NON contrôlé, il reflète la
|
|
451
451
|
* valeur initiale et ne suit plus le doigt : contrôler le champ dès que le
|
|
452
452
|
* remplissage doit suivre.
|
|
@@ -493,8 +493,8 @@ export interface InputPasswordProps
|
|
|
493
493
|
* l'appelant, qui est déjà un composant client puisqu'il tient la valeur du
|
|
494
494
|
* champ. Le paquet ne tire pas React côté navigateur pour un champ.
|
|
495
495
|
*
|
|
496
|
-
* Le libellé du bouton change avec l'état
|
|
497
|
-
* va se passer
|
|
496
|
+
* Le libellé du bouton change avec l'état, une icône seule ne dit pas ce qui
|
|
497
|
+
* va se passer, et `aria-pressed` l'annonce aux lecteurs d'écran.
|
|
498
498
|
*/
|
|
499
499
|
export function InputPassword({
|
|
500
500
|
visible,
|
package/react/glyph-data.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// GÉNÉRÉ par scripts/build-glyph-data.mjs
|
|
1
|
+
// GÉNÉRÉ par scripts/build-glyph-data.mjs : ne pas éditer à la main.
|
|
2
2
|
// Source : logos/glyphes/*.png, réduits par scripts/build-glyphes.py.
|
|
3
3
|
|
|
4
4
|
export type GlypheId =
|
|
@@ -21,7 +21,7 @@ export const GLYPHES: Record<GlypheId, string> = {
|
|
|
21
21
|
'zevra': 'data:image/png;base64,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',
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/** Le mot « Zevra »
|
|
24
|
+
/** Le mot « Zevra » : le wordmark officiel, tracé, en data-URI. */
|
|
25
25
|
export const MOT_ZEVRA = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NTMuOCAxMDAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iWmV2cmEiPgogIDx0aXRsZT5aZXZyYTwvdGl0bGU+CiAgPCEtLSBMZSBtb3QgWmV2cmEsIHJlbGV2w6kgc3VyIGxlIGxvY2t1cCBkJ29yaWdpbmUuIExhIGhhdXRldXIgZGUgY2FwaXRhbGUKICAgICAgIHZhdXQgMTAwIHVuaXTDqXMgOiB1bmUgaGF1dGV1ciBDU1MgcG9zw6llIHN1ciBjZSBmaWNoaWVyIEVTVCBsYSBjYXBpdGFsZSwKICAgICAgIGNlIHF1aSByZW5kIGxlcyBwcm9wb3J0aW9ucyBkdSBsb2NrdXAgY2FsY3VsYWJsZXMgKHZvaXIgLnp2LWxvY2t1cCkuIC0tPgogIDxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNIDQxMy4zMSAwLjE4IEwgNDEzLjg1IDAuMTggTCA0MTQuMjEgMC4wMCBMIDQxNC43NCAwLjAwIEwgNDE1LjI4IDAuMDAgTCA0MTUuODEgMC4wMCBMIDQxNi4zNSAwLjAwIEwgNDE2Ljg4IDAuMDAgTCA0MTcuNDIgMC4wMCBMIDQxNy45NiAwLjAwIEwgNDE4LjQ5IDAuMDAgTCA0MTkuMDMgMC4wMCBMIDQxOS41NiAwLjAwIEwgNDIwLjEwIDAuMDAgTCA0MjAuNjMgMC4wMCBMIDQyMC45OSAwLjE4IEwgNDIxLjM1IDAuMzYgTCA0MjEuNzEgMC4zNiBMIDQ1My44MCA4MC4yOCBMIDQ0MS44MyA4NS4wNiBMIDQzMi41NyA2MS43NyBMIDQwMi40OSA2MS43NyBMIDM5My4xOSA4NS4wMiBMIDM4MS4yMiA4MC4zMCBaIE0gMC4xOCA4MC44OCBMIDQ4Ljg2IDEzLjIxIEwgNy4zMiAxMi44NSBMIDYuOTYgMTIuMzIgTCA2Ljk2IDExLjQzIEwgNi45NiAxMC41MyBMIDYuOTYgOS42NCBMIDYuOTYgOC43NSBMIDYuOTYgNy44NiBMIDYuOTYgNi45NiBMIDYuOTYgNi4wNyBMIDYuOTYgNS4xOCBMIDYuOTYgNC4yOCBMIDYuOTYgMy4zOSBMIDYuOTYgMi41MCBMIDYuOTYgMS42MSBMIDcuMTQgMC44OSBMIDcuMzIgMC41NCBMIDc0LjA4IDAuMTggTCAxOS41MCA3Ni4wNiBMIDYxLjA2IDc2LjU5IEwgNjEuMjQgNzcuMzEgTCA2MS40MiA3OC4wMiBMIDYxLjQyIDc4LjkxIEwgNjEuNDIgNzkuODEgTCA2MS40MiA4MC43MCBMIDYxLjQyIDgxLjU5IEwgNjEuNDIgODIuNDggTCA2MS40MiA4My4zOCBMIDYxLjQyIDg0LjI3IEwgNjEuNDIgODUuMTYgTCA2MS40MiA4Ni4wNSBMIDYxLjQyIDg2Ljk1IEwgNjEuNDIgODcuODQgTCA2MS4wNiA4OC4zOCBMIDYxLjA2IDg4LjczIEwgMC4zNiA4OC43MyBMIDAuMTggODguMzggTCAwLjAwIDg4LjAyIEwgMC4wMCA4Ny40OCBMIDAuMDAgODYuOTUgTCAwLjAwIDg2LjQxIEwgMC4wMCA4NS44OCBMIDAuMDAgODUuMzQgTCAwLjAwIDg0LjgxIEwgMC4wMCA4NC4yNyBMIDAuMDAgODMuNzMgTCAwLjAwIDgzLjIwIEwgMC4wMCA4Mi42NiBMIDAuMDAgODIuMTMgTCAwLjAwIDgxLjU5IEwgMC4xOCA4MS4yMyBaIE0gOTYuMjMgMC41NCBMIDE1MC41MSAwLjcxIEwgMTUwLjY5IDEuNDMgTCAxNTAuODYgMi4xNCBMIDE1MC44NiAzLjA0IEwgMTUwLjg2IDMuOTMgTCAxNTAuODYgNC44MiBMIDE1MC44NiA1LjcxIEwgMTUwLjg2IDYuNjEgTCAxNTAuODYgNy41MCBMIDE1MC44NiA4LjM5IEwgMTUwLjg2IDkuMjggTCAxNTAuODYgMTAuMTggTCAxNTAuODYgMTEuMDcgTCAxNTAuODYgMTEuOTYgTCAxNTAuNTEgMTIuNTAgTCAxNTAuNTEgMTIuNjggTCAxMDkuMDkgMTMuMzkgTCAxMDguNzMgMTQuODIgTCAxMDguNzMgMTYuMjUgTCAxMDguNzMgMTguMDMgTCAxMDguNzMgMTkuODIgTCAxMDguNzMgMjEuNjAgTCAxMDguNzMgMjMuMzkgTCAxMDguNzMgMjUuMTcgTCAxMDguNzMgMjYuOTYgTCAxMDguNzMgMjguNzQgTCAxMDguNzMgMzAuNTMgTCAxMDguNzMgMzIuMzIgTCAxMDguNzMgMzQuMTAgTCAxMDguNzMgMzUuODkgTCAxMDguOTEgMzcuNDkgTCAxMDkuMDkgMzguMjEgTCAxNDMuMjIgMzguMzkgTCAxNDMuMTEgNTEuMjQgTCAxMDkuMDkgNTEuNDIgTCAxMDguNzMgNTIuODUgTCAxMDguNzMgNTQuNjMgTCAxMDguNzMgNTYuNDIgTCAxMDguNzMgNTguMjAgTCAxMDguNzMgNTkuOTkgTCAxMDguNzMgNjEuNzcgTCAxMDguNzMgNjMuNTYgTCAxMDguNzMgNjUuMzQgTCAxMDguNzMgNjcuMTMgTCAxMDguNzMgNjguOTIgTCAxMDguNzMgNzAuNzAgTCAxMDguNzMgNzIuNDkgTCAxMDguNzMgNzQuMjcgTCAxMDkuMDkgNzUuNzAgTCAxMDkuMDkgNzUuODggTCAxNTAuMTUgNzYuNTkgTCAxNTAuNTEgNzcuMTMgTCAxNTAuODYgNzcuNjYgTCAxNTAuODYgNzguNTYgTCAxNTAuODYgNzkuNDUgTCAxNTAuODYgODAuMzQgTCAxNTAuODYgODEuMjMgTCAxNTAuODYgODIuMTMgTCAxNTAuODYgODMuMDIgTCAxNTAuODYgODMuOTEgTCAxNTAuODYgODQuODEgTCAxNTAuODYgODUuNzAgTCAxNTAuODYgODYuNTkgTCAxNTAuODYgODcuNDggTCAxNTAuNjkgODguMjAgTCAxNTAuNjkgODguMzggTCA5Ni40MSA4OC45MSBMIDk1Ljg3IDgzLjU2IEwgOTUuODcgNzcuMzEgTCA5NS44NyA3MS4wNiBMIDk1Ljg3IDY0LjgxIEwgOTUuODcgNTguNTYgTCA5NS44NyA1Mi4zMSBMIDk1Ljg3IDQ2LjA2IEwgOTUuODcgMzkuODEgTCA5NS44NyAzMy41NyBMIDk1Ljg3IDI3LjMyIEwgOTUuODcgMjEuMDcgTCA5NS44NyAxNC44MiBMIDk1Ljg3IDguNTcgTCA5NS44NyAyLjMyIFogTSAyODcuMjcgMC41NCBMIDI5NS44NCAwLjE4IEwgMzA0Ljc2IDAuMTggTCAzMTMuNjkgMC4xOCBMIDMyMi4yNiAwLjU0IEwgMzI5LjIyIDIuNTAgTCAzMzQuOTQgNS43MSBMIDMzOS41OCAxMC4wMCBMIDM0Mi45NyAxNS41MyBMIDM0NS40NyAyMS45NiBMIDM0NS42NSAzMC4zNSBMIDM0NC4wNCAzNy42NyBMIDM0MC42NSA0My4yMSBMIDMzNi4zNiA0Ny44NSBMIDMzMS4xOSA1MS42MCBMIDMzMC44MyA1MS45NSBMIDM1MS4zNCA3OC43MCBMIDM0MS4yMCA4Ni42MyBMIDMxNS45MyA1NC4yOCBMIDMwMC4xMiA1NC40NSBMIDI5OS43NiA1Ni4wNiBMIDI5OS43NiA1OC4wMiBMIDI5OS43NiA1OS45OSBMIDI5OS43NiA2MS45NSBMIDI5OS43NiA2My45MiBMIDI5OS43NiA2NS44OCBMIDI5OS43NiA2Ny44NCBMIDI5OS43NiA2OS44MSBMIDI5OS43NiA3MS43NyBMIDI5OS43NiA3My43NCBMIDI5OS43NiA3NS43MCBMIDI5OS43NiA3Ny42NiBMIDI5OS43NiA3OS42MyBMIDI5OS43NiA4MS41OSBMIDI5OS40MSA4Mi40OCBMIDI4Ny4yNyA4Mi40OCBMIDI4Ni45MSA3Ni45NSBMIDI4Ni45MSA3MS4wNiBMIDI4Ni45MSA2NS4xNyBMIDI4Ni45MSA1OS4yNyBMIDI4Ni45MSA1My4zOCBMIDI4Ni45MSA0Ny40OSBMIDI4Ni45MSA0MS42MCBMIDI4Ni45MSAzNS43MSBMIDI4Ni45MSAyOS44MiBMIDI4Ni45MSAyMy45MiBMIDI4Ni45MSAxOC4wMyBMIDI4Ni45MSAxMi4xNCBMIDI4Ni45MSA2LjI1IEwgMjg3LjI3IDAuNzEgWiBNIDE4NS43NSA5LjI1IEwgMTk3LjY5IDQuNDMgTCAyMjIuMDIgNjUuMzQgTCAyNDYuMzUgNC40MyBMIDI1OC4yOCA5LjI0IEwgMjIyLjAyIDEwMC4wMCBaIE0gMzAwLjEyIDEzLjU3IEwgMzAyLjgwIDEzLjIxIEwgMzA1LjgzIDEzLjIxIEwgMzA4Ljg3IDEzLjIxIEwgMzExLjkwIDEzLjIxIEwgMzE0Ljk0IDEzLjIxIEwgMzE3Ljk3IDEzLjIxIEwgMzIwLjgzIDEzLjM5IEwgMzIzLjE1IDE0LjEwIEwgMzI1LjQ3IDE0LjgyIEwgMzI3LjI2IDE2LjA3IEwgMzI4Ljg3IDE3LjUwIEwgMzMwLjI5IDE5LjEwIEwgMzMxLjU0IDIwLjg5IEwgMzMyLjQ0IDIzLjAzIEwgMzMyLjYyIDIzLjIxIEwgMzMyLjk3IDI2LjQyIEwgMzMyLjc5IDI5LjgyIEwgMzMxLjkwIDMyLjQ5IEwgMzMwLjgzIDM0Ljk5IEwgMzI5LjIyIDM2Ljk2IEwgMzI3LjI2IDM4LjU2IEwgMzI1LjEyIDM5Ljk5IEwgMzIyLjQ0IDQwLjg5IEwgMzE5LjQwIDQxLjQyIEwgMzE1LjgzIDQxLjQyIEwgMzEyLjI2IDQxLjQyIEwgMzA4LjY5IDQxLjQyIEwgMzA1LjEyIDQxLjQyIEwgMzAxLjU1IDQxLjQyIEwgMzAwLjEyIDQxLjA2IEwgMjk5Ljc2IDM5LjQ2IEwgMjk5Ljc2IDM3LjQ5IEwgMjk5Ljc2IDM1LjUzIEwgMjk5Ljc2IDMzLjU3IEwgMjk5Ljc2IDMxLjYwIEwgMjk5Ljc2IDI5LjY0IEwgMjk5Ljc2IDI3LjY3IEwgMjk5Ljc2IDI1LjcxIEwgMjk5Ljc2IDIzLjc1IEwgMjk5Ljc2IDIxLjc4IEwgMjk5Ljc2IDE5LjgyIEwgMjk5Ljc2IDE3Ljg1IEwgMjk5Ljc2IDE1Ljg5IEwgMjk5Ljk0IDE0LjEwIFogTSA0MTcuNTIgMjQuMTIgTCA0MjcuMzMgNDguNzQgTCA0MDcuNzAgNDguNzQgWiIvPgo8L3N2Zz4K'
|
|
26
26
|
|
|
27
27
|
/** Le nom lisible de chaque produit, depuis logos/index.json. */
|
|
@@ -35,7 +35,7 @@ export const NOMS_PRODUITS: Record<GlypheId, string> = {
|
|
|
35
35
|
'zevra': "Zevra",
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/** Ce que chaque produit FAIT, en deux mots
|
|
38
|
+
/** Ce que chaque produit FAIT, en deux mots : pour le lanceur d'apps. */
|
|
39
39
|
export const ACCROCHES_PRODUITS: Record<GlypheId, string> = {
|
|
40
40
|
'campus': "Formation",
|
|
41
41
|
'lexform': "Anonymisation",
|
package/react/glyph-urls.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// GÉNÉRÉ par scripts/build-glyph-data.mjs
|
|
1
|
+
// GÉNÉRÉ par scripts/build-glyph-data.mjs : ne pas éditer à la main.
|
|
2
2
|
|
|
3
3
|
/** Où la planche publie les glyphes (staticDirs de .storybook/main.ts). */
|
|
4
4
|
export const BASE_RESSOURCES = 'https://design.zevra.tech'
|
package/react/glyphes.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Les glyphes couleur des produits Zevra, prêts à poser.
|
|
2
2
|
//
|
|
3
3
|
// ⚠️ ENTRÉE SÉPARÉE, et c'est délibéré : ces images pèsent ~80 Ko une fois
|
|
4
|
-
// inlinées. Le barrel React n'en porte pas un octet
|
|
4
|
+
// inlinées. Le barrel React n'en porte pas un octet : seule une app qui
|
|
5
5
|
// affiche des logos importe ce module.
|
|
6
6
|
//
|
|
7
7
|
// POURQUOI INLINÉS plutôt que servis en fichiers : un bundler ne sait pas
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// Storybook, un canvas sans réseau, un catalogue compilé par tsc qui ne sait
|
|
10
10
|
// pas importer un PNG). C'est le même arbitrage que pour les icônes.
|
|
11
11
|
//
|
|
12
|
-
// ⚠️ Ce sont les versions COULEUR, à ne jamais recolorer ni resaturer
|
|
12
|
+
// ⚠️ Ce sont les versions COULEUR, à ne jamais recolorer ni resaturer : la
|
|
13
13
|
// charte l'interdit. Sous 24 px, la charte demande une version monochrome :
|
|
14
14
|
// elle n'existe pas encore (retirée le 24/08, à regénérer), et ces glyphes
|
|
15
15
|
// ne doivent donc pas descendre sous cette taille.
|
|
@@ -30,11 +30,11 @@ export { ACCROCHES_PRODUITS, ADRESSES_PRODUITS, GLYPHES, MOT_ZEVRA, NOMS_PRODUIT
|
|
|
30
30
|
export const PRODUITS = Object.keys(GLYPHES) as GlypheId[]
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* Les produits À METTRE DANS LE LANCEUR
|
|
33
|
+
* Les produits À METTRE DANS LE LANCEUR : ceux qui ont une adresse.
|
|
34
34
|
*
|
|
35
35
|
* ⚠️ CE N'EST PAS `PRODUITS`, et l'écart est le sujet. Plume et Supernovia
|
|
36
36
|
* ont leur glyphe depuis le 26/08 et rien à ouvrir : une tuile qui mène à
|
|
37
|
-
* rien est pire qu'une absence
|
|
37
|
+
* rien est pire qu'une absence, elle promet un produit, le visiteur clique,
|
|
38
38
|
* et il tombe. Le lanceur se construit sur CETTE liste.
|
|
39
39
|
*
|
|
40
40
|
* Elle se déduit du registre, elle n'est pas recopiée : le jour où Plume
|
package/react/icon-data.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// GÉNÉRÉ par scripts/build-icon-react.mjs
|
|
1
|
+
// GÉNÉRÉ par scripts/build-icon-react.mjs : ne pas éditer à la main.
|
|
2
2
|
// Source de vérité : icons/index.json.
|
|
3
3
|
|
|
4
4
|
export type IconId =
|
|
@@ -150,7 +150,7 @@ export const CORPS: Record<IconId, string> = {
|
|
|
150
150
|
'video': "<path d=\"M3.5 6 H15 V18 H3.5 Z\" /><path d=\"M15 10 L20.5 7 V17 L15 14 Z\" />",
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
/** Nom lisible de chaque icône
|
|
153
|
+
/** Nom lisible de chaque icône : sert de titre accessible par défaut à
|
|
154
154
|
* `IconButton`, et de libellé dans la page de consultation de l'admin. */
|
|
155
155
|
export const NOMS: Record<IconId, string> = {
|
|
156
156
|
'accueil': "Accueil",
|
package/react/icon.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Icônes
|
|
1
|
+
// Icônes : jeu de 55, tracées, sur grille de 24.
|
|
2
2
|
//
|
|
3
3
|
// Le corps du tracé est INLINÉ (voir scripts/build-icon-react.mjs) : pas de
|
|
4
4
|
// <use href="…sprite.svg">, donc rien à résoudre côté bundler et rien à
|
|
@@ -12,7 +12,7 @@ import { CORPS, NOMS, type IconId } from './icon-data.js'
|
|
|
12
12
|
export type { IconId }
|
|
13
13
|
export { NOMS as NOMS_ICONES }
|
|
14
14
|
|
|
15
|
-
/** Les 55 identifiants, triés
|
|
15
|
+
/** Les 55 identifiants, triés : pour construire une planche ou un sélecteur. */
|
|
16
16
|
export const ICONES = Object.keys(CORPS) as IconId[]
|
|
17
17
|
|
|
18
18
|
export interface IconProps
|
|
@@ -23,7 +23,7 @@ export interface IconProps
|
|
|
23
23
|
taille?: 'sm' | 'normal' | 'lg' | 'xl'
|
|
24
24
|
discret?: boolean
|
|
25
25
|
/** Titre accessible. Sans lui l'icône est DÉCORATIVE et masquée aux
|
|
26
|
-
* lecteurs d'écran
|
|
26
|
+
* lecteurs d'écran, ce qui est le bon défaut : une icône accolée à un
|
|
27
27
|
* libellé qui la répète crée une annonce double. */
|
|
28
28
|
titre?: string
|
|
29
29
|
className?: string
|
|
@@ -50,12 +50,12 @@ export function Icon({ nom, taille = 'normal', discret, titre, className, ...res
|
|
|
50
50
|
|
|
51
51
|
export interface IconButtonProps
|
|
52
52
|
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className'> {
|
|
53
|
-
/** Icône du jeu. À défaut, passe le glyphe en `children
|
|
53
|
+
/** Icône du jeu. À défaut, passe le glyphe en `children`, mais préfère le
|
|
54
54
|
* jeu : trois tracés maison de la même « croix » ont déjà divergé. */
|
|
55
55
|
icone?: IconId
|
|
56
56
|
/** OBLIGATOIRE : une icône seule n'a pas de libellé pour la nommer.
|
|
57
57
|
* L'écrire pour l'ACTION (« Fermer le panneau ») et non pour le dessin
|
|
58
|
-
* (« Croix »)
|
|
58
|
+
* (« Croix ») : c'est ce que le lecteur d'écran annonce. */
|
|
59
59
|
label: string
|
|
60
60
|
taille?: IconProps['taille']
|
|
61
61
|
className?: string
|
|
@@ -88,7 +88,7 @@ export function IconButton({
|
|
|
88
88
|
export interface IconTileProps
|
|
89
89
|
extends Omit<HTMLAttributes<HTMLSpanElement>, 'className'> {
|
|
90
90
|
nom: IconId
|
|
91
|
-
/** Titre accessible. Sans lui la tuile est DÉCORATIVE
|
|
91
|
+
/** Titre accessible. Sans lui la tuile est DÉCORATIVE, le bon défaut :
|
|
92
92
|
* elle accompagne un libellé qui la nomme déjà. */
|
|
93
93
|
titre?: string
|
|
94
94
|
className?: string
|
package/react/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// @zevra/ui/react
|
|
1
|
+
// @zevra/ui/react : bibliothèque de composants du design system « Encre & Papier ».
|
|
2
2
|
//
|
|
3
3
|
// COMMENT C'EST ORGANISÉ. Le CSS reste la source de vérité : chaque composant
|
|
4
4
|
// n'est qu'une façade typée et documentée au-dessus de classes `zv-`. Rien
|
|
5
5
|
// n'est calculé au rendu, aucune valeur de charte n'est écrite en JavaScript.
|
|
6
6
|
// Un test (tests/react.test.js) vérifie que toute classe qu'un composant peut
|
|
7
|
-
// émettre existe bien dans le CSS
|
|
7
|
+
// émettre existe bien dans le CSS : c'est ce qui empêche les deux de diverger.
|
|
8
8
|
//
|
|
9
9
|
// CE QU'IL FAUT CHARGER. Les composants ne portent PAS le CSS : il s'importe
|
|
10
10
|
// à part, une fois, dans la page ou la mise en page racine.
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// ou '@zevra/ui/css' pour tout charger d'un coup.
|
|
17
17
|
//
|
|
18
18
|
// SERVEUR / CLIENT. Un seul module est marqué « use client » : `overlays`
|
|
19
|
-
// (tiroir, modale, notification)
|
|
19
|
+
// (tiroir, modale, notification) : eux seuls ont besoin du navigateur. Tout
|
|
20
20
|
// le reste se rend au serveur, y compris dans une île Astro à zéro JS.
|
|
21
21
|
// Les composants qui prennent un gestionnaire d'événement (Switch, Seg,
|
|
22
22
|
// Tabs…) restent sans état : c'est un composant client à toi qui détient
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
//
|
|
25
25
|
// ÉCRITURE DES PROPS. En français, comme le CSS et les commentaires. Chaque
|
|
26
26
|
// prop qui porte une décision de charte est documentée à l'endroit où on la
|
|
27
|
-
// lit
|
|
27
|
+
// lit : les avertissements ⚠️ signalent ce qui casse le système si on le
|
|
28
28
|
// contourne, pas de simples préférences.
|
|
29
29
|
|
|
30
30
|
export { cx, mod } from './cx.js'
|