@zevra/ui 0.43.0 → 0.44.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 +49 -0
- package/dist/css-inline.d.ts.map +1 -1
- package/dist/css-inline.js +1 -1
- package/dist/css-inline.js.map +1 -1
- package/dist/data-table.d.ts +33 -3
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +58 -40
- package/dist/data-table.js.map +1 -1
- package/dist/grille.d.ts +93 -8
- package/dist/grille.d.ts.map +1 -1
- package/dist/grille.js +251 -112
- package/dist/grille.js.map +1 -1
- package/package.json +1 -1
- package/react/css-inline.ts +1 -1
- package/react/data-table.tsx +87 -6
- package/react/grille.tsx +627 -268
- package/src/data.css +101 -10
package/react/data-table.tsx
CHANGED
|
@@ -15,11 +15,12 @@
|
|
|
15
15
|
// variables. Aucune classe nouvelle n'entre par cette porte : ce sont les
|
|
16
16
|
// mêmes .zv-table / .zv-row / .zv-cell que le tableau écrit à la main.
|
|
17
17
|
|
|
18
|
-
import { Fragment, type CSSProperties, type ReactNode } from 'react'
|
|
18
|
+
import { Fragment, type CSSProperties, type DragEvent, type ReactNode } from 'react'
|
|
19
19
|
import {
|
|
20
20
|
flexRender,
|
|
21
21
|
type Column,
|
|
22
22
|
type Header,
|
|
23
|
+
type Row,
|
|
23
24
|
type RowData,
|
|
24
25
|
type Table as TableTanstack,
|
|
25
26
|
} from '@tanstack/react-table'
|
|
@@ -81,6 +82,36 @@ export interface DataTableProps<T> {
|
|
|
81
82
|
/** Appelé au clic sur une ligne. Sans lui, la ligne n'est pas cliquable :
|
|
82
83
|
* et elle ne doit pas en avoir l'air. */
|
|
83
84
|
onRowClick?: (ligne: T) => void
|
|
85
|
+
/** La ligne dont on regarde le détail (l'aperçu ouvert à côté de la
|
|
86
|
+
* liste). Elle porte un filet d'accent à gauche, distinct de la ligne
|
|
87
|
+
* COCHÉE, qui est une sélection pour agir. */
|
|
88
|
+
ligneActive?: (ligne: T) => boolean
|
|
89
|
+
/** Clic droit sur une ligne : l'appelant ouvre son menu contextuel aux
|
|
90
|
+
* coordonnées données. Le menu du navigateur est alors retenu. */
|
|
91
|
+
onRowContextMenu?: (ligne: T, x: number, y: number) => void
|
|
92
|
+
/** Double clic : « ouvrir pour de bon », quand le simple clic ne fait que
|
|
93
|
+
* montrer. La sélection de texte qu'il provoque est effacée. */
|
|
94
|
+
onRowDoubleClick?: (ligne: T) => void
|
|
95
|
+
/** La ligne se saisit (glisser-déposer NATIF) : l'appelant remplit le
|
|
96
|
+
* `dataTransfer`, pour ranger une pièce dans un dossier par exemple. */
|
|
97
|
+
onRowDragStart?: (ligne: T, e: DragEvent<HTMLDivElement>) => void
|
|
98
|
+
/** Les lignes à rendre, dans leur ordre, à la place du modèle de TanStack :
|
|
99
|
+
* pour un ordre que le tri ne sait pas dire (des lignes épinglées en tête
|
|
100
|
+
* quel que soit le tri). */
|
|
101
|
+
lignes?: Row<T>[]
|
|
102
|
+
/** Rangée posée en tête du corps, avant les lignes : une ligne de SAISIE. */
|
|
103
|
+
avantLignes?: ReactNode
|
|
104
|
+
/** Rangée intercalée AVANT une ligne : l'intitulé d'un groupe (une année,
|
|
105
|
+
* un mois) quand la clé change. Rien pour ne rien intercaler. */
|
|
106
|
+
intercalaire?: (ligne: Row<T>, precedente: Row<T> | undefined) => ReactNode
|
|
107
|
+
/** Sous les lignes : un pied de totaux, le déclencheur d'une liste infinie. */
|
|
108
|
+
apresLignes?: ReactNode
|
|
109
|
+
/** Rangées resserrées, pour un tableau secondaire posé dans une fiche. */
|
|
110
|
+
dense?: boolean
|
|
111
|
+
/** Le tableau REMPLIT la hauteur de son parent (une colonne flex bornée) :
|
|
112
|
+
* seules les lignes défilent, les intitulés et les filtres restent en
|
|
113
|
+
* haut. */
|
|
114
|
+
remplir?: boolean
|
|
84
115
|
className?: string
|
|
85
116
|
}
|
|
86
117
|
|
|
@@ -139,6 +170,16 @@ export function DataTable<T>({
|
|
|
139
170
|
onRowClick,
|
|
140
171
|
celluleEntete,
|
|
141
172
|
sousEntete,
|
|
173
|
+
ligneActive,
|
|
174
|
+
onRowContextMenu,
|
|
175
|
+
onRowDoubleClick,
|
|
176
|
+
onRowDragStart,
|
|
177
|
+
lignes: lignesImposees,
|
|
178
|
+
avantLignes,
|
|
179
|
+
intercalaire,
|
|
180
|
+
apresLignes,
|
|
181
|
+
dense,
|
|
182
|
+
remplir,
|
|
142
183
|
className,
|
|
143
184
|
}: DataTableProps<T>) {
|
|
144
185
|
const grille = table.options.enableColumnResizing === true
|
|
@@ -149,7 +190,7 @@ export function DataTable<T>({
|
|
|
149
190
|
grille ? piste(c, reglages[c.id] != null) : (c.columnDef.meta?.largeur ?? 'minmax(0, 1fr)'),
|
|
150
191
|
)
|
|
151
192
|
.join(' ')
|
|
152
|
-
const lignes = table.getRowModel().rows
|
|
193
|
+
const lignes = lignesImposees ?? table.getRowModel().rows
|
|
153
194
|
const style: Record<string, string> = { '--zv-cols': gabarit }
|
|
154
195
|
if (grille) {
|
|
155
196
|
style['--zv-cols-somme'] = `${colonnes.reduce((s, c) => s + c.getSize(), 0)}px`
|
|
@@ -158,9 +199,10 @@ export function DataTable<T>({
|
|
|
158
199
|
|
|
159
200
|
return (
|
|
160
201
|
<div
|
|
161
|
-
className={cx('zv-table', className)}
|
|
202
|
+
className={cx('zv-table', dense && 'zv-table--dense', remplir && 'zv-table--remplir', className)}
|
|
162
203
|
style={style as CSSProperties}
|
|
163
204
|
>
|
|
205
|
+
<Haut actif={remplir}>
|
|
164
206
|
{table.getHeaderGroups().map((groupe) => (
|
|
165
207
|
<div className="zv-table__head" key={groupe.id}>
|
|
166
208
|
{groupe.headers.map((entete) => {
|
|
@@ -213,19 +255,47 @@ export function DataTable<T>({
|
|
|
213
255
|
))}
|
|
214
256
|
|
|
215
257
|
{sousEntete}
|
|
258
|
+
</Haut>
|
|
259
|
+
|
|
260
|
+
{avantLignes}
|
|
216
261
|
|
|
217
262
|
{lignes.length === 0
|
|
218
263
|
? vide
|
|
219
|
-
: lignes.map((ligne) => (
|
|
264
|
+
: lignes.map((ligne, i) => (
|
|
265
|
+
<Fragment key={ligne.id}>
|
|
266
|
+
{intercalaire?.(ligne, lignes[i - 1])}
|
|
220
267
|
<div
|
|
221
|
-
|
|
222
|
-
|
|
268
|
+
className={cx(
|
|
269
|
+
'zv-row',
|
|
270
|
+
ligne.getIsSelected() && 'zv-row--selected',
|
|
271
|
+
ligneActive?.(ligne.original) && 'zv-row--on',
|
|
272
|
+
)}
|
|
273
|
+
aria-current={ligneActive?.(ligne.original) || undefined}
|
|
223
274
|
// Le clavier suit le clic : une ligne cliquable qui n'est pas
|
|
224
275
|
// atteignable au clavier est une action inaccessible.
|
|
225
276
|
role={onRowClick ? 'button' : undefined}
|
|
226
277
|
tabIndex={onRowClick ? 0 : undefined}
|
|
227
278
|
style={onRowClick ? undefined : { cursor: 'default' }}
|
|
228
279
|
onClick={onRowClick ? () => onRowClick(ligne.original) : undefined}
|
|
280
|
+
onContextMenu={
|
|
281
|
+
onRowContextMenu
|
|
282
|
+
? (e) => {
|
|
283
|
+
e.preventDefault()
|
|
284
|
+
onRowContextMenu(ligne.original, e.clientX, e.clientY)
|
|
285
|
+
}
|
|
286
|
+
: undefined
|
|
287
|
+
}
|
|
288
|
+
onDoubleClick={
|
|
289
|
+
onRowDoubleClick
|
|
290
|
+
? (e) => {
|
|
291
|
+
e.preventDefault()
|
|
292
|
+
e.currentTarget.ownerDocument.defaultView?.getSelection()?.removeAllRanges()
|
|
293
|
+
onRowDoubleClick(ligne.original)
|
|
294
|
+
}
|
|
295
|
+
: undefined
|
|
296
|
+
}
|
|
297
|
+
draggable={onRowDragStart ? true : undefined}
|
|
298
|
+
onDragStart={onRowDragStart ? (e) => onRowDragStart(ligne.original, e) : undefined}
|
|
229
299
|
onKeyDown={
|
|
230
300
|
onRowClick
|
|
231
301
|
? (e) => {
|
|
@@ -259,7 +329,18 @@ export function DataTable<T>({
|
|
|
259
329
|
)
|
|
260
330
|
})}
|
|
261
331
|
</div>
|
|
332
|
+
</Fragment>
|
|
262
333
|
))}
|
|
334
|
+
|
|
335
|
+
{apresLignes}
|
|
263
336
|
</div>
|
|
264
337
|
)
|
|
265
338
|
}
|
|
339
|
+
|
|
340
|
+
/** L'en-tête et les filtres, groupés dans un bloc COLLANT quand le tableau
|
|
341
|
+
* remplit son parent : sans ce bloc, chaque rangée collerait au même
|
|
342
|
+
* endroit et la seconde recouvrirait la première. Sinon, rien n'enveloppe :
|
|
343
|
+
* le balisage reste celui d'avant. */
|
|
344
|
+
function Haut({ actif, children }: { actif?: boolean; children: ReactNode }) {
|
|
345
|
+
return actif ? <div className="zv-table__haut">{children}</div> : <>{children}</>
|
|
346
|
+
}
|