@zevra/ui 0.4.0 → 0.5.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.
Files changed (82) hide show
  1. package/README.md +64 -1
  2. package/dist/actions.d.ts +41 -0
  3. package/dist/actions.d.ts.map +1 -0
  4. package/dist/actions.js +24 -0
  5. package/dist/actions.js.map +1 -0
  6. package/dist/catalogue.d.ts +30 -0
  7. package/dist/catalogue.d.ts.map +1 -0
  8. package/dist/catalogue.js +830 -0
  9. package/dist/catalogue.js.map +1 -0
  10. package/dist/content.d.ts +135 -0
  11. package/dist/content.d.ts.map +1 -0
  12. package/dist/content.js +85 -0
  13. package/dist/content.js.map +1 -0
  14. package/dist/css-inline.d.ts +11 -0
  15. package/dist/css-inline.d.ts.map +1 -0
  16. package/dist/css-inline.js +13 -0
  17. package/dist/css-inline.js.map +1 -0
  18. package/dist/cx.d.ts +16 -0
  19. package/dist/cx.d.ts.map +1 -0
  20. package/dist/cx.js +20 -0
  21. package/dist/cx.js.map +1 -0
  22. package/dist/data.d.ts +136 -0
  23. package/dist/data.d.ts.map +1 -0
  24. package/dist/data.js +98 -0
  25. package/dist/data.js.map +1 -0
  26. package/dist/feedback.d.ts +88 -0
  27. package/dist/feedback.d.ts.map +1 -0
  28. package/dist/feedback.js +38 -0
  29. package/dist/feedback.js.map +1 -0
  30. package/dist/forms.d.ts +120 -0
  31. package/dist/forms.d.ts.map +1 -0
  32. package/dist/forms.js +60 -0
  33. package/dist/forms.js.map +1 -0
  34. package/dist/icon-data.d.ts +6 -0
  35. package/dist/icon-data.d.ts.map +1 -0
  36. package/dist/icon-data.js +77 -0
  37. package/dist/icon-data.js.map +1 -0
  38. package/dist/icon.d.ts +33 -0
  39. package/dist/icon.d.ts.map +1 -0
  40. package/dist/icon.js +13 -0
  41. package/dist/icon.js.map +1 -0
  42. package/dist/index.d.ts +13 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +41 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/layout.d.ts +166 -0
  47. package/dist/layout.d.ts.map +1 -0
  48. package/dist/layout.js +120 -0
  49. package/dist/layout.js.map +1 -0
  50. package/dist/navigation.d.ts +150 -0
  51. package/dist/navigation.d.ts.map +1 -0
  52. package/dist/navigation.js +104 -0
  53. package/dist/navigation.js.map +1 -0
  54. package/dist/overlays.d.ts +68 -0
  55. package/dist/overlays.d.ts.map +1 -0
  56. package/dist/overlays.js +114 -0
  57. package/dist/overlays.js.map +1 -0
  58. package/dist/surfaces.d.ts +61 -0
  59. package/dist/surfaces.d.ts.map +1 -0
  60. package/dist/surfaces.js +54 -0
  61. package/dist/surfaces.js.map +1 -0
  62. package/dist/typography.d.ts +89 -0
  63. package/dist/typography.d.ts.map +1 -0
  64. package/dist/typography.js +76 -0
  65. package/dist/typography.js.map +1 -0
  66. package/package.json +36 -4
  67. package/react/actions.tsx +138 -0
  68. package/react/catalogue.tsx +1644 -0
  69. package/react/content.tsx +313 -0
  70. package/react/css-inline.ts +14 -0
  71. package/react/cx.ts +20 -0
  72. package/react/data.tsx +300 -0
  73. package/react/feedback.tsx +229 -0
  74. package/react/forms.tsx +383 -0
  75. package/react/icon-data.ts +114 -0
  76. package/react/icon.tsx +84 -0
  77. package/react/index.ts +42 -0
  78. package/react/layout.tsx +347 -0
  79. package/react/navigation.tsx +329 -0
  80. package/react/overlays.tsx +237 -0
  81. package/react/surfaces.tsx +161 -0
  82. package/react/typography.tsx +220 -0
package/README.md CHANGED
@@ -50,6 +50,66 @@ unique à 768px reprend les valeurs mobiles du relevé.
50
50
  Chaque valeur vient des maquettes de la passation. Là où deux planches se
51
51
  contredisaient, l'arbitrage est écrit dans [DECISIONS.md](DECISIONS.md).
52
52
 
