@zevra/ui 0.3.2 → 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.
- package/DECISIONS.md +25 -0
- package/README.md +83 -11
- package/dist/actions.d.ts +41 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +24 -0
- package/dist/actions.js.map +1 -0
- package/dist/catalogue.d.ts +30 -0
- package/dist/catalogue.d.ts.map +1 -0
- package/dist/catalogue.js +830 -0
- package/dist/catalogue.js.map +1 -0
- package/dist/content.d.ts +135 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +85 -0
- package/dist/content.js.map +1 -0
- package/dist/css-inline.d.ts +11 -0
- package/dist/css-inline.d.ts.map +1 -0
- package/dist/css-inline.js +13 -0
- package/dist/css-inline.js.map +1 -0
- package/dist/cx.d.ts +16 -0
- package/dist/cx.d.ts.map +1 -0
- package/dist/cx.js +20 -0
- package/dist/cx.js.map +1 -0
- package/dist/data.d.ts +136 -0
- package/dist/data.d.ts.map +1 -0
- package/dist/data.js +98 -0
- package/dist/data.js.map +1 -0
- package/dist/feedback.d.ts +88 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +38 -0
- package/dist/feedback.js.map +1 -0
- package/dist/forms.d.ts +120 -0
- package/dist/forms.d.ts.map +1 -0
- package/dist/forms.js +60 -0
- package/dist/forms.js.map +1 -0
- package/dist/icon-data.d.ts +6 -0
- package/dist/icon-data.d.ts.map +1 -0
- package/dist/icon-data.js +77 -0
- package/dist/icon-data.js.map +1 -0
- package/dist/icon.d.ts +33 -0
- package/dist/icon.d.ts.map +1 -0
- package/dist/icon.js +13 -0
- package/dist/icon.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +166 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +120 -0
- package/dist/layout.js.map +1 -0
- package/dist/navigation.d.ts +150 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +104 -0
- package/dist/navigation.js.map +1 -0
- package/dist/overlays.d.ts +68 -0
- package/dist/overlays.d.ts.map +1 -0
- package/dist/overlays.js +114 -0
- package/dist/overlays.js.map +1 -0
- package/dist/surfaces.d.ts +61 -0
- package/dist/surfaces.d.ts.map +1 -0
- package/dist/surfaces.js +54 -0
- package/dist/surfaces.js.map +1 -0
- package/dist/typography.d.ts +89 -0
- package/dist/typography.d.ts.map +1 -0
- package/dist/typography.js +76 -0
- package/dist/typography.js.map +1 -0
- package/logos/index.json +10 -500
- package/package.json +36 -4
- package/react/actions.tsx +138 -0
- package/react/catalogue.tsx +1644 -0
- package/react/content.tsx +313 -0
- package/react/css-inline.ts +14 -0
- package/react/cx.ts +20 -0
- package/react/data.tsx +300 -0
- package/react/feedback.tsx +229 -0
- package/react/forms.tsx +383 -0
- package/react/icon-data.ts +114 -0
- package/react/icon.tsx +84 -0
- package/react/index.ts +42 -0
- package/react/layout.tsx +347 -0
- package/react/navigation.tsx +329 -0
- package/react/overlays.tsx +237 -0
- package/react/surfaces.tsx +161 -0
- package/react/typography.tsx +220 -0
- package/logos/png/campus-glyph-ink.png +0 -0
- package/logos/png/campus-glyph-white.png +0 -0
- package/logos/png/campus-lockup.png +0 -0
- package/logos/png/lexform-glyph-ink.png +0 -0
- package/logos/png/lexform-glyph-white.png +0 -0
- package/logos/png/lexform-lockup.png +0 -0
- package/logos/png/mcp-glyph-ink.png +0 -0
- package/logos/png/mcp-glyph-white.png +0 -0
- package/logos/png/mcp-lockup.png +0 -0
- package/logos/png/memoire-dark.png +0 -0
- package/logos/png/memoire-glyph-ink.png +0 -0
- package/logos/png/memoire-glyph-white.png +0 -0
- package/logos/png/memoire-lockup.png +0 -0
- package/logos/png/zevra-lockup-ink.png +0 -0
- package/logos/png/zevra-lockup-white.png +0 -0
- package/logos/png/zevra-lockup.png +0 -0
- package/logos/png/zevra-mark-ink.png +0 -0
- package/logos/png/zevra-mark-white.png +0 -0
- package/logos/png/zevra-mark.png +0 -0
- package/logos/svg/campus-glyph-ink.svg +0 -1
- package/logos/svg/campus-glyph-navy.svg +0 -1
- package/logos/svg/campus-glyph-orig.svg +0 -1
- package/logos/svg/campus-glyph-white.svg +0 -1
- package/logos/svg/campus-lockup-ink.svg +0 -1
- package/logos/svg/campus-lockup-navy.svg +0 -1
- package/logos/svg/campus-lockup-orig.svg +0 -1
- package/logos/svg/campus-lockup-white.svg +0 -1
- package/logos/svg/lexform-glyph-ink.svg +0 -1
- package/logos/svg/lexform-glyph-navy.svg +0 -1
- package/logos/svg/lexform-glyph-orig.svg +0 -1
- package/logos/svg/lexform-glyph-white.svg +0 -1
- package/logos/svg/lexform-lockup-ink.svg +0 -1
- package/logos/svg/lexform-lockup-navy.svg +0 -1
- package/logos/svg/lexform-lockup-orig.svg +0 -1
- package/logos/svg/lexform-lockup-white.svg +0 -1
- package/logos/svg/mcp-glyph-ink.svg +0 -1
- package/logos/svg/mcp-glyph-navy.svg +0 -1
- package/logos/svg/mcp-glyph-orig.svg +0 -1
- package/logos/svg/mcp-glyph-white.svg +0 -1
- package/logos/svg/mcp-lockup-ink.svg +0 -1
- package/logos/svg/mcp-lockup-navy.svg +0 -1
- package/logos/svg/mcp-lockup-orig.svg +0 -1
- package/logos/svg/mcp-lockup-white.svg +0 -1
- package/logos/svg/memoire-glyph-ink.svg +0 -1
- package/logos/svg/memoire-glyph-navy.svg +0 -1
- package/logos/svg/memoire-glyph-orig.svg +0 -1
- package/logos/svg/memoire-glyph-white.svg +0 -1
- package/logos/svg/memoire-lockup-ink.svg +0 -1
- package/logos/svg/memoire-lockup-navy.svg +0 -1
- package/logos/svg/memoire-lockup-orig.svg +0 -1
- package/logos/svg/memoire-lockup-white.svg +0 -1
- package/logos/svg/zevra-lockup-ink.svg +0 -1
- package/logos/svg/zevra-lockup-navy.svg +0 -1
- package/logos/svg/zevra-lockup-orig.svg +0 -1
- package/logos/svg/zevra-lockup-white.svg +0 -1
- package/logos/svg/zevra-mark-ink.svg +0 -1
- package/logos/svg/zevra-mark-navy.svg +0 -1
- package/logos/svg/zevra-mark-orig.svg +0 -1
- package/logos/svg/zevra-mark-white.svg +0 -1
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// Contenu — surfaces, badges et blocs éditoriaux. Sans état, donc sans
|
|
2
|
+
// « use client ».
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes, ReactNode } from 'react'
|
|
5
|
+
import { cx } from './cx.js'
|
|
6
|
+
|
|
7
|
+
type Div = Omit<HTMLAttributes<HTMLDivElement>, 'className'>
|
|
8
|
+
type Span = Omit<HTMLAttributes<HTMLSpanElement>, 'className'>
|
|
9
|
+
|
|
10
|
+
export interface CardProps extends Div {
|
|
11
|
+
/** Retrait intérieur. Sans, la carte est nue — à toi de poser le tien. */
|
|
12
|
+
padding?: 'aucun' | 'normal' | 'large'
|
|
13
|
+
/** Filet coloré : un état porté par la bordure, pas par un fond. */
|
|
14
|
+
etat?: 'ok' | 'err'
|
|
15
|
+
/** Ombre portée, pour une carte qui flotte au-dessus d'une scène. */
|
|
16
|
+
flottante?: boolean
|
|
17
|
+
className?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function Card({
|
|
21
|
+
padding = 'normal',
|
|
22
|
+
etat,
|
|
23
|
+
flottante,
|
|
24
|
+
className,
|
|
25
|
+
...reste
|
|
26
|
+
}: CardProps) {
|
|
27
|
+
return (
|
|
28
|
+
<div
|
|
29
|
+
className={cx(
|
|
30
|
+
'zv-card',
|
|
31
|
+
padding === 'normal' && 'zv-card--pad',
|
|
32
|
+
padding === 'large' && 'zv-card--pad-lg',
|
|
33
|
+
etat === 'ok' && 'zv-card--ok',
|
|
34
|
+
etat === 'err' && 'zv-card--err',
|
|
35
|
+
flottante && 'zv-card--float',
|
|
36
|
+
className,
|
|
37
|
+
)}
|
|
38
|
+
{...reste}
|
|
39
|
+
/>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Surface semi-opaque. ⚠️ Ne fonctionne QUE devant un halo ou une scène :
|
|
44
|
+
* posée sur du papier nu, elle disparaît. */
|
|
45
|
+
export function Glass({ className, ...reste }: Div & { className?: string }) {
|
|
46
|
+
return <div className={cx('zv-glass', className)} {...reste} />
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface InsetProps extends Div {
|
|
50
|
+
/** Variante document : fond panel, texte encre, très aérée. */
|
|
51
|
+
discret?: boolean
|
|
52
|
+
/** Poussé en bas de la carte qui l'accueille. */
|
|
53
|
+
enBas?: boolean
|
|
54
|
+
className?: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Encart dans une carte. Deux objets distincts au relevé, longtemps
|
|
58
|
+
* confondus : la citation d'interface et l'extrait de document. */
|
|
59
|
+
export function Inset({ discret, enBas, className, ...reste }: InsetProps) {
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
className={cx(
|
|
63
|
+
'zv-inset',
|
|
64
|
+
discret && 'zv-inset--quiet',
|
|
65
|
+
enBas && 'zv-inset--bottom',
|
|
66
|
+
className,
|
|
67
|
+
)}
|
|
68
|
+
{...reste}
|
|
69
|
+
/>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function Pill({ className, ...reste }: Span & { className?: string }) {
|
|
74
|
+
return <span className={cx('zv-pill', className)} {...reste} />
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface SrcProps extends Span {
|
|
78
|
+
/** Le « + 11 » qui clôt une liste de sources. */
|
|
79
|
+
attenue?: boolean
|
|
80
|
+
className?: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Puce de source — BOSS, Judilibre, KALI. */
|
|
84
|
+
export function Src({ attenue, className, ...reste }: SrcProps) {
|
|
85
|
+
return (
|
|
86
|
+
<span
|
|
87
|
+
className={cx('zv-src', attenue && 'zv-src--quiet', className)}
|
|
88
|
+
{...reste}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function SrcRow({ className, ...reste }: Span & { className?: string }) {
|
|
94
|
+
return <span className={cx('zv-src-row', className)} {...reste} />
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface TagProps extends Span {
|
|
98
|
+
etat?: 'ok' | 'err'
|
|
99
|
+
/** Sur la bande manifeste : les états s'éclaircissent, sinon ils virent au noir. */
|
|
100
|
+
surSombre?: boolean
|
|
101
|
+
className?: string
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function Tag({ etat, surSombre, className, ...reste }: TagProps) {
|
|
105
|
+
return (
|
|
106
|
+
<span
|
|
107
|
+
className={cx(
|
|
108
|
+
'zv-tag',
|
|
109
|
+
etat === 'ok' && (surSombre ? 'zv-tag--ok-on-dark' : 'zv-tag--ok'),
|
|
110
|
+
etat === 'err' && (surSombre ? 'zv-tag--err-on-dark' : 'zv-tag--err'),
|
|
111
|
+
className,
|
|
112
|
+
)}
|
|
113
|
+
{...reste}
|
|
114
|
+
/>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Entité pseudonymisée, posée dans le fil du texte. */
|
|
119
|
+
export function Ent({ className, ...reste }: Span & { className?: string }) {
|
|
120
|
+
return <span className={cx('zv-ent', className)} {...reste} />
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Le paragraphe qui accueille des entités : il respire davantage, sinon les
|
|
124
|
+
* jetons se télescopent d'une ligne à l'autre. */
|
|
125
|
+
export function EntText({ className, ...reste }: Span & { className?: string }) {
|
|
126
|
+
return <span className={cx('zv-ent-text', className)} {...reste} />
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface DividerProps extends Div {
|
|
130
|
+
/** Filet plein plutôt que cheveu. */
|
|
131
|
+
marque?: boolean
|
|
132
|
+
surSombre?: boolean
|
|
133
|
+
className?: string
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function Divider({ marque, surSombre, className, ...reste }: DividerProps) {
|
|
137
|
+
return (
|
|
138
|
+
<div
|
|
139
|
+
className={cx(
|
|
140
|
+
'zv-divider',
|
|
141
|
+
marque && 'zv-divider--strong',
|
|
142
|
+
surSombre && 'zv-divider--on-dark',
|
|
143
|
+
className,
|
|
144
|
+
)}
|
|
145
|
+
{...reste}
|
|
146
|
+
/>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface QuoteProps extends Div {
|
|
151
|
+
auteur?: ReactNode
|
|
152
|
+
className?: string
|
|
153
|
+
children?: ReactNode
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** ⚠️ RÈGLE DE CHARTE : jamais de fausse citation. Tant qu'un verbatim client
|
|
157
|
+
* n'a pas été recueilli, ce bloc reste en réserve — un placeholder crédible
|
|
158
|
+
* finit toujours par partir en production. */
|
|
159
|
+
export function Quote({ auteur, className, children, ...reste }: QuoteProps) {
|
|
160
|
+
return (
|
|
161
|
+
<div className={cx('zv-quote', className)} {...reste}>
|
|
162
|
+
<span className="zv-spec zv-spec--on-dark" aria-hidden="true" />
|
|
163
|
+
<span className="zv-quote__text">{children}</span>
|
|
164
|
+
{auteur && <span className="zv-mono zv-mono--micro zv-mono--on-dark">{auteur}</span>}
|
|
165
|
+
</div>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface PriceProps extends Div {
|
|
170
|
+
montant: ReactNode
|
|
171
|
+
/** Le « HT · 1re année » accolé au montant. */
|
|
172
|
+
unite?: ReactNode
|
|
173
|
+
/** La mention sous la carte. */
|
|
174
|
+
note?: ReactNode
|
|
175
|
+
/** La formule recommandée — bandeau indigo. Trois cartes par page au plus. */
|
|
176
|
+
recommande?: boolean
|
|
177
|
+
className?: string
|
|
178
|
+
children?: ReactNode
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function Price({
|
|
182
|
+
montant,
|
|
183
|
+
unite,
|
|
184
|
+
note,
|
|
185
|
+
recommande,
|
|
186
|
+
className,
|
|
187
|
+
children,
|
|
188
|
+
...reste
|
|
189
|
+
}: PriceProps) {
|
|
190
|
+
return (
|
|
191
|
+
<div
|
|
192
|
+
className={cx('zv-price', recommande && 'zv-price--featured', className)}
|
|
193
|
+
{...reste}
|
|
194
|
+
>
|
|
195
|
+
{children}
|
|
196
|
+
<span className="zv-price__amount">
|
|
197
|
+
{montant}
|
|
198
|
+
{unite && <span className="zv-price__unit"> {unite}</span>}
|
|
199
|
+
</span>
|
|
200
|
+
{note && <span className="zv-price__note">{note}</span>}
|
|
201
|
+
</div>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* ─── Statistiques de carte ────────────────────────────────────────── */
|
|
206
|
+
|
|
207
|
+
export function MiniStat({ className, ...reste }: Div & { className?: string }) {
|
|
208
|
+
return <div className={cx('zv-ministat', className)} {...reste} />
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface MiniStatItemProps extends Div {
|
|
212
|
+
valeur: ReactNode
|
|
213
|
+
className?: string
|
|
214
|
+
children?: ReactNode
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Paire de mini-statistiques au pied d'une carte produit. */
|
|
218
|
+
export function MiniStatItem({ valeur, className, children, ...reste }: MiniStatItemProps) {
|
|
219
|
+
return (
|
|
220
|
+
<div className={cx('zv-ministat__item', className)} {...reste}>
|
|
221
|
+
<span className="zv-ministat__value">{valeur}</span>
|
|
222
|
+
{children}
|
|
223
|
+
</div>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface StatCardProps extends Div {
|
|
228
|
+
valeur: ReactNode
|
|
229
|
+
className?: string
|
|
230
|
+
children?: ReactNode
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function StatCard({ valeur, className, children, ...reste }: StatCardProps) {
|
|
234
|
+
return (
|
|
235
|
+
<div className={cx('zv-stat-card', className)} {...reste}>
|
|
236
|
+
<span className="zv-stat-card__value">{valeur}</span>
|
|
237
|
+
{children}
|
|
238
|
+
</div>
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* ─── FAQ ──────────────────────────────────────────────────────────── */
|
|
243
|
+
|
|
244
|
+
/** ⚠️ Cinq questions par page au MAXIMUM. Au-delà, ce n'est plus une FAQ,
|
|
245
|
+
* c'est une documentation mal rangée. */
|
|
246
|
+
export function Faq({ className, ...reste }: Div & { className?: string }) {
|
|
247
|
+
return <div className={cx('zv-faq', className)} {...reste} />
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export interface FaqItemProps extends Div {
|
|
251
|
+
question: ReactNode
|
|
252
|
+
className?: string
|
|
253
|
+
children?: ReactNode
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Rendu ouvert, sans repli : la réponse est le contenu, la cacher derrière
|
|
257
|
+
* un clic n'ajoute rien à cinq questions. Pour un vrai accordéon, c'est un
|
|
258
|
+
* `<details>` — et il n'existe pas de style pour lui dans le paquet. */
|
|
259
|
+
export function FaqItem({ question, className, children, ...reste }: FaqItemProps) {
|
|
260
|
+
return (
|
|
261
|
+
<div className={className} {...reste}>
|
|
262
|
+
<p className="zv-faq__q">{question}</p>
|
|
263
|
+
<p className="zv-faq__a">{children}</p>
|
|
264
|
+
</div>
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* ─── Aperçu d'interface ───────────────────────────────────────────── */
|
|
269
|
+
|
|
270
|
+
export interface UiPreviewProps extends Div {
|
|
271
|
+
question: ReactNode
|
|
272
|
+
/** Le compteur de sources, seul élément mis en avant de la réponse. */
|
|
273
|
+
sources?: ReactNode
|
|
274
|
+
/** Les puces de source, sous la réponse. */
|
|
275
|
+
puces?: ReactNode
|
|
276
|
+
className?: string
|
|
277
|
+
children?: ReactNode
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** La preuve produit : une question, une réponse, ses sources.
|
|
281
|
+
* ⚠️ RÈGLE DE CHARTE : toujours de VRAIES données réalistes, jamais de
|
|
282
|
+
* lorem — c'est ce bloc qui porte la crédibilité de la page. */
|
|
283
|
+
export function UiPreview({
|
|
284
|
+
question,
|
|
285
|
+
sources,
|
|
286
|
+
puces,
|
|
287
|
+
className,
|
|
288
|
+
children,
|
|
289
|
+
...reste
|
|
290
|
+
}: UiPreviewProps) {
|
|
291
|
+
return (
|
|
292
|
+
<div className={cx('zv-ui-preview', className)} {...reste}>
|
|
293
|
+
<p className="zv-ui-preview__q">{question}</p>
|
|
294
|
+
<p className="zv-ui-preview__a">{children}</p>
|
|
295
|
+
{sources && <span className="zv-ui-preview__count">{sources}</span>}
|
|
296
|
+
{puces}
|
|
297
|
+
</div>
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* ─── Pastille de validation ───────────────────────────────────────── */
|
|
302
|
+
|
|
303
|
+
/** ⚠️ À ne pas confondre avec `Checkbox` (forms) : celle-ci n'est PAS un
|
|
304
|
+
* contrôle, c'est une coche décorative en tête de liste d'arguments. */
|
|
305
|
+
export function Check({ className, ...reste }: Span & { className?: string }) {
|
|
306
|
+
return (
|
|
307
|
+
<span className={cx('zv-check', className)} aria-hidden="true" {...reste}>
|
|
308
|
+
<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="12" height="12">
|
|
309
|
+
<polyline points="2.5,6.5 5,9 9.5,3.5" />
|
|
310
|
+
</svg>
|
|
311
|
+
</span>
|
|
312
|
+
)
|
|
313
|
+
}
|