53
+ ### Les composants React
54
+
55
+ ```bash
56
+ pnpm add @zevra/ui react
57
+ ```
58
+
59
+ ```tsx
60
+ import '@zevra/ui/tokens.css'
61
+ import '@zevra/ui/base.css'
62
+ import '@zevra/ui/forms.css'
63
+
64
+ import { Button, Field, Input } from '@zevra/ui/react'
65
+
66
+ <Field label="ADRESSE E-MAIL" htmlFor="mail" help="Votre adresse professionnelle.">
67
+ <Input id="mail" type="email" />
68
+ </Field>
69
+ <Button>Envoyer</Button>
70
+ ```
71
+
72
+ Ce ne sont **pas** des composants qui embarquent leur style : chacun n'est
73
+ qu'une façade typée et documentée au-dessus des classes `zv-`. Le CSS reste
74
+ la source de vérité, il s'importe à part, et un test
75
+ (`tests/react.test.js`) vérifie que toute classe qu'un composant peut émettre
76
+ existe bien dans un module CSS — c'est ce qui empêche les deux de diverger.
77
+
78
+ **Rendu serveur.** Un seul module est marqué `"use client"` : `overlays`
79
+ (tiroir, modale, notification), les seuls qui aient besoin du navigateur.
80
+ Tout le reste se rend au serveur — dans un composant serveur Next comme dans
81
+ une île Astro à zéro JavaScript. Les composants qui prennent un gestionnaire
82
+ d'événement (`Switch`, `Seg`, `Tabs`…) restent sans état : c'est un composant
83
+ client à toi qui détient l'état et les rend.
84
+
85
+ **Astro.** `@astrojs/react` rend ces composants en HTML statique à la
86
+ compilation, sans JavaScript envoyé, tant qu'aucune directive `client:*` n'est
87
+ posée. Un `<Button>` dans une `.astro` ne coûte donc rien de plus qu'un
88
+ `<button class="zv-btn">` — avec les props typées et la documentation en
89
+ prime. ⚠️ En revanche, un composant `.astro` ne peut pas être passé en enfant
90
+ d'un composant React : ce sens-là ne fonctionne pas.
91
+
92
+ **Les props sont en français**, comme le CSS et les commentaires. Celles qui
93
+ portent une décision de charte sont documentées à l'endroit où on les lit, et
94
+ les avertissements ⚠️ signalent ce qui casse le système si on le contourne —
95
+ pas de simples préférences.
96
+
97
+ **`className` est accepté partout**, mais c'est une sortie de secours, pas un
98
+ usage courant : une classe locale qui repeint un composant du système est une
99
+ divergence qui n'a pas dit son nom.
100
+
101
+ ### Le catalogue
102
+
103
+ ```tsx
104
+ import { CATALOGUE } from '@zevra/ui/react/catalogue'
105
+ ```
106
+
107
+ Chaque composant y a un exemple rendu, le code qui le produit, et le résumé de
108
+ ce qu'il fait — et surtout de quand ne pas l'employer. C'est ce que rend la
109
+ page « Design system » de l'admin, et c'est aussi le banc d'essai du test de
110
+ dérive : un composant sans exemple n'est ni documenté ni couvert, et le test
111
+ le refuse.
112
+
53
113
  ## Deux couches
54
114
 
55
115
  Le paquet en porte deux, et elles ne servent pas au même usage.
@@ -167,7 +227,10 @@ Tiroir et modale s'ouvrent par un état porté sur le conteneur. Deux écritures
167
227
  panneau resterait ouvert. C'est précisément pour cela que la variante en
168
228
  classe existe.
169
229
 
170
- Fermer sur Échap relève du gabarit : le CSS ne sait pas le faire.
230
+ Fermer sur Échap relève du gabarit : le CSS ne sait pas le faire. En React,
231
+ `Drawer` et `Modal` (`@zevra/ui/react`) le font déjà, avec le piège de focus,
232
+ la restitution du focus au bouton d'ouverture et le verrou de défilement —
233
+ et le hook `useOverlay` est exporté pour un enrobage maison.
171
234
 
172
235
  ## Les règles qui ne se négocient pas
173
236
 
@@ -0,0 +1,41 @@
1
+ import type { AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react';
2
+ /** Variantes de bouton. Une seule pleine par écran — c'est la règle de charte. */
3
+ export type BoutonVariante = 'plein' | 'fantome' | 'encre';
4
+ export interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className'> {
5
+ variante?: BoutonVariante;
6
+ /** Hauteur réduite, pour une barre de navigation. */
7
+ compact?: boolean;
8
+ /** Pleine largeur — l'appel à l'action d'un écran mobile. */
9
+ bloc?: boolean;
10
+ /** Échappatoire assumée. À employer pour un ajustement de position, jamais
11
+ * pour redéfinir une couleur ou une taille : c'est par là que les apps
12
+ * recommencent à écrire leur propre charte. */
13
+ className?: string;
14
+ children?: ReactNode;
15
+ }
16
+ export declare function Button({ variante, compact, bloc, className, type, ...reste }: ButtonProps): import("react").JSX.Element;
17
+ export interface ButtonLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'className'> {
18
+ variante?: BoutonVariante;
19
+ compact?: boolean;
20
+ bloc?: boolean;
21
+ className?: string;
22
+ }
23
+ /** Le même bouton, mais qui navigue. Un <a> et un <button> ne s'échangent pas :
24
+ * l'un ouvre dans un onglet, se copie et s'indexe, l'autre non. */
25
+ export declare function ButtonLink({ variante, compact, bloc, className, ...reste }: ButtonLinkProps): import("react").JSX.Element;
26
+ export interface ButtonRowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'className'> {
27
+ /** Empile les boutons et les étire — la disposition mobile. */
28
+ empile?: boolean;
29
+ className?: string;
30
+ }
31
+ export declare function ButtonRow({ empile, className, ...reste }: ButtonRowProps): import("react").JSX.Element;
32
+ export interface LinkArrowProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'className'> {
33
+ /** Sur la bande manifeste, où le lien passe en blanc. */
34
+ surSombre?: boolean;
35
+ className?: string;
36
+ children?: ReactNode;
37
+ }
38
+ /** Lien fléché. La flèche est fournie : elle fait partie du composant, pas du
39
+ * contenu — la recopier à la main a produit trois tracés différents. */
40
+ export declare function LinkArrow({ surSombre, className, children, ...reste }: LinkArrowProps): import("react").JSX.Element;
41
+ //# sourceMappingURL=actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../react/actions.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,SAAS,EACV,MAAM,OAAO,CAAA;AAGd,kFAAkF;AAClF,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAA;AAQ1D,MAAM,WAAW,WACf,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC;IAClE,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,qDAAqD;IACrD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;oDAEgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED,wBAAgB,MAAM,CAAC,EACrB,QAAkB,EAClB,OAAO,EACP,IAAI,EACJ,SAAS,EACT,IAAe,EACf,GAAG,KAAK,EACT,EAAE,WAAW,+BAab;AAED,MAAM,WAAW,eACf,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC;IAClE,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;oEACoE;AACpE,wBAAgB,UAAU,CAAC,EACzB,QAAkB,EAClB,OAAO,EACP,IAAI,EACJ,SAAS,EACT,GAAG,KAAK,EACT,EAAE,eAAe,+BAYjB;AAED,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC;IACzD,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,+BAOxE;AAED,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC;IAClE,yDAAyD;IACzD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED;yEACyE;AACzE,wBAAgB,SAAS,CAAC,EACxB,SAAS,EACT,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,cAAc,+BAiBhB"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from './cx.js';
3
+ const VARIANTE_BOUTON = {
4
+ plein: 'zv-btn',
5
+ fantome: 'zv-btn--ghost',
6
+ encre: 'zv-btn--ink',
7
+ };
8
+ export function Button({ variante = 'plein', compact, bloc, className, type = 'button', ...reste }) {
9
+ return (_jsx("button", { type: type, className: cx(VARIANTE_BOUTON[variante], compact && 'zv-btn--sm', bloc && 'zv-btn--block', className), ...reste }));
10
+ }
11
+ /** Le même bouton, mais qui navigue. Un <a> et un <button> ne s'échangent pas :
12
+ * l'un ouvre dans un onglet, se copie et s'indexe, l'autre non. */
13
+ export function ButtonLink({ variante = 'plein', compact, bloc, className, ...reste }) {
14
+ return (_jsx("a", { className: cx(VARIANTE_BOUTON[variante], compact && 'zv-btn--sm', bloc && 'zv-btn--block', className), ...reste }));
15
+ }
16
+ export function ButtonRow({ empile, className, ...reste }) {
17
+ return (_jsx("div", { className: cx('zv-btn-row', empile && 'zv-btn-row--stack', className), ...reste }));
18
+ }
19
+ /** Lien fléché. La flèche est fournie : elle fait partie du composant, pas du
20
+ * contenu — la recopier à la main a produit trois tracés différents. */
21
+ export function LinkArrow({ surSombre, className, children, ...reste }) {
22
+ return (_jsxs("a", { className: cx('zv-link-arrow', surSombre && 'zv-link-arrow--on-dark', className), ...reste, children: [children, _jsxs("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("path", { d: "M5 12h14" }), _jsx("path", { d: "M13 6l6 6-6 6" })] })] }));
23
+ }
24
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../react/actions.tsx"],"names":[],"mappings":";AAYA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAK5B,MAAM,eAAe,GAAmC;IACtD,KAAK,EAAE,QAAQ;IACf,OAAO,EAAE,eAAe;IACxB,KAAK,EAAE,aAAa;CACrB,CAAA;AAgBD,MAAM,UAAU,MAAM,CAAC,EACrB,QAAQ,GAAG,OAAO,EAClB,OAAO,EACP,IAAI,EACJ,SAAS,EACT,IAAI,GAAG,QAAQ,EACf,GAAG,KAAK,EACI;IACZ,OAAO,CACL,iBACE,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,EAAE,CACX,eAAe,CAAC,QAAQ,CAAC,EACzB,OAAO,IAAI,YAAY,EACvB,IAAI,IAAI,eAAe,EACvB,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAA;AACH,CAAC;AAUD;oEACoE;AACpE,MAAM,UAAU,UAAU,CAAC,EACzB,QAAQ,GAAG,OAAO,EAClB,OAAO,EACP,IAAI,EACJ,SAAS,EACT,GAAG,KAAK,EACQ;IAChB,OAAO,CACL,YACE,SAAS,EAAE,EAAE,CACX,eAAe,CAAC,QAAQ,CAAC,EACzB,OAAO,IAAI,YAAY,EACvB,IAAI,IAAI,eAAe,EACvB,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAA;AACH,CAAC;AASD,MAAM,UAAU,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,EAAkB;IACvE,OAAO,CACL,cACE,SAAS,EAAE,EAAE,CAAC,YAAY,EAAE,MAAM,IAAI,mBAAmB,EAAE,SAAS,CAAC,KACjE,KAAK,GACT,CACH,CAAA;AACH,CAAC;AAUD;yEACyE;AACzE,MAAM,UAAU,SAAS,CAAC,EACxB,SAAS,EACT,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACO;IACf,OAAO,CACL,aACE,SAAS,EAAE,EAAE,CACX,eAAe,EACf,SAAS,IAAI,wBAAwB,EACrC,SAAS,CACV,KACG,KAAK,aAER,QAAQ,EACT,eAAK,OAAO,EAAC,WAAW,iBAAa,MAAM,aACzC,eAAM,CAAC,EAAC,UAAU,GAAG,EACrB,eAAM,CAAC,EAAC,eAAe,GAAG,IACtB,IACJ,CACL,CAAA;AACH,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { type ReactNode } from 'react';
2
+ export interface Exemple {
3
+ /** Identifiant stable — sert d'ancre dans la page de consultation. */
4
+ id: string;
5
+ nom: string;
6
+ /** Ce que le composant fait, et surtout quand NE PAS l'employer. */
7
+ resume: string;
8
+ /** Le code qui produit `rendu`, à copier tel quel. */
9
+ code: string;
10
+ rendu: ReactNode;
11
+ /** Rendu sur la bande manifeste plutôt que sur papier. */
12
+ surSombre?: boolean;
13
+ }
14
+ export interface GroupeCatalogue {
15
+ id: string;
16
+ nom: string;
17
+ resume: string;
18
+ /** `applicatif` : interfaces internes. `vitrine` : pages publiques.
19
+ * `commun` : les deux. */
20
+ couche: 'commun' | 'applicatif' | 'vitrine';
21
+ /** Le module CSS à charger pour ce groupe. */
22
+ css: string;
23
+ exemples: Exemple[];
24
+ }
25
+ export declare const CATALOGUE: GroupeCatalogue[];
26
+ /** Tous les exemples à plat — pratique pour une recherche ou un test. */
27
+ export declare const EXEMPLES: (Exemple & {
28
+ groupe: string;
29
+ })[];
30
+ //# sourceMappingURL=catalogue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogue.d.ts","sourceRoot":"","sources":["../react/catalogue.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAoChD,MAAM,WAAW,OAAO;IACtB,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,MAAM,CAAA;IACX,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAA;IACd,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,SAAS,CAAA;IAChB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd;+BAC2B;IAC3B,MAAM,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAA;IAC3C,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,OAAO,EAAE,CAAA;CACpB;AA0JD,eAAO,MAAM,SAAS,EAAE,eAAe,EA63CtC,CAAA;AAMD,yEAAyE;AACzE,eAAO,MAAM,QAAQ,EAAE,CAAC,OAAO,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAEpD,CAAA"}