@kanunilabs/datagrid-react 1.0.2 → 1.1.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/README.md +247 -246
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +5 -134
- package/dist/index.d.ts +5 -134
- package/dist/index.js +2 -2
- package/dist/styles.css +2380 -2351
- package/package.json +4 -3
package/dist/styles.css
CHANGED
|
@@ -1,2354 +1,2383 @@
|
|
|
1
|
-
/* ─────────────────────────────────────────────────────────────
|
|
2
|
-
KANUNILABS DataGrid — isolated theme system
|
|
3
|
-
─────────────────────────────────────────────────────────────
|
|
4
|
-
Every rule is scoped under .kanuni-datagrid-root so the host app's CSS
|
|
5
|
-
cannot bleed in and ours cannot bleed out. Tokens mirror the PivotGrid
|
|
6
|
-
vocabulary (--pivot-*) with a --dg-* prefix so the two products can be
|
|
7
|
-
themed side by side without collisions.
|
|
8
|
-
───────────────────────────────────────────────────────────── */
|
|
9
|
-
|
|
10
|
-
/* ── Design tokens ───────────────────────────────────────────
|
|
11
|
-
Declared on more than the grid root because two things render OUTSIDE it
|
|
12
|
-
and still have to look like the grid: the enterprise wrapper (which owns
|
|
13
|
-
the row/form editor, a SIBLING of the root) and any layer portalled to
|
|
14
|
-
<body> (the lookup list, tagged `.kanuni-datagrid-detached`).
|
|
15
|
-
|
|
16
|
-
Tokens defined only on the root left those resolving `var(--dg-surface)` to
|
|
17
|
-
nothing — which is not a fallback but an INVALID declaration, so the popup
|
|
18
|
-
editor rendered fully transparent and unreadable over the grid.
|
|
19
|
-
|
|
20
|
-
Only the custom properties live here. The grid's own box (border, radius,
|
|
21
|
-
overflow, flex) stays on the root below: giving it to the wrapper would draw
|
|
22
|
-
a second border around the whole component and clip the editor. */
|
|
23
|
-
.kanuni-datagrid-root,
|
|
24
|
-
.kanuni-datagrid-enterprise,
|
|
25
|
-
.kanuni-datagrid-detached {
|
|
26
|
-
/* ── Color tokens ── */
|
|
27
|
-
--dg-bg: #ffffff;
|
|
28
|
-
--dg-surface: #ffffff;
|
|
29
|
-
--dg-surface-header: #f8fafc; /* slate-50 */
|
|
30
|
-
--dg-surface-hover: #f1f5f9; /* slate-100 */
|
|
31
|
-
--dg-surface-stripe: #fafafa;
|
|
32
|
-
--dg-border: #e2e8f0; /* slate-200 */
|
|
33
|
-
--dg-border-strong: #cbd5e1; /* slate-300 */
|
|
34
|
-
--dg-text: #0f172a; /* slate-900 */
|
|
35
|
-
--dg-text-muted: #64748b; /* slate-500 */
|
|
36
|
-
/* sky-700, and the depth is the point rather than the hue.
|
|
37
|
-
This was sky-500 (#0ea5e9), which is the accent EVERY foreground in the
|
|
38
|
-
grid is drawn from: the sort caret over the header, the active funnel, the
|
|
39
|
-
editor underline, the detail rule, and the white label on a group chip or
|
|
40
|
-
a primary button. On a light surface sky-500 measures 2.77:1 under white
|
|
41
|
-
and 2.65:1 as a caret on the header — under the 4.5:1 that text needs and
|
|
42
|
-
under the 3:1 that an icon needs. sky-600 was not enough either (4.10:1
|
|
43
|
-
white-on). sky-700 clears both: 5.93:1 under white, 5.67:1 on the header.
|
|
44
|
-
The dark palette below keeps sky-400 — it is already at 8.33:1 there,
|
|
45
|
-
because the problem was never the hue, it was using a mid-tone accent
|
|
46
|
-
against a white background. */
|
|
47
|
-
--dg-primary: #0369a1; /* sky-700 */
|
|
48
|
-
--dg-primary-soft: #e0f2fe; /* sky-100 */
|
|
49
|
-
--dg-focus-ring: #0369a1; /* matches the accent */
|
|
50
|
-
/* Cell flash (P1.7). Translucent so the row's own background — striped,
|
|
51
|
-
selected, dirty — stays visible under the pulse rather than being
|
|
52
|
-
replaced by it. */
|
|
53
|
-
--dg-flash-bg: rgba(3, 105, 161, 0.35);
|
|
54
|
-
--dg-flash-duration: 700ms;
|
|
55
|
-
/* Cell note marker (P3.3). Red on purpose and NOT the accent: the accent
|
|
56
|
-
means "the grid did this" (focus, selection, sort), and a note means
|
|
57
|
-
"a person put something here" — two different kinds of signal. */
|
|
58
|
-
--dg-note-marker: #dc2626;
|
|
59
|
-
|
|
60
|
-
/* ── Semantic colours ──────────────────────────────────────
|
|
61
|
-
Every default below is the literal that used to be written inline at the
|
|
62
|
-
point of use, so a grid with no theme renders exactly as before. They are
|
|
63
|
-
tokens now because a theme has to be able to move them: `--dg-on-primary`
|
|
64
|
-
is the clearest case — the KanuniLabs accent is a GOLD, and the white that
|
|
65
|
-
reads fine on sky-500 measures about 2:1 on it. A theme that can recolour
|
|
66
|
-
the chip but not the text on the chip is a theme that ships unreadable
|
|
67
|
-
buttons. */
|
|
68
|
-
/* The header row is its own surface, separate from `--dg-surface-header`.
|
|
69
|
-
They default to the same value, and every theme here but one keeps them
|
|
70
|
-
that way — but a corporate theme with an INK header still wants the group
|
|
71
|
-
panel, the filter row and the footer to stay light. Without the split, the
|
|
72
|
-
one token dragged all four along and the panel hints (muted text) went
|
|
73
|
-
unreadable on the dark fill. */
|
|
74
|
-
--dg-header-bg: var(--dg-surface-header);
|
|
75
|
-
--dg-header-text: var(--dg-text);
|
|
76
|
-
--dg-header-text-muted: var(--dg-text-muted);
|
|
77
|
-
--dg-header-hover: var(--dg-surface-hover);
|
|
78
|
-
--dg-on-primary: #ffffff;
|
|
79
|
-
--dg-danger: #dc2626; /* red-600 — borders, solid fills */
|
|
80
|
-
--dg-danger-text: #b91c1c; /* red-700 — text on a light surface */
|
|
81
|
-
--dg-on-danger: #ffffff; /* text ON a solid --dg-danger fill */
|
|
82
|
-
--dg-warning: #b45309; /* amber-700 */
|
|
83
|
-
--dg-selected-bg: var(--dg-primary-soft);
|
|
84
|
-
/* Search highlight (P2.4). Amber rather than the accent: the accent already
|
|
85
|
-
means "selected" and "focused" in this grid, and a third meaning on the
|
|
86
|
-
same colour would make a highlighted cell read as a selected one. */
|
|
87
|
-
--dg-highlight-bg: #fde68a; /* amber-200 */
|
|
88
|
-
--dg-highlight-text: #713f12; /* amber-900 — 8.9:1 on the above */
|
|
89
|
-
--dg-group-bg: var(--dg-surface-header);
|
|
90
|
-
--dg-footer-bg: var(--dg-surface-header);
|
|
91
|
-
--dg-overlay: rgba(15, 23, 42, 0.55);
|
|
92
|
-
--dg-scrollbar-thumb: var(--dg-border-strong);
|
|
93
|
-
--dg-scrollbar-track: transparent;
|
|
94
|
-
|
|
95
|
-
/* ── Elevation ──
|
|
96
|
-
A flat theme sets these to `none`; Material raises them. */
|
|
97
|
-
--dg-shadow-popup: 0 10px 25px -5px rgba(15, 23, 42, 0.25);
|
|
98
|
-
--dg-shadow-popup-sm: 0 8px 24px rgba(15, 23, 42, 0.16);
|
|
99
|
-
--dg-shadow-menu: 0 12px 28px rgba(15, 23, 42, 0.22);
|
|
100
|
-
--dg-shadow-card: 0 12px 32px rgba(15, 23, 42, 0.18);
|
|
101
|
-
--dg-shadow-panel: 0 8px 20px rgba(0, 0, 0, 0.12);
|
|
102
|
-
--dg-shadow-dialog: 0 24px 48px rgba(0, 0, 0, 0.28);
|
|
103
|
-
|
|
104
|
-
/* ── Metric tokens ── */
|
|
105
|
-
--dg-row-height: 28px;
|
|
106
|
-
--dg-header-height: 32px;
|
|
107
|
-
--dg-cell-padding-x: 8px;
|
|
108
|
-
--dg-font-size: 13px;
|
|
109
|
-
--dg-header-font-size: 12px;
|
|
110
|
-
--dg-radius: 6px;
|
|
111
|
-
--dg-shadow-pinned: 2px 0 4px -2px rgba(15, 23, 42, 0.18);
|
|
112
|
-
--dg-shadow-pinned-right: -2px 0 4px -2px rgba(15, 23, 42, 0.18);
|
|
113
|
-
|
|
114
|
-
/* ── Line work ─────────────────────────────────────────────
|
|
115
|
-
Whole SHORTHANDS rather than a colour and a width, so a theme can drop a
|
|
116
|
-
line entirely (`none`) instead of only fading it. This is most of what
|
|
117
|
-
separates the presets: Balham rules every column and every row, Material
|
|
118
|
-
rules rows only, Quartz keeps the rows faint and skips the columns. */
|
|
119
|
-
--dg-cell-border: 1px solid var(--dg-border);
|
|
120
|
-
--dg-row-border: 1px solid var(--dg-border);
|
|
121
|
-
--dg-header-cell-border: 1px solid var(--dg-border);
|
|
122
|
-
--dg-header-border: 1px solid var(--dg-border-strong);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
--dg-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
box
|
|
156
|
-
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
color: var(--dg-text);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
--dg-bg
|
|
182
|
-
--dg-
|
|
183
|
-
--dg-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
--dg-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
--dg-
|
|
204
|
-
--dg-
|
|
205
|
-
--dg-
|
|
206
|
-
--dg-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
--dg-
|
|
211
|
-
--dg-
|
|
212
|
-
--dg-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
--dg-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
background: var(--dg-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
.kanuni-datagrid-band-cell
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
text-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
.
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
.
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
padding
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
.
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
.kanuni-datagrid-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
.kanuni-datagrid-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
.kanuni-datagrid-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
background: var(--dg-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
.kanuni-datagrid-pager
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
.kanuni-datagrid-pager-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
.kanuni-datagrid-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
bottom:
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
.kanuni-datagrid-group-level
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
.kanuni-datagrid-group-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
font-
|
|
801
|
-
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
.kanuni-datagrid-group-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
.
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
background: color-mix(in srgb, var(--dg-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
.kanuni-datagrid-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
.kanuni-datagrid-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
.kanuni-datagrid-cell
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
.kanuni-datagrid-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
.
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
.kanuni-datagrid-header-cell
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
/*
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
.kanuni-datagrid-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
inset-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
.kanuni-datagrid-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
.kanuni-datagrid-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
.kanuni-datagrid-loading-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
height:
|
|
1213
|
-
border
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
font-weight:
|
|
1221
|
-
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
.kanuni-datagrid-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
.
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
/* ──
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
.kanuni-datagrid-filter-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
.
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
.
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
.kanuni-datagrid-root.kanuni-datagrid-
|
|
1676
|
-
--dg-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
*/
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
padding:
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
.kanuni-datagrid-menu
|
|
1713
|
-
position:
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
.kanuni-datagrid-menu-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
.
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
height:
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
.
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
.kanuni-datagrid-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
.kanuni-datagrid-
|
|
1879
|
-
top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * var(--dg-pinned-gap, 1));
|
|
1880
|
-
bottom:
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
background: var(--dg-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
.
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
.kanuni-datagrid-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
/* ──
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
.kanuni-datagrid-row-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
}
|
|
2027
|
-
|
|
2028
|
-
.kanuni-datagrid-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
}
|
|
2046
|
-
|
|
2047
|
-
.
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
.kanuni-datagrid-print .kanuni-datagrid-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
/*
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
.
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
.kanuni-datagrid-
|
|
2187
|
-
.kanuni-datagrid-
|
|
2188
|
-
.kanuni-datagrid-
|
|
2189
|
-
.kanuni-datagrid-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
}
|
|
2195
|
-
|
|
2196
|
-
/*
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
}
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
.kanuni-datagrid-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
.
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
.kanuni-datagrid-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
.
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
/*
|
|
2310
|
-
|
|
2311
|
-
.
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
.kanuni-datagrid-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
1
|
+
/* ─────────────────────────────────────────────────────────────
|
|
2
|
+
KANUNILABS DataGrid — isolated theme system
|
|
3
|
+
─────────────────────────────────────────────────────────────
|
|
4
|
+
Every rule is scoped under .kanuni-datagrid-root so the host app's CSS
|
|
5
|
+
cannot bleed in and ours cannot bleed out. Tokens mirror the PivotGrid
|
|
6
|
+
vocabulary (--pivot-*) with a --dg-* prefix so the two products can be
|
|
7
|
+
themed side by side without collisions.
|
|
8
|
+
───────────────────────────────────────────────────────────── */
|
|
9
|
+
|
|
10
|
+
/* ── Design tokens ───────────────────────────────────────────
|
|
11
|
+
Declared on more than the grid root because two things render OUTSIDE it
|
|
12
|
+
and still have to look like the grid: the enterprise wrapper (which owns
|
|
13
|
+
the row/form editor, a SIBLING of the root) and any layer portalled to
|
|
14
|
+
<body> (the lookup list, tagged `.kanuni-datagrid-detached`).
|
|
15
|
+
|
|
16
|
+
Tokens defined only on the root left those resolving `var(--dg-surface)` to
|
|
17
|
+
nothing — which is not a fallback but an INVALID declaration, so the popup
|
|
18
|
+
editor rendered fully transparent and unreadable over the grid.
|
|
19
|
+
|
|
20
|
+
Only the custom properties live here. The grid's own box (border, radius,
|
|
21
|
+
overflow, flex) stays on the root below: giving it to the wrapper would draw
|
|
22
|
+
a second border around the whole component and clip the editor. */
|
|
23
|
+
.kanuni-datagrid-root,
|
|
24
|
+
.kanuni-datagrid-enterprise,
|
|
25
|
+
.kanuni-datagrid-detached {
|
|
26
|
+
/* ── Color tokens ── */
|
|
27
|
+
--dg-bg: #ffffff;
|
|
28
|
+
--dg-surface: #ffffff;
|
|
29
|
+
--dg-surface-header: #f8fafc; /* slate-50 */
|
|
30
|
+
--dg-surface-hover: #f1f5f9; /* slate-100 */
|
|
31
|
+
--dg-surface-stripe: #fafafa;
|
|
32
|
+
--dg-border: #e2e8f0; /* slate-200 */
|
|
33
|
+
--dg-border-strong: #cbd5e1; /* slate-300 */
|
|
34
|
+
--dg-text: #0f172a; /* slate-900 */
|
|
35
|
+
--dg-text-muted: #64748b; /* slate-500 */
|
|
36
|
+
/* sky-700, and the depth is the point rather than the hue.
|
|
37
|
+
This was sky-500 (#0ea5e9), which is the accent EVERY foreground in the
|
|
38
|
+
grid is drawn from: the sort caret over the header, the active funnel, the
|
|
39
|
+
editor underline, the detail rule, and the white label on a group chip or
|
|
40
|
+
a primary button. On a light surface sky-500 measures 2.77:1 under white
|
|
41
|
+
and 2.65:1 as a caret on the header — under the 4.5:1 that text needs and
|
|
42
|
+
under the 3:1 that an icon needs. sky-600 was not enough either (4.10:1
|
|
43
|
+
white-on). sky-700 clears both: 5.93:1 under white, 5.67:1 on the header.
|
|
44
|
+
The dark palette below keeps sky-400 — it is already at 8.33:1 there,
|
|
45
|
+
because the problem was never the hue, it was using a mid-tone accent
|
|
46
|
+
against a white background. */
|
|
47
|
+
--dg-primary: #0369a1; /* sky-700 */
|
|
48
|
+
--dg-primary-soft: #e0f2fe; /* sky-100 */
|
|
49
|
+
--dg-focus-ring: #0369a1; /* matches the accent */
|
|
50
|
+
/* Cell flash (P1.7). Translucent so the row's own background — striped,
|
|
51
|
+
selected, dirty — stays visible under the pulse rather than being
|
|
52
|
+
replaced by it. */
|
|
53
|
+
--dg-flash-bg: rgba(3, 105, 161, 0.35);
|
|
54
|
+
--dg-flash-duration: 700ms;
|
|
55
|
+
/* Cell note marker (P3.3). Red on purpose and NOT the accent: the accent
|
|
56
|
+
means "the grid did this" (focus, selection, sort), and a note means
|
|
57
|
+
"a person put something here" — two different kinds of signal. */
|
|
58
|
+
--dg-note-marker: #dc2626;
|
|
59
|
+
|
|
60
|
+
/* ── Semantic colours ──────────────────────────────────────
|
|
61
|
+
Every default below is the literal that used to be written inline at the
|
|
62
|
+
point of use, so a grid with no theme renders exactly as before. They are
|
|
63
|
+
tokens now because a theme has to be able to move them: `--dg-on-primary`
|
|
64
|
+
is the clearest case — the KanuniLabs accent is a GOLD, and the white that
|
|
65
|
+
reads fine on sky-500 measures about 2:1 on it. A theme that can recolour
|
|
66
|
+
the chip but not the text on the chip is a theme that ships unreadable
|
|
67
|
+
buttons. */
|
|
68
|
+
/* The header row is its own surface, separate from `--dg-surface-header`.
|
|
69
|
+
They default to the same value, and every theme here but one keeps them
|
|
70
|
+
that way — but a corporate theme with an INK header still wants the group
|
|
71
|
+
panel, the filter row and the footer to stay light. Without the split, the
|
|
72
|
+
one token dragged all four along and the panel hints (muted text) went
|
|
73
|
+
unreadable on the dark fill. */
|
|
74
|
+
--dg-header-bg: var(--dg-surface-header);
|
|
75
|
+
--dg-header-text: var(--dg-text);
|
|
76
|
+
--dg-header-text-muted: var(--dg-text-muted);
|
|
77
|
+
--dg-header-hover: var(--dg-surface-hover);
|
|
78
|
+
--dg-on-primary: #ffffff;
|
|
79
|
+
--dg-danger: #dc2626; /* red-600 — borders, solid fills */
|
|
80
|
+
--dg-danger-text: #b91c1c; /* red-700 — text on a light surface */
|
|
81
|
+
--dg-on-danger: #ffffff; /* text ON a solid --dg-danger fill */
|
|
82
|
+
--dg-warning: #b45309; /* amber-700 */
|
|
83
|
+
--dg-selected-bg: var(--dg-primary-soft);
|
|
84
|
+
/* Search highlight (P2.4). Amber rather than the accent: the accent already
|
|
85
|
+
means "selected" and "focused" in this grid, and a third meaning on the
|
|
86
|
+
same colour would make a highlighted cell read as a selected one. */
|
|
87
|
+
--dg-highlight-bg: #fde68a; /* amber-200 */
|
|
88
|
+
--dg-highlight-text: #713f12; /* amber-900 — 8.9:1 on the above */
|
|
89
|
+
--dg-group-bg: var(--dg-surface-header);
|
|
90
|
+
--dg-footer-bg: var(--dg-surface-header);
|
|
91
|
+
--dg-overlay: rgba(15, 23, 42, 0.55);
|
|
92
|
+
--dg-scrollbar-thumb: var(--dg-border-strong);
|
|
93
|
+
--dg-scrollbar-track: transparent;
|
|
94
|
+
|
|
95
|
+
/* ── Elevation ──
|
|
96
|
+
A flat theme sets these to `none`; Material raises them. */
|
|
97
|
+
--dg-shadow-popup: 0 10px 25px -5px rgba(15, 23, 42, 0.25);
|
|
98
|
+
--dg-shadow-popup-sm: 0 8px 24px rgba(15, 23, 42, 0.16);
|
|
99
|
+
--dg-shadow-menu: 0 12px 28px rgba(15, 23, 42, 0.22);
|
|
100
|
+
--dg-shadow-card: 0 12px 32px rgba(15, 23, 42, 0.18);
|
|
101
|
+
--dg-shadow-panel: 0 8px 20px rgba(0, 0, 0, 0.12);
|
|
102
|
+
--dg-shadow-dialog: 0 24px 48px rgba(0, 0, 0, 0.28);
|
|
103
|
+
|
|
104
|
+
/* ── Metric tokens ── */
|
|
105
|
+
--dg-row-height: 28px;
|
|
106
|
+
--dg-header-height: 32px;
|
|
107
|
+
--dg-cell-padding-x: 8px;
|
|
108
|
+
--dg-font-size: 13px;
|
|
109
|
+
--dg-header-font-size: 12px;
|
|
110
|
+
--dg-radius: 6px;
|
|
111
|
+
--dg-shadow-pinned: 2px 0 4px -2px rgba(15, 23, 42, 0.18);
|
|
112
|
+
--dg-shadow-pinned-right: -2px 0 4px -2px rgba(15, 23, 42, 0.18);
|
|
113
|
+
|
|
114
|
+
/* ── Line work ─────────────────────────────────────────────
|
|
115
|
+
Whole SHORTHANDS rather than a colour and a width, so a theme can drop a
|
|
116
|
+
line entirely (`none`) instead of only fading it. This is most of what
|
|
117
|
+
separates the presets: Balham rules every column and every row, Material
|
|
118
|
+
rules rows only, Quartz keeps the rows faint and skips the columns. */
|
|
119
|
+
--dg-cell-border: 1px solid var(--dg-border);
|
|
120
|
+
--dg-row-border: 1px solid var(--dg-border);
|
|
121
|
+
--dg-header-cell-border: 1px solid var(--dg-border);
|
|
122
|
+
--dg-header-border: 1px solid var(--dg-border-strong);
|
|
123
|
+
/* A band's EDGE is not a column separator, so it gets its own token rather
|
|
124
|
+
than riding on `--dg-header-cell-border`. Quartz and Material set that one
|
|
125
|
+
to `none` on purpose — they rule rows, not columns — and the bands went
|
|
126
|
+
with it: "Where" and "Sales" sat over their columns with nothing marking
|
|
127
|
+
where either one started or stopped. Same argument the band row's
|
|
128
|
+
`border-bottom` already makes: a band boundary is structural. A theme can
|
|
129
|
+
still override this token if it wants a different line.
|
|
130
|
+
|
|
131
|
+
Mixed from the header TEXT rather than taken from a border token, because
|
|
132
|
+
both border tokens are too faint for a 24px vertical segment: measured on
|
|
133
|
+
Quartz against the header background, `--dg-border` gives 1.17:1 and
|
|
134
|
+
`--dg-border-strong` 1.39:1 — lines that are technically drawn and
|
|
135
|
+
practically invisible, i.e. a fix that changes nothing the user sees. The
|
|
136
|
+
mix lands near 2:1 and follows the theme in both light and dark, since the
|
|
137
|
+
header text is what already has to be readable on that ground. */
|
|
138
|
+
--dg-band-cell-border: 1px solid color-mix(in srgb, var(--dg-header-text) 32%, transparent);
|
|
139
|
+
|
|
140
|
+
/* ── Header treatment ── */
|
|
141
|
+
--dg-header-weight: 600;
|
|
142
|
+
--dg-header-transform: none;
|
|
143
|
+
--dg-header-tracking: normal;
|
|
144
|
+
|
|
145
|
+
/* ── Chrome (toolbar, pager, panels, popups) ──
|
|
146
|
+
Sized separately from the data surface: a compact grid shrinks its rows
|
|
147
|
+
hard, but a 9px button is not usable at any density. */
|
|
148
|
+
--dg-chrome-font-size: 12px;
|
|
149
|
+
--dg-chrome-padding-y: 6px;
|
|
150
|
+
--dg-control-height: 26px;
|
|
151
|
+
--dg-control-font-size: 12px;
|
|
152
|
+
--dg-filter-input-height: 22px;
|
|
153
|
+
--dg-radius-control: 4px;
|
|
154
|
+
|
|
155
|
+
/* The line box a WRAPPED cell lays its text out on, and the height an editor
|
|
156
|
+
takes in one. Whole pixels — see the note on `--wrap` below. */
|
|
157
|
+
--dg-wrap-line-height: 18px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* Detached surfaces carry the foreground as well as the tokens, for the reason
|
|
161
|
+
the enterprise wrapper does: a portalled layer is a child of <body>, so
|
|
162
|
+
anything inside it written as `color: inherit` would resolve against the host
|
|
163
|
+
page rather than against the grid it belongs to. The dialog and the row
|
|
164
|
+
editor set their own colour, but the box AROUND them is the safety net. */
|
|
165
|
+
.kanuni-datagrid-detached {
|
|
166
|
+
color: var(--dg-text);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* The grid's own box. */
|
|
170
|
+
.kanuni-datagrid-root {
|
|
171
|
+
box-sizing: border-box;
|
|
172
|
+
font-family: inherit;
|
|
173
|
+
-webkit-font-smoothing: antialiased;
|
|
174
|
+
-moz-osx-font-smoothing: grayscale;
|
|
175
|
+
position: relative;
|
|
176
|
+
display: flex;
|
|
177
|
+
flex-direction: column;
|
|
178
|
+
overflow: hidden;
|
|
179
|
+
border: 1px solid var(--dg-border);
|
|
180
|
+
border-radius: var(--dg-radius);
|
|
181
|
+
background: var(--dg-bg);
|
|
182
|
+
color: var(--dg-text);
|
|
183
|
+
font-size: var(--dg-font-size);
|
|
184
|
+
line-height: 1.4;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.kanuni-datagrid-root *,
|
|
188
|
+
.kanuni-datagrid-root *::before,
|
|
189
|
+
.kanuni-datagrid-root *::after {
|
|
190
|
+
box-sizing: border-box;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Dark theme: opt in with .kanuni-datagrid-root.kanuni-datagrid-dark */
|
|
194
|
+
.kanuni-datagrid-root.kanuni-datagrid-dark,
|
|
195
|
+
.kanuni-datagrid-enterprise.kanuni-datagrid-dark,
|
|
196
|
+
.kanuni-datagrid-detached.kanuni-datagrid-dark {
|
|
197
|
+
--dg-bg: #0b1120;
|
|
198
|
+
--dg-surface: #0f172a;
|
|
199
|
+
--dg-surface-header: #16233c;
|
|
200
|
+
--dg-surface-hover: #1e293b;
|
|
201
|
+
--dg-surface-stripe: #101a2e;
|
|
202
|
+
--dg-border: #24344d;
|
|
203
|
+
--dg-border-strong: #334155;
|
|
204
|
+
--dg-text: #e2e8f0;
|
|
205
|
+
--dg-text-muted: #94a3b8;
|
|
206
|
+
--dg-primary: #38bdf8;
|
|
207
|
+
--dg-primary-soft: #0c4a6e;
|
|
208
|
+
--dg-focus-ring: #7dd3fc;
|
|
209
|
+
/* Lifted for a dark surface: crimson on near-black reads as brown. */
|
|
210
|
+
--dg-note-marker: #f87171;
|
|
211
|
+
--dg-shadow-pinned: 2px 0 4px -2px rgba(0, 0, 0, 0.55);
|
|
212
|
+
--dg-shadow-pinned-right: -2px 0 4px -2px rgba(0, 0, 0, 0.55);
|
|
213
|
+
|
|
214
|
+
/* The semantic set, re-pointed for a dark surface. `--dg-on-primary` goes
|
|
215
|
+
DARK here because the dark accent is a light sky-400. */
|
|
216
|
+
--dg-on-primary: #082f49;
|
|
217
|
+
/* Dark: a muted amber fill with light text, so the mark reads as emphasis
|
|
218
|
+
instead of glowing off a dark surface the way amber-200 would. */
|
|
219
|
+
--dg-highlight-bg: #78350f; /* amber-900 */
|
|
220
|
+
--dg-highlight-text: #fef3c7; /* amber-100 — 9.6:1 on the above */
|
|
221
|
+
--dg-danger: #f87171;
|
|
222
|
+
--dg-danger-text: #fca5a5;
|
|
223
|
+
/* The dark danger is a LIGHT red, so what sits on it has to be dark. White
|
|
224
|
+
on #f87171 measures 2.4:1 — the delete button's own label. */
|
|
225
|
+
--dg-on-danger: #450a0a;
|
|
226
|
+
--dg-warning: #fbbf24;
|
|
227
|
+
--dg-overlay: rgba(2, 6, 23, 0.7);
|
|
228
|
+
--dg-scrollbar-thumb: #475569;
|
|
229
|
+
--dg-shadow-popup: 0 10px 25px -5px rgba(0, 0, 0, 0.6);
|
|
230
|
+
--dg-shadow-popup-sm: 0 8px 24px rgba(0, 0, 0, 0.5);
|
|
231
|
+
--dg-shadow-menu: 0 12px 28px rgba(0, 0, 0, 0.55);
|
|
232
|
+
--dg-shadow-card: 0 12px 32px rgba(0, 0, 0, 0.55);
|
|
233
|
+
--dg-shadow-panel: 0 8px 20px rgba(0, 0, 0, 0.45);
|
|
234
|
+
--dg-shadow-dialog: 0 24px 48px rgba(0, 0, 0, 0.65);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* ── Single scroll container (§7.4: NO dual-table architecture) ────────
|
|
238
|
+
The header and the body live in ONE scroller, so horizontal alignment
|
|
239
|
+
cannot drift: there is no second element to keep in sync. */
|
|
240
|
+
.kanuni-datagrid-scroller {
|
|
241
|
+
position: relative;
|
|
242
|
+
flex: 1 1 auto;
|
|
243
|
+
overflow: auto;
|
|
244
|
+
overscroll-behavior: contain;
|
|
245
|
+
/* Scroll anchoring must be OFF in a virtualized scroller. Under scroll
|
|
246
|
+
compression the rows legitimately move relative to the canvas on every
|
|
247
|
+
scroll; Chrome's anchoring reads that as "content shifted under the user"
|
|
248
|
+
and walks scrollTop back to chase the anchor row. During a scrollbar drag
|
|
249
|
+
the correction is deferred and applied on mouse RELEASE — the reported
|
|
250
|
+
symptom (2026-08-14, 1M rows): the last rows peek out while dragging and
|
|
251
|
+
snap back when the button is let go, leaving the true bottom unreachable
|
|
252
|
+
by user scrolling while programmatic probes see nothing wrong. */
|
|
253
|
+
overflow-anchor: none;
|
|
254
|
+
background: var(--dg-surface);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* Sized to the full virtual extent so the native scrollbar is accurate. */
|
|
258
|
+
.kanuni-datagrid-canvas {
|
|
259
|
+
position: relative;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* ── Header ───────────────────────────────────────────────── */
|
|
263
|
+
/* ── Header bands (multi-level headers) ──────────────────────
|
|
264
|
+
Siblings of the header row, each sticky at its own offset so the whole
|
|
265
|
+
stack stays put while the rows scroll under it. */
|
|
266
|
+
.kanuni-datagrid-band-row {
|
|
267
|
+
position: sticky;
|
|
268
|
+
z-index: 4;
|
|
269
|
+
display: flex;
|
|
270
|
+
background: var(--dg-header-bg);
|
|
271
|
+
/* Deliberately a literal, not `--dg-header-cell-border`: that token is the
|
|
272
|
+
COLUMN separator, which some themes drop entirely. The line under a band
|
|
273
|
+
row is structural — without it a two-level header reads as one tall box. */
|
|
274
|
+
border-bottom: 1px solid var(--dg-border);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.kanuni-datagrid-band-cell {
|
|
278
|
+
box-sizing: border-box;
|
|
279
|
+
display: flex;
|
|
280
|
+
align-items: center;
|
|
281
|
+
justify-content: center;
|
|
282
|
+
min-width: 0;
|
|
283
|
+
padding: 0 8px;
|
|
284
|
+
background: var(--dg-header-bg);
|
|
285
|
+
color: var(--dg-header-text);
|
|
286
|
+
font-size: calc(var(--dg-font-size) - 1px);
|
|
287
|
+
font-weight: var(--dg-header-weight);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* A real band is drawn as a bounded box, so its extent is readable: the right
|
|
291
|
+
edge on every one, and the left edge only where the thing before it is NOT
|
|
292
|
+
another band. Bands that touch therefore share one line instead of drawing
|
|
293
|
+
two beside each other. */
|
|
294
|
+
.kanuni-datagrid-band-cell:not(.kanuni-datagrid-band-cell--spacer) {
|
|
295
|
+
border-inline-end: var(--dg-band-cell-border);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.kanuni-datagrid-band-cell--spacer + .kanuni-datagrid-band-cell:not(.kanuni-datagrid-band-cell--spacer),
|
|
299
|
+
.kanuni-datagrid-band-cell:first-child:not(.kanuni-datagrid-band-cell--spacer) {
|
|
300
|
+
border-inline-start: var(--dg-band-cell-border);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/* No band at this level: the column's own header reaches up through here, so
|
|
304
|
+
the box must be INVISIBLE rather than an empty cell with borders. */
|
|
305
|
+
.kanuni-datagrid-band-cell--spacer {
|
|
306
|
+
border-inline-end: none;
|
|
307
|
+
border-inline-start: none;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.kanuni-datagrid-band-cell--pinned {
|
|
311
|
+
position: sticky;
|
|
312
|
+
z-index: 5;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.kanuni-datagrid-band-label {
|
|
316
|
+
overflow: hidden;
|
|
317
|
+
text-overflow: ellipsis;
|
|
318
|
+
white-space: nowrap;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.kanuni-datagrid-header {
|
|
322
|
+
position: sticky;
|
|
323
|
+
top: 0;
|
|
324
|
+
z-index: 3;
|
|
325
|
+
display: flex;
|
|
326
|
+
height: var(--dg-header-height);
|
|
327
|
+
background: var(--dg-header-bg);
|
|
328
|
+
border-bottom: var(--dg-header-border);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.kanuni-datagrid-header-cell {
|
|
332
|
+
/* Anchors the resize handle. Pinned headers override this with `sticky`,
|
|
333
|
+
which is also a positioned ancestor, so the handle works there too. */
|
|
334
|
+
position: relative;
|
|
335
|
+
display: flex;
|
|
336
|
+
align-items: center;
|
|
337
|
+
gap: 4px;
|
|
338
|
+
flex: none;
|
|
339
|
+
height: 100%;
|
|
340
|
+
padding: 0 var(--dg-cell-padding-x);
|
|
341
|
+
background: var(--dg-header-bg);
|
|
342
|
+
border-inline-end: var(--dg-header-cell-border);
|
|
343
|
+
font-size: var(--dg-header-font-size);
|
|
344
|
+
font-weight: var(--dg-header-weight);
|
|
345
|
+
letter-spacing: var(--dg-header-tracking);
|
|
346
|
+
text-transform: var(--dg-header-transform);
|
|
347
|
+
color: var(--dg-header-text);
|
|
348
|
+
white-space: nowrap;
|
|
349
|
+
overflow: hidden;
|
|
350
|
+
user-select: none;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.kanuni-datagrid-header-cell--sortable {
|
|
354
|
+
cursor: pointer;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.kanuni-datagrid-header-cell--sortable:hover {
|
|
358
|
+
background: var(--dg-header-hover);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.kanuni-datagrid-header-label {
|
|
362
|
+
overflow: hidden;
|
|
363
|
+
text-overflow: ellipsis;
|
|
364
|
+
white-space: nowrap;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.kanuni-datagrid-sort-indicator {
|
|
368
|
+
flex: none;
|
|
369
|
+
font-size: 10px;
|
|
370
|
+
line-height: 1;
|
|
371
|
+
color: var(--dg-primary);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.kanuni-datagrid-sort-order {
|
|
375
|
+
flex: none;
|
|
376
|
+
font-size: 9px;
|
|
377
|
+
color: var(--dg-header-text-muted);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* ── Rows and cells ───────────────────────────────────────── */
|
|
381
|
+
.kanuni-datagrid-viewport {
|
|
382
|
+
position: absolute;
|
|
383
|
+
top: 0;
|
|
384
|
+
left: 0;
|
|
385
|
+
width: 100%;
|
|
386
|
+
will-change: transform;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.kanuni-datagrid-row {
|
|
390
|
+
display: flex;
|
|
391
|
+
height: var(--dg-row-height);
|
|
392
|
+
background: var(--dg-surface);
|
|
393
|
+
border-bottom: var(--dg-row-border);
|
|
394
|
+
/* Dragging a CELL RANGE must not also drag a text selection. The browser
|
|
395
|
+
starts one on any mousedown-and-move over text, so the user got two
|
|
396
|
+
overlapping highlights at once — the grid's range in pale blue and the
|
|
397
|
+
native selection in solid blue — with no way to tell which one the totals
|
|
398
|
+
strip was summing. The header and footer already do this; the data rows
|
|
399
|
+
were the gap. Copying is not lost: the range owns Ctrl+C. */
|
|
400
|
+
user-select: none;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/* ── Auto-height rows ──────────────────────────────────────
|
|
404
|
+
A grid with an `autoHeight` column lets its rows grow. The row's height is
|
|
405
|
+
NOT written inline here: it is measured after layout and handed to the window
|
|
406
|
+
engine, which owns the scroll canvas and every row offset. Setting it inline
|
|
407
|
+
as well would make the measurement read back whatever was set — the row would
|
|
408
|
+
agree with itself and never grow.
|
|
409
|
+
|
|
410
|
+
`min-height` keeps a one-line row exactly the height it was, so turning the
|
|
411
|
+
feature on does not resize the rows that did not need it. */
|
|
412
|
+
.kanuni-datagrid-root[data-auto-height] .kanuni-datagrid-row {
|
|
413
|
+
height: auto;
|
|
414
|
+
min-height: var(--dg-row-height);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/* Every OTHER cell of an autoHeight grid must stretch to its row.
|
|
418
|
+
The base rule's `height: 100%` cannot resolve against the row's
|
|
419
|
+
`height: auto`, so it computed as content height and every single-line cell
|
|
420
|
+
collapsed to its text — 18px inside a 28px row, worse inside a wrapped one.
|
|
421
|
+
Two user-visible symptoms (report, 2026-08-14): the focus rectangle hugged
|
|
422
|
+
the TEXT instead of the cell, and a click in the lower band of a cell landed
|
|
423
|
+
on the bare row — no column resolved, so nothing got focused. Stretching is
|
|
424
|
+
what the base rule always meant; `align-items: center` then keeps single-line
|
|
425
|
+
text centred exactly as in a fixed-height grid. The wrap cell is excluded:
|
|
426
|
+
its own block sizes it to its text on purpose — that is how the row learns
|
|
427
|
+
how tall to be. */
|
|
428
|
+
.kanuni-datagrid-root[data-auto-height] .kanuni-datagrid-row .kanuni-datagrid-cell:not(.kanuni-datagrid-cell--wrap) {
|
|
429
|
+
height: auto;
|
|
430
|
+
align-self: stretch;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* Anything the user genuinely types into, or reads as prose, gets it back. */
|
|
434
|
+
.kanuni-datagrid-row input,
|
|
435
|
+
.kanuni-datagrid-row textarea,
|
|
436
|
+
.kanuni-datagrid-row select,
|
|
437
|
+
.kanuni-datagrid-detail-cell,
|
|
438
|
+
.kanuni-datagrid-editform-cell {
|
|
439
|
+
user-select: text;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.kanuni-datagrid-row--odd {
|
|
443
|
+
background: var(--dg-surface-stripe);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.kanuni-datagrid-row:hover,
|
|
447
|
+
.kanuni-datagrid-row:hover .kanuni-datagrid-cell--pinned {
|
|
448
|
+
background: var(--dg-surface-hover);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.kanuni-datagrid-cell {
|
|
452
|
+
display: flex;
|
|
453
|
+
align-items: center;
|
|
454
|
+
flex: none;
|
|
455
|
+
height: 100%;
|
|
456
|
+
padding: 0 var(--dg-cell-padding-x);
|
|
457
|
+
border-inline-end: var(--dg-cell-border);
|
|
458
|
+
white-space: nowrap;
|
|
459
|
+
overflow: hidden;
|
|
460
|
+
text-overflow: ellipsis;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/* ── autoHeight: the columns that wrap ─────────────────────
|
|
464
|
+
Only the columns that asked for it. Everything else keeps its single line and
|
|
465
|
+
its ellipsis — wrapping a whole grid because one column needed to is how a
|
|
466
|
+
dense table turns into a wall of text.
|
|
467
|
+
|
|
468
|
+
The doubled class is deliberate and load-bearing. `.kanuni-datagrid-cell--wrap`
|
|
469
|
+
alone has the same specificity as the base rule directly above, so whichever
|
|
470
|
+
came LAST in the file won; written earlier in the sheet it lost silently and
|
|
471
|
+
the cells kept `white-space: nowrap` while everything else about the feature
|
|
472
|
+
worked. Two classes settle it regardless of where this block sits. */
|
|
473
|
+
.kanuni-datagrid-cell.kanuni-datagrid-cell--wrap {
|
|
474
|
+
white-space: normal;
|
|
475
|
+
overflow: visible;
|
|
476
|
+
text-overflow: clip;
|
|
477
|
+
/* `anywhere` and not `break-word`: an unbroken 200-character token (a URL, a
|
|
478
|
+
serial number) otherwise overflows its column and lands on the next one. */
|
|
479
|
+
overflow-wrap: anywhere;
|
|
480
|
+
align-items: flex-start;
|
|
481
|
+
/* Not `height: 100%` — the cell has to be as tall as its own text, or the row
|
|
482
|
+
it is supposed to stretch never learns it needs to grow. */
|
|
483
|
+
height: auto;
|
|
484
|
+
align-self: stretch;
|
|
485
|
+
padding-block: 4px;
|
|
486
|
+
/* An INTEGER line box, not a ratio. `1.35` against the 13px body font lays
|
|
487
|
+
out at 17.55px, so every wrapped row landed on a fractional boundary and
|
|
488
|
+
its 1px border rendered a pixel thick on some rows and two on others.
|
|
489
|
+
A token now, because a theme moves the body font under it — 18px is right
|
|
490
|
+
for 13px text and cramped for Material's 14px. */
|
|
491
|
+
line-height: var(--dg-wrap-line-height);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.kanuni-datagrid-cell--number {
|
|
495
|
+
justify-content: flex-end;
|
|
496
|
+
font-variant-numeric: tabular-nums;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.kanuni-datagrid-cell--center {
|
|
500
|
+
justify-content: center;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/* ── Pinned columns = sticky cells inside the same row (§7.4) ──────────
|
|
504
|
+
Compositor-friendly and layout-free; structurally immune to the
|
|
505
|
+
header/body desync bug class of a dual-table design. */
|
|
506
|
+
.kanuni-datagrid-cell--pinned,
|
|
507
|
+
.kanuni-datagrid-header-cell--pinned {
|
|
508
|
+
position: sticky;
|
|
509
|
+
z-index: 2;
|
|
510
|
+
background: inherit;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/* HARDENING: a pinned cell is sticky, whatever else it becomes.
|
|
514
|
+
State classes keep anchoring their markers with `position: relative` —
|
|
515
|
+
the dirty corner, the range overlay, whatever comes next — and on a pinned
|
|
516
|
+
cell that override turns `insetInlineStart` from a parking threshold into a
|
|
517
|
+
literal shift: the cell jumps sideways by its whole inset and parks over its
|
|
518
|
+
neighbour. It happened twice from two different classes in one day
|
|
519
|
+
(range-anchor on click, dirty on commit), so the guarantee lives HERE at
|
|
520
|
+
three-class specificity instead of as a `:not(--pinned)` chased onto every
|
|
521
|
+
state rule. Sticky is itself a positioned ancestor, so those markers lose
|
|
522
|
+
nothing. */
|
|
523
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell.kanuni-datagrid-cell--pinned {
|
|
524
|
+
position: sticky;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/* A STICKY column sits in the scrolling order and parks itself only once it
|
|
528
|
+
would leave the viewport. It needs a lower z-index than a truly pinned
|
|
529
|
+
column: when the two meet, the pinned one is the fixed landmark and has to
|
|
530
|
+
win, or the sticky column would slide over the top of it. */
|
|
531
|
+
.kanuni-datagrid-cell--sticky {
|
|
532
|
+
z-index: 1;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.kanuni-datagrid-header-cell--pinned {
|
|
536
|
+
z-index: 4;
|
|
537
|
+
background: var(--dg-header-bg);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.kanuni-datagrid-cell--pinned-left:last-of-type,
|
|
541
|
+
.kanuni-datagrid-header-cell--pinned-left:last-of-type {
|
|
542
|
+
box-shadow: var(--dg-shadow-pinned);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.kanuni-datagrid-pin-edge-left {
|
|
546
|
+
box-shadow: var(--dg-shadow-pinned);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.kanuni-datagrid-pin-edge-right {
|
|
550
|
+
box-shadow: var(--dg-shadow-pinned-right);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/* Spacer that reserves the width of columns outside the horizontal window. */
|
|
554
|
+
.kanuni-datagrid-spacer {
|
|
555
|
+
flex: none;
|
|
556
|
+
height: 100%;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/* ── Toolbar ──────────────────────────────────────────────── */
|
|
560
|
+
.kanuni-datagrid-toolbar {
|
|
561
|
+
display: flex;
|
|
562
|
+
align-items: center;
|
|
563
|
+
gap: 6px;
|
|
564
|
+
flex: none;
|
|
565
|
+
padding: var(--dg-chrome-padding-y) 8px;
|
|
566
|
+
background: var(--dg-surface);
|
|
567
|
+
border-bottom: 1px solid var(--dg-border);
|
|
568
|
+
font-size: var(--dg-chrome-font-size);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.kanuni-datagrid-toolbar-spacer {
|
|
572
|
+
flex: 1 1 auto;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.kanuni-datagrid-toolbar-search {
|
|
576
|
+
width: min(260px, 40%);
|
|
577
|
+
height: var(--dg-control-height);
|
|
578
|
+
padding: 0 8px;
|
|
579
|
+
border: 1px solid var(--dg-border-strong);
|
|
580
|
+
border-radius: var(--dg-radius-control);
|
|
581
|
+
background: var(--dg-surface);
|
|
582
|
+
color: var(--dg-text);
|
|
583
|
+
font: inherit;
|
|
584
|
+
font-size: var(--dg-control-font-size);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.kanuni-datagrid-toolbar-search:focus {
|
|
588
|
+
outline: none;
|
|
589
|
+
border-color: var(--dg-primary);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.kanuni-datagrid-toolbar > button {
|
|
593
|
+
padding: 3px 10px;
|
|
594
|
+
border: 1px solid var(--dg-border-strong);
|
|
595
|
+
border-radius: var(--dg-radius-control);
|
|
596
|
+
background: var(--dg-surface);
|
|
597
|
+
color: inherit;
|
|
598
|
+
font: inherit;
|
|
599
|
+
font-size: var(--dg-control-font-size);
|
|
600
|
+
cursor: pointer;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.kanuni-datagrid-toolbar > button:hover:not(:disabled) {
|
|
604
|
+
background: var(--dg-surface-hover);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.kanuni-datagrid-toolbar > button:disabled {
|
|
608
|
+
opacity: 0.6;
|
|
609
|
+
cursor: default;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.kanuni-datagrid-toolbar-error {
|
|
613
|
+
color: var(--dg-danger-text);
|
|
614
|
+
font-size: 11px;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/*
|
|
618
|
+
* The same message, but floating where a context menu just was — the cell
|
|
619
|
+
* menu's Export entries have nowhere else to report a missing optional peer.
|
|
620
|
+
* Sized and boxed like a menu so it reads as coming from one; it is a BUTTON
|
|
621
|
+
* because clicking it dismisses it, and a click target has to be one.
|
|
622
|
+
*/
|
|
623
|
+
.kanuni-datagrid-menu-error {
|
|
624
|
+
position: fixed;
|
|
625
|
+
/* Same layer as the context menu it replaces — see the rule further down. */
|
|
626
|
+
z-index: 1200;
|
|
627
|
+
max-width: 320px;
|
|
628
|
+
padding: 8px 10px;
|
|
629
|
+
text-align: left;
|
|
630
|
+
background: var(--dg-surface);
|
|
631
|
+
border: 1px solid var(--dg-danger-text);
|
|
632
|
+
border-radius: var(--dg-radius);
|
|
633
|
+
box-shadow: var(--dg-shadow-menu);
|
|
634
|
+
font-size: var(--dg-font-size);
|
|
635
|
+
cursor: pointer;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.kanuni-datagrid-column-chooser .kanuni-datagrid-filter-item {
|
|
639
|
+
cursor: pointer;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/* ── Pager ────────────────────────────────────────────────── */
|
|
643
|
+
.kanuni-datagrid-pager {
|
|
644
|
+
display: flex;
|
|
645
|
+
align-items: center;
|
|
646
|
+
flex-wrap: wrap;
|
|
647
|
+
gap: 12px;
|
|
648
|
+
flex: none;
|
|
649
|
+
padding: 5px 8px;
|
|
650
|
+
background: var(--dg-surface);
|
|
651
|
+
border-top: 1px solid var(--dg-border);
|
|
652
|
+
font-size: var(--dg-chrome-font-size);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.kanuni-datagrid-pager-size {
|
|
656
|
+
display: inline-flex;
|
|
657
|
+
align-items: center;
|
|
658
|
+
gap: 6px;
|
|
659
|
+
color: var(--dg-text-muted);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
.kanuni-datagrid-pager-size select {
|
|
663
|
+
padding: 2px 4px;
|
|
664
|
+
border: 1px solid var(--dg-border-strong);
|
|
665
|
+
border-radius: var(--dg-radius-control);
|
|
666
|
+
background: var(--dg-surface);
|
|
667
|
+
color: var(--dg-text);
|
|
668
|
+
font: inherit;
|
|
669
|
+
font-size: var(--dg-control-font-size);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.kanuni-datagrid-pager-range {
|
|
673
|
+
color: var(--dg-text-muted);
|
|
674
|
+
font-variant-numeric: tabular-nums;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
.kanuni-datagrid-pager-buttons {
|
|
678
|
+
display: inline-flex;
|
|
679
|
+
align-items: center;
|
|
680
|
+
gap: 4px;
|
|
681
|
+
margin-left: auto;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.kanuni-datagrid-pager-buttons button {
|
|
685
|
+
min-width: 26px;
|
|
686
|
+
height: calc(var(--dg-control-height) - 2px);
|
|
687
|
+
border: 1px solid var(--dg-border-strong);
|
|
688
|
+
border-radius: var(--dg-radius-control);
|
|
689
|
+
background: var(--dg-surface);
|
|
690
|
+
color: inherit;
|
|
691
|
+
font: inherit;
|
|
692
|
+
cursor: pointer;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
.kanuni-datagrid-pager-buttons button:hover:not(:disabled) {
|
|
696
|
+
background: var(--dg-surface-hover);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
.kanuni-datagrid-pager-buttons button:disabled {
|
|
700
|
+
opacity: 0.45;
|
|
701
|
+
cursor: default;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.kanuni-datagrid-pager-current {
|
|
705
|
+
padding: 0 6px;
|
|
706
|
+
font-variant-numeric: tabular-nums;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/* ── Group panel ──────────────────────────────────────────── */
|
|
710
|
+
.kanuni-datagrid-group-panel {
|
|
711
|
+
display: flex;
|
|
712
|
+
align-items: flex-start;
|
|
713
|
+
flex-wrap: wrap;
|
|
714
|
+
gap: 8px;
|
|
715
|
+
flex: none;
|
|
716
|
+
padding: var(--dg-chrome-padding-y) 8px;
|
|
717
|
+
background: var(--dg-surface-header);
|
|
718
|
+
border-bottom: 1px solid var(--dg-border);
|
|
719
|
+
font-size: var(--dg-chrome-font-size);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/* ── Cascading levels ──────────────────────────────────────
|
|
723
|
+
One row per level, each indented past the one above and joined to it by an
|
|
724
|
+
elbow. Grouping is a hierarchy; a flat row of chips said nothing about which
|
|
725
|
+
column was the OUTER level, which is the only thing the order encodes. */
|
|
726
|
+
.kanuni-datagrid-group-levels {
|
|
727
|
+
display: flex;
|
|
728
|
+
flex-direction: column;
|
|
729
|
+
gap: 2px;
|
|
730
|
+
min-width: 0;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.kanuni-datagrid-group-level {
|
|
734
|
+
position: relative;
|
|
735
|
+
display: flex;
|
|
736
|
+
align-items: center;
|
|
737
|
+
/* --dg-group-level is set per chip from its index. */
|
|
738
|
+
padding-inline-start: calc(var(--dg-group-level, 0) * 18px);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/* The elbow: down from the parent chip, then right into this one. */
|
|
742
|
+
.kanuni-datagrid-group-level:not(:first-child)::before {
|
|
743
|
+
content: '';
|
|
744
|
+
position: absolute;
|
|
745
|
+
inset-inline-start: calc((var(--dg-group-level, 1) - 1) * 18px + 9px);
|
|
746
|
+
top: -3px;
|
|
747
|
+
bottom: 50%;
|
|
748
|
+
width: 9px;
|
|
749
|
+
border-inline-start: 1px solid var(--dg-border-strong);
|
|
750
|
+
border-block-end: 1px solid var(--dg-border-strong);
|
|
751
|
+
border-end-start-radius: 4px;
|
|
752
|
+
pointer-events: none;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/* Drop indicator while a level is dragged over another. */
|
|
756
|
+
.kanuni-datagrid-group-level--drop-before::after,
|
|
757
|
+
.kanuni-datagrid-group-level--drop-after::after {
|
|
758
|
+
content: '';
|
|
759
|
+
position: absolute;
|
|
760
|
+
inset-inline: calc(var(--dg-group-level, 0) * 18px) 0;
|
|
761
|
+
height: 2px;
|
|
762
|
+
border-radius: 1px;
|
|
763
|
+
background: var(--dg-primary);
|
|
764
|
+
pointer-events: none;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
.kanuni-datagrid-group-level--drop-before::after {
|
|
768
|
+
top: -2px;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.kanuni-datagrid-group-level--drop-after::after {
|
|
772
|
+
bottom: -2px;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
.kanuni-datagrid-group-panel-actions {
|
|
776
|
+
display: flex;
|
|
777
|
+
align-items: center;
|
|
778
|
+
gap: 8px;
|
|
779
|
+
margin-inline-start: auto;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
.kanuni-datagrid-group-panel--over {
|
|
783
|
+
background: var(--dg-primary-soft);
|
|
784
|
+
outline: 2px dashed var(--dg-primary);
|
|
785
|
+
outline-offset: -3px;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.kanuni-datagrid-group-panel-hint {
|
|
789
|
+
color: var(--dg-text-muted);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.kanuni-datagrid-group-chip {
|
|
793
|
+
display: inline-flex;
|
|
794
|
+
align-items: center;
|
|
795
|
+
gap: 6px;
|
|
796
|
+
padding: 2px 6px 2px 10px;
|
|
797
|
+
border-radius: 999px;
|
|
798
|
+
background: var(--dg-primary);
|
|
799
|
+
color: var(--dg-on-primary);
|
|
800
|
+
font-weight: 600;
|
|
801
|
+
white-space: nowrap;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
.kanuni-datagrid-group-chip[draggable='true'] {
|
|
805
|
+
cursor: grab;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
.kanuni-datagrid-group-chip:focus-visible {
|
|
809
|
+
outline: 2px solid var(--dg-focus-ring);
|
|
810
|
+
outline-offset: 2px;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.kanuni-datagrid-group-chip button {
|
|
814
|
+
border: none;
|
|
815
|
+
background: transparent;
|
|
816
|
+
color: inherit;
|
|
817
|
+
font-size: 14px;
|
|
818
|
+
line-height: 1;
|
|
819
|
+
cursor: pointer;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.kanuni-datagrid-group-panel-actions > button {
|
|
823
|
+
padding: 2px 8px;
|
|
824
|
+
border: 1px solid var(--dg-border-strong);
|
|
825
|
+
border-radius: var(--dg-radius-control);
|
|
826
|
+
background: var(--dg-surface);
|
|
827
|
+
color: inherit;
|
|
828
|
+
font: inherit;
|
|
829
|
+
font-size: 12px;
|
|
830
|
+
cursor: pointer;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.kanuni-datagrid-group-select select {
|
|
834
|
+
padding: 2px 6px;
|
|
835
|
+
border: 1px solid var(--dg-border-strong);
|
|
836
|
+
border-radius: var(--dg-radius-control);
|
|
837
|
+
background: var(--dg-surface);
|
|
838
|
+
color: inherit;
|
|
839
|
+
font: inherit;
|
|
840
|
+
font-size: 12px;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/* ── Group rows ───────────────────────────────────────────── */
|
|
844
|
+
.kanuni-datagrid-group-row {
|
|
845
|
+
background: var(--dg-group-bg);
|
|
846
|
+
cursor: pointer;
|
|
847
|
+
font-weight: 600;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.kanuni-datagrid-group-row:hover {
|
|
851
|
+
background: var(--dg-surface-hover);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.kanuni-datagrid-group-cell {
|
|
855
|
+
/* Sticky so the label stays readable while scrolling horizontally. */
|
|
856
|
+
position: sticky;
|
|
857
|
+
inset-inline-start: 0;
|
|
858
|
+
display: flex;
|
|
859
|
+
align-items: center;
|
|
860
|
+
gap: 6px;
|
|
861
|
+
height: 100%;
|
|
862
|
+
padding: 0 var(--dg-cell-padding-x);
|
|
863
|
+
white-space: nowrap;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
.kanuni-datagrid-group-twisty {
|
|
867
|
+
display: inline-block;
|
|
868
|
+
width: 12px;
|
|
869
|
+
color: var(--dg-text-muted);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
.kanuni-datagrid-group-caption {
|
|
873
|
+
color: var(--dg-text-muted);
|
|
874
|
+
font-weight: 400;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
.kanuni-datagrid-group-count {
|
|
878
|
+
color: var(--dg-text-muted);
|
|
879
|
+
font-weight: 400;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/* Nested levels step back toward the body colour so depth reads at a glance
|
|
883
|
+
without needing to count the indent. */
|
|
884
|
+
.kanuni-datagrid-group-row--level-1 {
|
|
885
|
+
background: color-mix(in srgb, var(--dg-surface-header) 60%, var(--dg-surface));
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
.kanuni-datagrid-group-row--level-2 {
|
|
889
|
+
background: color-mix(in srgb, var(--dg-surface-header) 35%, var(--dg-surface));
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
.kanuni-datagrid-group-row--level-3 {
|
|
893
|
+
background: color-mix(in srgb, var(--dg-surface-header) 18%, var(--dg-surface));
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/* Aggregates trail the label; they must not push the count off-screen, so the
|
|
897
|
+
whole strip stays on one line and simply clips at the viewport edge. */
|
|
898
|
+
.kanuni-datagrid-group-aggregate {
|
|
899
|
+
margin-left: 12px;
|
|
900
|
+
font-weight: 600;
|
|
901
|
+
font-variant-numeric: tabular-nums;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
.kanuni-datagrid-group-aggregate-label {
|
|
905
|
+
color: var(--dg-text-muted);
|
|
906
|
+
font-weight: 400;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
.kanuni-datagrid-group-chip-level {
|
|
910
|
+
display: inline-flex;
|
|
911
|
+
align-items: center;
|
|
912
|
+
justify-content: center;
|
|
913
|
+
min-width: 15px;
|
|
914
|
+
height: 15px;
|
|
915
|
+
border-radius: 999px;
|
|
916
|
+
/* A wash of the chip's own FOREGROUND, not a fixed white: on a gold chip the
|
|
917
|
+
text is near-black, and a white pip on it disappeared. */
|
|
918
|
+
background: color-mix(in srgb, var(--dg-on-primary) 25%, transparent);
|
|
919
|
+
font-size: 10px;
|
|
920
|
+
font-weight: 700;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.kanuni-datagrid-group-chip button[disabled] {
|
|
924
|
+
opacity: 0.4;
|
|
925
|
+
cursor: default;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.kanuni-datagrid-group-row--focused {
|
|
929
|
+
outline: 2px solid var(--dg-focus-ring);
|
|
930
|
+
outline-offset: -2px;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/* ── Master-detail ────────────────────────────────────────── */
|
|
934
|
+
.kanuni-datagrid-detail-toggle-cell {
|
|
935
|
+
padding: 0;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
.kanuni-datagrid-detail-toggle {
|
|
939
|
+
width: 100%;
|
|
940
|
+
height: 100%;
|
|
941
|
+
border: none;
|
|
942
|
+
background: transparent;
|
|
943
|
+
color: var(--dg-text-muted);
|
|
944
|
+
font-size: 11px;
|
|
945
|
+
line-height: 1;
|
|
946
|
+
cursor: pointer;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
.kanuni-datagrid-detail-toggle:hover {
|
|
950
|
+
color: var(--dg-text);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/* The panel is sized in whole rows by the component (span x rowHeight); this
|
|
954
|
+
rule only styles it. Overriding the height here would desync it from the
|
|
955
|
+
filler slots the virtualizer reserved. */
|
|
956
|
+
.kanuni-datagrid-detail-row {
|
|
957
|
+
background: var(--dg-surface);
|
|
958
|
+
border-bottom: 1px solid var(--dg-border);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.kanuni-datagrid-detail-cell {
|
|
962
|
+
position: sticky;
|
|
963
|
+
inset-inline-start: 0;
|
|
964
|
+
box-sizing: border-box;
|
|
965
|
+
width: 100%;
|
|
966
|
+
height: 100%;
|
|
967
|
+
padding: 8px 12px;
|
|
968
|
+
overflow: auto;
|
|
969
|
+
border-inline-start: 3px solid var(--dg-primary);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/* ── In-grid edit form (mode: 'form') ─────────────────────── */
|
|
973
|
+
/* Sticky to the scroller's inline start so the form stays put while the
|
|
974
|
+
columns scroll under it — a form that slides off to the left with column 40
|
|
975
|
+
is a form the user has to chase. Width is the VIEWPORT's, not the canvas's,
|
|
976
|
+
for the same reason. */
|
|
977
|
+
.kanuni-datagrid-editform-row {
|
|
978
|
+
background: var(--dg-surface);
|
|
979
|
+
border-bottom: 1px solid var(--dg-border);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
.kanuni-datagrid-editform-cell {
|
|
983
|
+
position: sticky;
|
|
984
|
+
inset-inline-start: 0;
|
|
985
|
+
box-sizing: border-box;
|
|
986
|
+
/* The VIEWPORT's width, not the row's — see `--dg-viewport-width`. Falls
|
|
987
|
+
back to the row when nothing has measured yet (SSR, first paint). */
|
|
988
|
+
width: var(--dg-viewport-width, 100%);
|
|
989
|
+
height: 100%;
|
|
990
|
+
padding: 0;
|
|
991
|
+
border-inline-start: 3px solid var(--dg-primary);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/* ── Summary bar ──────────────────────────────────────────── */
|
|
995
|
+
.kanuni-datagrid-summary-bar {
|
|
996
|
+
display: flex;
|
|
997
|
+
align-items: center;
|
|
998
|
+
flex-wrap: wrap;
|
|
999
|
+
gap: 16px;
|
|
1000
|
+
flex: none;
|
|
1001
|
+
padding: 5px 8px;
|
|
1002
|
+
background: var(--dg-footer-bg);
|
|
1003
|
+
border-top: var(--dg-header-border);
|
|
1004
|
+
font-size: var(--dg-chrome-font-size);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
.kanuni-datagrid-summary-item {
|
|
1008
|
+
display: inline-flex;
|
|
1009
|
+
align-items: baseline;
|
|
1010
|
+
gap: 4px;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.kanuni-datagrid-summary-label {
|
|
1014
|
+
color: var(--dg-text-muted);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/* ── Selection ────────────────────────────────────────────── */
|
|
1018
|
+
.kanuni-datagrid-row--selected,
|
|
1019
|
+
.kanuni-datagrid-row--selected .kanuni-datagrid-cell--pinned {
|
|
1020
|
+
background: var(--dg-selected-bg);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
.kanuni-datagrid-row--selected:hover,
|
|
1024
|
+
.kanuni-datagrid-row--selected:hover .kanuni-datagrid-cell--pinned {
|
|
1025
|
+
background: var(--dg-selected-bg);
|
|
1026
|
+
filter: brightness(0.97);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
.kanuni-datagrid-select-cell {
|
|
1030
|
+
justify-content: center;
|
|
1031
|
+
padding: 0;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
.kanuni-datagrid-select-cell input {
|
|
1035
|
+
accent-color: var(--dg-primary);
|
|
1036
|
+
cursor: pointer;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/* ── Row numbers (gap analysis P3.5) ───────────────────────── */
|
|
1040
|
+
/* Muted and tabular on purpose. The number is an address, not data: at full
|
|
1041
|
+
text weight it competes with the first real column, and proportional digits
|
|
1042
|
+
make a column of numbers ragged down its right edge. */
|
|
1043
|
+
.kanuni-datagrid-rownum-cell {
|
|
1044
|
+
color: var(--dg-text-muted);
|
|
1045
|
+
font-variant-numeric: tabular-nums;
|
|
1046
|
+
background: var(--dg-header-bg);
|
|
1047
|
+
user-select: none;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
.kanuni-datagrid-row--selected .kanuni-datagrid-rownum-cell {
|
|
1051
|
+
background: var(--dg-selected-bg);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/* ── Keyboard focus ───────────────────────────────────────── */
|
|
1055
|
+
/* Focus is virtual (aria-activedescendant), so it must be drawn explicitly —
|
|
1056
|
+
there is no browser focus ring on the cell itself. `inset` keeps the outline
|
|
1057
|
+
from being clipped by the neighbouring cell. */
|
|
1058
|
+
.kanuni-datagrid-cell--focused {
|
|
1059
|
+
outline: 2px solid var(--dg-focus-ring);
|
|
1060
|
+
outline-offset: -2px;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
.kanuni-datagrid-scroller:focus {
|
|
1064
|
+
outline: none;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
.kanuni-datagrid-scroller:focus-visible {
|
|
1068
|
+
outline: 2px solid var(--dg-focus-ring);
|
|
1069
|
+
outline-offset: -2px;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/* Visually hidden but still read aloud. */
|
|
1073
|
+
.kanuni-datagrid-sr-only {
|
|
1074
|
+
position: absolute;
|
|
1075
|
+
width: 1px;
|
|
1076
|
+
height: 1px;
|
|
1077
|
+
margin: -1px;
|
|
1078
|
+
padding: 0;
|
|
1079
|
+
overflow: hidden;
|
|
1080
|
+
clip: rect(0, 0, 0, 0);
|
|
1081
|
+
white-space: nowrap;
|
|
1082
|
+
border: 0;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/* ── Header filter button ──────────────────────────────────
|
|
1086
|
+
Hidden until the header is hovered or focused (`filterIconMode: 'hover'`,
|
|
1087
|
+
the default). `visibility` rather than `display` so the icon does not push
|
|
1088
|
+
the label around when it appears — the space is reserved either way. */
|
|
1089
|
+
.kanuni-datagrid-filter-button {
|
|
1090
|
+
display: inline-flex;
|
|
1091
|
+
align-items: center;
|
|
1092
|
+
justify-content: center;
|
|
1093
|
+
flex: none;
|
|
1094
|
+
margin-inline-start: auto;
|
|
1095
|
+
padding: 1px 3px;
|
|
1096
|
+
border: none;
|
|
1097
|
+
border-radius: 3px;
|
|
1098
|
+
background: transparent;
|
|
1099
|
+
/* Lives INSIDE the header cell, so it follows the header's foreground: on an
|
|
1100
|
+
ink header the body's muted slate is barely visible. */
|
|
1101
|
+
color: var(--dg-header-text-muted);
|
|
1102
|
+
line-height: 0;
|
|
1103
|
+
cursor: pointer;
|
|
1104
|
+
visibility: hidden;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
.kanuni-datagrid-header-cell:hover > .kanuni-datagrid-filter-button,
|
|
1108
|
+
.kanuni-datagrid-header-cell:focus-within > .kanuni-datagrid-filter-button,
|
|
1109
|
+
.kanuni-datagrid-filter-button--pinned,
|
|
1110
|
+
/* An ACTIVE filter is the only sign that rows are being withheld, so it stays
|
|
1111
|
+
visible in both modes — hiding it would make the grid look simply empty. */
|
|
1112
|
+
.kanuni-datagrid-filter-button--active {
|
|
1113
|
+
visibility: visible;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
.kanuni-datagrid-filter-button:hover {
|
|
1117
|
+
background: var(--dg-primary-soft);
|
|
1118
|
+
color: var(--dg-text);
|
|
1119
|
+
/* `--dg-primary-soft` is a LIGHT tint in every light theme, so the label on
|
|
1120
|
+
it is the body text, not the header's — this is the one spot in the header
|
|
1121
|
+
that deliberately does not follow `--dg-header-text`. */
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
.kanuni-datagrid-filter-button--active {
|
|
1125
|
+
color: var(--dg-primary);
|
|
1126
|
+
background: var(--dg-primary-soft);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/* ── Column reorder (drag & drop) ──────────────────────────
|
|
1130
|
+
The indicator is an inset shadow, not a border: a border would change the
|
|
1131
|
+
cell's box and shift every column to its right by a pixel mid-drag. */
|
|
1132
|
+
.kanuni-datagrid-header-cell--drop-before {
|
|
1133
|
+
box-shadow: inset 3px 0 0 -1px var(--dg-primary);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
.kanuni-datagrid-header-cell--drop-after {
|
|
1137
|
+
box-shadow: inset -3px 0 0 -1px var(--dg-primary);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
[dir='rtl'] .kanuni-datagrid-header-cell--drop-before {
|
|
1141
|
+
box-shadow: inset -3px 0 0 -1px var(--dg-primary);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
[dir='rtl'] .kanuni-datagrid-header-cell--drop-after {
|
|
1145
|
+
box-shadow: inset 3px 0 0 -1px var(--dg-primary);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/* ── Column resize handle ─────────────────────────────────── */
|
|
1149
|
+
.kanuni-datagrid-resize-handle {
|
|
1150
|
+
position: absolute;
|
|
1151
|
+
inset-block: 0;
|
|
1152
|
+
inset-inline-end: -3px;
|
|
1153
|
+
width: 7px;
|
|
1154
|
+
z-index: 5;
|
|
1155
|
+
cursor: col-resize;
|
|
1156
|
+
/* Invisible by default; the line under the cursor is what marks the seam. */
|
|
1157
|
+
background: transparent;
|
|
1158
|
+
touch-action: none;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
.kanuni-datagrid-resize-handle:hover::after,
|
|
1162
|
+
.kanuni-datagrid-header-cell--resizing .kanuni-datagrid-resize-handle::after {
|
|
1163
|
+
content: '';
|
|
1164
|
+
position: absolute;
|
|
1165
|
+
inset-block: 4px;
|
|
1166
|
+
inset-inline-start: 3px;
|
|
1167
|
+
width: 2px;
|
|
1168
|
+
border-radius: 1px;
|
|
1169
|
+
background: var(--dg-primary);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/* ── Loading overlay (busy + percentage) ──────────────────── */
|
|
1173
|
+
.kanuni-datagrid-loading-overlay {
|
|
1174
|
+
position: absolute;
|
|
1175
|
+
inset: 0;
|
|
1176
|
+
z-index: 20;
|
|
1177
|
+
display: flex;
|
|
1178
|
+
align-items: center;
|
|
1179
|
+
justify-content: center;
|
|
1180
|
+
background: color-mix(in srgb, var(--dg-bg) 62%, transparent);
|
|
1181
|
+
/* Pointer-transparent: a recompute triggered by typing must not swallow the
|
|
1182
|
+
next keystroke aimed at the filter box. */
|
|
1183
|
+
pointer-events: none;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/* Exporting is the one phase that takes the grid away, so it looks like it.
|
|
1187
|
+
The sheet is nearly opaque instead of a hint, it swallows clicks instead of
|
|
1188
|
+
letting them fall through to a grid that cannot answer them, and the cursor
|
|
1189
|
+
says wait. See the comment on `data-blocking` in GridLoadingOverlay.tsx. */
|
|
1190
|
+
.kanuni-datagrid-loading-overlay[data-blocking='true'] {
|
|
1191
|
+
background: color-mix(in srgb, var(--dg-bg) 92%, transparent);
|
|
1192
|
+
pointer-events: auto;
|
|
1193
|
+
cursor: progress;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
.kanuni-datagrid-loading-card {
|
|
1197
|
+
display: flex;
|
|
1198
|
+
flex-direction: column;
|
|
1199
|
+
align-items: center;
|
|
1200
|
+
gap: 10px;
|
|
1201
|
+
padding: 18px 24px;
|
|
1202
|
+
border: 1px solid var(--dg-border);
|
|
1203
|
+
border-radius: calc(var(--dg-radius) + 4px);
|
|
1204
|
+
background: var(--dg-surface);
|
|
1205
|
+
box-shadow: var(--dg-shadow-card);
|
|
1206
|
+
color: var(--dg-text);
|
|
1207
|
+
font-size: var(--dg-chrome-font-size);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
.kanuni-datagrid-loading-spinner {
|
|
1211
|
+
width: 26px;
|
|
1212
|
+
height: 26px;
|
|
1213
|
+
border: 2px solid transparent;
|
|
1214
|
+
border-bottom-color: var(--dg-primary);
|
|
1215
|
+
border-radius: 50%;
|
|
1216
|
+
animation: kanuni-datagrid-spin 1s linear infinite;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
.kanuni-datagrid-loading-label {
|
|
1220
|
+
font-weight: 500;
|
|
1221
|
+
color: var(--dg-text-muted);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
.kanuni-datagrid-loading-progress {
|
|
1225
|
+
display: flex;
|
|
1226
|
+
flex-direction: column;
|
|
1227
|
+
align-items: center;
|
|
1228
|
+
gap: 6px;
|
|
1229
|
+
width: 220px;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
.kanuni-datagrid-loading-track {
|
|
1233
|
+
width: 100%;
|
|
1234
|
+
height: 6px;
|
|
1235
|
+
border-radius: 999px;
|
|
1236
|
+
overflow: hidden;
|
|
1237
|
+
background: color-mix(in srgb, var(--dg-border) 70%, transparent);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
.kanuni-datagrid-loading-fill {
|
|
1241
|
+
height: 100%;
|
|
1242
|
+
border-radius: 999px;
|
|
1243
|
+
background: var(--dg-primary);
|
|
1244
|
+
transition: width 160ms ease-out;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
.kanuni-datagrid-loading-percent {
|
|
1248
|
+
font-size: 11px;
|
|
1249
|
+
font-weight: 600;
|
|
1250
|
+
font-variant-numeric: tabular-nums;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/* ── Filter panel (cxGrid) ────────────────────────────────────
|
|
1254
|
+
Under the footer, outside the scroller: the conditions describe the whole
|
|
1255
|
+
result, so they must not scroll away with the rows. */
|
|
1256
|
+
.kanuni-datagrid-filter-panel {
|
|
1257
|
+
display: flex;
|
|
1258
|
+
align-items: center;
|
|
1259
|
+
gap: 6px;
|
|
1260
|
+
flex: none;
|
|
1261
|
+
padding: 4px 8px;
|
|
1262
|
+
background: var(--dg-surface-header);
|
|
1263
|
+
border-top: 1px solid var(--dg-border);
|
|
1264
|
+
font-size: var(--dg-chrome-font-size);
|
|
1265
|
+
color: var(--dg-text);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
/* Suspended: the conditions are still there, just not being applied. Dimmed
|
|
1269
|
+
rather than hidden — there has to be something to switch back on. */
|
|
1270
|
+
.kanuni-datagrid-filter-panel--suspended .kanuni-datagrid-filter-panel-text {
|
|
1271
|
+
opacity: 0.5;
|
|
1272
|
+
text-decoration: line-through;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
.kanuni-datagrid-filter-panel-clear,
|
|
1276
|
+
.kanuni-datagrid-filter-panel-builder {
|
|
1277
|
+
display: inline-flex;
|
|
1278
|
+
align-items: center;
|
|
1279
|
+
justify-content: center;
|
|
1280
|
+
flex: none;
|
|
1281
|
+
width: 18px;
|
|
1282
|
+
height: 18px;
|
|
1283
|
+
padding: 0;
|
|
1284
|
+
border: 1px solid transparent;
|
|
1285
|
+
border-radius: var(--dg-radius-control);
|
|
1286
|
+
background: transparent;
|
|
1287
|
+
color: var(--dg-text-muted);
|
|
1288
|
+
font: inherit;
|
|
1289
|
+
line-height: 1;
|
|
1290
|
+
cursor: pointer;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
.kanuni-datagrid-filter-panel-clear:hover:not(:disabled),
|
|
1294
|
+
.kanuni-datagrid-filter-panel-builder:hover:not(:disabled) {
|
|
1295
|
+
background: var(--dg-surface-hover);
|
|
1296
|
+
border-color: var(--dg-border-strong);
|
|
1297
|
+
color: var(--dg-text);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
.kanuni-datagrid-filter-panel-clear:disabled,
|
|
1301
|
+
.kanuni-datagrid-filter-panel-builder:disabled {
|
|
1302
|
+
opacity: 0.4;
|
|
1303
|
+
cursor: default;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
.kanuni-datagrid-filter-panel-toggle {
|
|
1307
|
+
flex: none;
|
|
1308
|
+
margin: 0;
|
|
1309
|
+
accent-color: var(--dg-primary);
|
|
1310
|
+
cursor: pointer;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
.kanuni-datagrid-filter-panel-text {
|
|
1314
|
+
flex: 1 1 auto;
|
|
1315
|
+
min-width: 0;
|
|
1316
|
+
overflow: hidden;
|
|
1317
|
+
text-overflow: ellipsis;
|
|
1318
|
+
white-space: nowrap;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
.kanuni-datagrid-filter-panel-builder {
|
|
1322
|
+
margin-inline-start: auto;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/* Shown only for phases that can actually stop — see CANCELLABLE in the overlay. */
|
|
1326
|
+
.kanuni-datagrid-loading-cancel {
|
|
1327
|
+
margin-top: 8px;
|
|
1328
|
+
padding: 3px 12px;
|
|
1329
|
+
border: 1px solid var(--dg-border);
|
|
1330
|
+
border-radius: var(--dg-radius);
|
|
1331
|
+
background: var(--dg-surface);
|
|
1332
|
+
color: inherit;
|
|
1333
|
+
font: inherit;
|
|
1334
|
+
font-size: 11px;
|
|
1335
|
+
cursor: pointer;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
.kanuni-datagrid-loading-cancel:hover {
|
|
1339
|
+
background: var(--dg-surface-hover);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1343
|
+
.kanuni-datagrid-loading-spinner {
|
|
1344
|
+
animation-duration: 3s;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
.kanuni-datagrid-loading-fill {
|
|
1348
|
+
transition: none;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
/* ── Filter row (under the header, same sticky geometry) ──── */
|
|
1353
|
+
.kanuni-datagrid-filter-row {
|
|
1354
|
+
position: sticky;
|
|
1355
|
+
/* Clears the BANDS as well as the header. `--dg-band-offset` is published by
|
|
1356
|
+
the component (0 when there are no bands); the header height stays a
|
|
1357
|
+
variable here so density keeps working. Sticking at the header height alone
|
|
1358
|
+
parked this row inside the header on any banded grid, and the two drew over
|
|
1359
|
+
each other as soon as the user scrolled. */
|
|
1360
|
+
top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height));
|
|
1361
|
+
z-index: 3;
|
|
1362
|
+
display: flex;
|
|
1363
|
+
height: var(--dg-header-height);
|
|
1364
|
+
background: var(--dg-surface-header);
|
|
1365
|
+
border-bottom: var(--dg-header-border);
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
.kanuni-datagrid-filter-cell {
|
|
1369
|
+
display: flex;
|
|
1370
|
+
align-items: center;
|
|
1371
|
+
flex: none;
|
|
1372
|
+
height: 100%;
|
|
1373
|
+
padding: 0 4px;
|
|
1374
|
+
background: var(--dg-surface-header);
|
|
1375
|
+
border-inline-end: var(--dg-header-cell-border);
|
|
1376
|
+
overflow: hidden;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
.kanuni-datagrid-filter-cell--pinned {
|
|
1380
|
+
position: sticky;
|
|
1381
|
+
z-index: 4;
|
|
1382
|
+
background: var(--dg-surface-header);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
.kanuni-datagrid-filter-input {
|
|
1386
|
+
width: 100%;
|
|
1387
|
+
min-width: 0;
|
|
1388
|
+
height: var(--dg-filter-input-height);
|
|
1389
|
+
padding: 0 6px;
|
|
1390
|
+
border: 1px solid var(--dg-border-strong);
|
|
1391
|
+
border-radius: var(--dg-radius-control);
|
|
1392
|
+
background: var(--dg-surface);
|
|
1393
|
+
color: var(--dg-text);
|
|
1394
|
+
font: inherit;
|
|
1395
|
+
font-size: var(--dg-control-font-size);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
.kanuni-datagrid-filter-input:focus {
|
|
1399
|
+
outline: none;
|
|
1400
|
+
border-color: var(--dg-primary);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
/* ── Header filter popup (portaled — see GridPortal) ──────── */
|
|
1404
|
+
.kanuni-datagrid-portal {
|
|
1405
|
+
/* The portal host must not paint or capture anything itself. */
|
|
1406
|
+
position: static;
|
|
1407
|
+
border: none;
|
|
1408
|
+
background: transparent;
|
|
1409
|
+
overflow: visible;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/* ── Band fold toggle (P2.6) ──────────────────────────────── */
|
|
1413
|
+
.kanuni-datagrid-band-toggle {
|
|
1414
|
+
display: inline-flex;
|
|
1415
|
+
align-items: center;
|
|
1416
|
+
justify-content: center;
|
|
1417
|
+
flex: none;
|
|
1418
|
+
width: 16px;
|
|
1419
|
+
height: 16px;
|
|
1420
|
+
margin-inline-end: 4px;
|
|
1421
|
+
padding: 0;
|
|
1422
|
+
border: none;
|
|
1423
|
+
border-radius: 3px;
|
|
1424
|
+
background: transparent;
|
|
1425
|
+
color: var(--dg-text-muted);
|
|
1426
|
+
font-size: 10px;
|
|
1427
|
+
line-height: 1;
|
|
1428
|
+
cursor: pointer;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
.kanuni-datagrid-band-toggle:hover {
|
|
1432
|
+
background: var(--dg-primary-soft);
|
|
1433
|
+
color: var(--dg-text);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
.kanuni-datagrid-band-toggle:focus-visible {
|
|
1437
|
+
outline: 2px solid var(--dg-focus-ring);
|
|
1438
|
+
outline-offset: -1px;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/* ── Search highlight (P2.4) ──────────────────────────────── */
|
|
1442
|
+
/* A <mark>, so the match is in the accessibility tree as emphasis rather than
|
|
1443
|
+
as a colour only. The browser default is a hard yellow that fights every
|
|
1444
|
+
theme; these follow the accent instead. */
|
|
1445
|
+
.kanuni-datagrid-highlight {
|
|
1446
|
+
padding: 0;
|
|
1447
|
+
border-radius: 2px;
|
|
1448
|
+
background: var(--dg-highlight-bg);
|
|
1449
|
+
color: var(--dg-highlight-text);
|
|
1450
|
+
font-weight: 600;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
/* ── Cell / header tooltip (P2.1) ─────────────────────────── */
|
|
1454
|
+
.kanuni-datagrid-tooltip {
|
|
1455
|
+
position: fixed;
|
|
1456
|
+
/* Above every popup: a tooltip describes whatever is under the pointer, and
|
|
1457
|
+
the pointer can be over a popup. */
|
|
1458
|
+
z-index: 1100;
|
|
1459
|
+
/* The tooltip must never be the hover target — it appears UNDER the pointer's
|
|
1460
|
+
path, and taking the pointer would fire mouseout on the cell that asked for
|
|
1461
|
+
it, hiding the tooltip, which un-hovers it, which shows it again. */
|
|
1462
|
+
pointer-events: none;
|
|
1463
|
+
max-width: 420px;
|
|
1464
|
+
padding: 4px 8px;
|
|
1465
|
+
border: 1px solid var(--dg-border-strong);
|
|
1466
|
+
border-radius: var(--dg-radius);
|
|
1467
|
+
background: var(--dg-surface);
|
|
1468
|
+
color: var(--dg-text);
|
|
1469
|
+
box-shadow: var(--dg-shadow-popup);
|
|
1470
|
+
font-size: var(--dg-font-size);
|
|
1471
|
+
line-height: 1.45;
|
|
1472
|
+
/* Long values wrap instead of running off-screen; a pasted paragraph in a
|
|
1473
|
+
cell is exactly the case the tooltip exists for. */
|
|
1474
|
+
white-space: pre-wrap;
|
|
1475
|
+
overflow-wrap: anywhere;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
.kanuni-datagrid-filter-popup {
|
|
1479
|
+
position: fixed;
|
|
1480
|
+
z-index: 1000;
|
|
1481
|
+
display: flex;
|
|
1482
|
+
flex-direction: column;
|
|
1483
|
+
gap: 6px;
|
|
1484
|
+
width: 260px;
|
|
1485
|
+
max-height: 340px;
|
|
1486
|
+
padding: 8px;
|
|
1487
|
+
border: 1px solid var(--dg-border-strong);
|
|
1488
|
+
border-radius: var(--dg-radius);
|
|
1489
|
+
/* Explicit background: a transparent popup was the PivotGrid drill-down bug. */
|
|
1490
|
+
background: var(--dg-surface);
|
|
1491
|
+
color: var(--dg-text);
|
|
1492
|
+
box-shadow: var(--dg-shadow-popup);
|
|
1493
|
+
font-size: var(--dg-font-size);
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
.kanuni-datagrid-filter-search {
|
|
1497
|
+
width: 100%;
|
|
1498
|
+
height: var(--dg-control-height);
|
|
1499
|
+
padding: 0 8px;
|
|
1500
|
+
border: 1px solid var(--dg-border-strong);
|
|
1501
|
+
border-radius: var(--dg-radius-control);
|
|
1502
|
+
background: var(--dg-surface);
|
|
1503
|
+
color: var(--dg-text);
|
|
1504
|
+
font: inherit;
|
|
1505
|
+
font-size: var(--dg-control-font-size);
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
/* "There are more values than these." Sits directly under the search box
|
|
1509
|
+
because the search box is the answer to it — on a capped list, search scans
|
|
1510
|
+
the whole dataset rather than the values already on screen. */
|
|
1511
|
+
.kanuni-datagrid-filter-truncated {
|
|
1512
|
+
margin: 6px 0 0;
|
|
1513
|
+
font-size: 11px;
|
|
1514
|
+
line-height: 1.4;
|
|
1515
|
+
color: var(--dg-text-muted);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
.kanuni-datagrid-filter-actions {
|
|
1519
|
+
display: flex;
|
|
1520
|
+
gap: 6px;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
.kanuni-datagrid-filter-actions button,
|
|
1524
|
+
.kanuni-datagrid-filter-footer button {
|
|
1525
|
+
padding: 3px 8px;
|
|
1526
|
+
border: 1px solid var(--dg-border-strong);
|
|
1527
|
+
border-radius: var(--dg-radius-control);
|
|
1528
|
+
background: var(--dg-surface);
|
|
1529
|
+
color: var(--dg-text);
|
|
1530
|
+
font: inherit;
|
|
1531
|
+
font-size: var(--dg-control-font-size);
|
|
1532
|
+
cursor: pointer;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
.kanuni-datagrid-filter-actions button:hover,
|
|
1536
|
+
.kanuni-datagrid-filter-footer button:hover {
|
|
1537
|
+
background: var(--dg-surface-hover);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
.kanuni-datagrid-filter-primary {
|
|
1541
|
+
background: var(--dg-primary) !important;
|
|
1542
|
+
border-color: var(--dg-primary) !important;
|
|
1543
|
+
color: var(--dg-on-primary) !important;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
.kanuni-datagrid-filter-list {
|
|
1547
|
+
flex: 1 1 auto;
|
|
1548
|
+
overflow-y: auto;
|
|
1549
|
+
/* Windowed list — scroll anchoring off for the same reason as the main
|
|
1550
|
+
scroller: rows repositioning during scroll read as content shifts and the
|
|
1551
|
+
browser fights the gesture. See `.kanuni-datagrid-scroller`. */
|
|
1552
|
+
overflow-anchor: none;
|
|
1553
|
+
/* Stop the wheel from CHAINING into the grid once the list hits its end.
|
|
1554
|
+
Without this, wheeling to the bottom of the list keeps going and scrolls
|
|
1555
|
+
the rows underneath — which moves the anchor and closes the popup, in the
|
|
1556
|
+
middle of a gesture the user meant for the list. */
|
|
1557
|
+
overscroll-behavior: contain;
|
|
1558
|
+
border: 1px solid var(--dg-border);
|
|
1559
|
+
border-radius: var(--dg-radius-control);
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
.kanuni-datagrid-filter-item {
|
|
1563
|
+
display: flex;
|
|
1564
|
+
align-items: center;
|
|
1565
|
+
gap: 6px;
|
|
1566
|
+
padding: 3px 6px;
|
|
1567
|
+
cursor: pointer;
|
|
1568
|
+
white-space: nowrap;
|
|
1569
|
+
overflow: hidden;
|
|
1570
|
+
text-overflow: ellipsis;
|
|
1571
|
+
/* FIXED, and the virtualizer depends on it: the list renders only the rows in
|
|
1572
|
+
view and positions them by `index x FILTER_ITEM_HEIGHT`. A label that
|
|
1573
|
+
wrapped to two lines would put every row below it out of place. Single-line
|
|
1574
|
+
is already enforced by `white-space: nowrap` above; this makes the height
|
|
1575
|
+
the popup computes with a fact rather than an assumption.
|
|
1576
|
+
Keep in sync with FILTER_ITEM_HEIGHT in GridHeaderFilterPopup.tsx. */
|
|
1577
|
+
height: 25px;
|
|
1578
|
+
box-sizing: border-box;
|
|
1579
|
+
flex: none;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
.kanuni-datagrid-filter-item:hover,
|
|
1583
|
+
.kanuni-datagrid-filter-item--active {
|
|
1584
|
+
background: var(--dg-surface-hover);
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
.kanuni-datagrid-filter-item input {
|
|
1588
|
+
flex: none;
|
|
1589
|
+
accent-color: var(--dg-primary);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
.kanuni-datagrid-filter-empty {
|
|
1593
|
+
padding: 10px;
|
|
1594
|
+
color: var(--dg-text-muted);
|
|
1595
|
+
text-align: center;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
.kanuni-datagrid-filter-footer {
|
|
1599
|
+
display: flex;
|
|
1600
|
+
justify-content: flex-end;
|
|
1601
|
+
gap: 6px;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
/* ── Empty state ──────────────────────────────────────────── */
|
|
1605
|
+
.kanuni-datagrid-empty {
|
|
1606
|
+
display: flex;
|
|
1607
|
+
align-items: center;
|
|
1608
|
+
justify-content: center;
|
|
1609
|
+
padding: 32px 16px;
|
|
1610
|
+
color: var(--dg-text-muted);
|
|
1611
|
+
font-size: var(--dg-font-size);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
/* ── Accessibility ────────────────────────────────────────── */
|
|
1615
|
+
.kanuni-datagrid-root :focus-visible {
|
|
1616
|
+
outline: 2px solid var(--dg-focus-ring);
|
|
1617
|
+
outline-offset: -2px;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1621
|
+
.kanuni-datagrid-viewport {
|
|
1622
|
+
will-change: auto;
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
/* ── Remote loading rows (P10) ────────────────────────────── */
|
|
1627
|
+
/* A row that exists on the server but has not arrived. It keeps its slot so
|
|
1628
|
+
the scrollbar and every row index stay exact while the block is in flight. */
|
|
1629
|
+
.kanuni-datagrid-row--loading {
|
|
1630
|
+
align-items: center;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
.kanuni-datagrid-loading-cell {
|
|
1634
|
+
position: sticky;
|
|
1635
|
+
inset-inline-start: 0;
|
|
1636
|
+
flex: 1;
|
|
1637
|
+
border-inline-end: none;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
.kanuni-datagrid-loading-bar {
|
|
1641
|
+
display: block;
|
|
1642
|
+
width: 40%;
|
|
1643
|
+
max-width: 240px;
|
|
1644
|
+
height: 8px;
|
|
1645
|
+
border-radius: 4px;
|
|
1646
|
+
background: var(--dg-surface-hover);
|
|
1647
|
+
animation: kanuni-datagrid-pulse 1.2s ease-in-out infinite;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
@keyframes kanuni-datagrid-pulse {
|
|
1651
|
+
0%,
|
|
1652
|
+
100% {
|
|
1653
|
+
opacity: 0.45;
|
|
1654
|
+
}
|
|
1655
|
+
50% {
|
|
1656
|
+
opacity: 1;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
/* Respect a reduced-motion preference: the pulse is decoration, and a user who
|
|
1661
|
+
asked for stillness should get a static placeholder. */
|
|
1662
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1663
|
+
.kanuni-datagrid-loading-bar {
|
|
1664
|
+
animation: none;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
|
|
1669
|
+
/* ── RTL ──────────────────────────────────────────────────────
|
|
1670
|
+
Insets are logical (inset-inline-*), so sticky columns flip on their own.
|
|
1671
|
+
What does NOT flip automatically is a directional SHADOW: the edge shadow
|
|
1672
|
+
that separates a left-pinned block from the scrolling body has to fall on
|
|
1673
|
+
the other side when the writing direction reverses. */
|
|
1674
|
+
.kanuni-datagrid-root[dir='rtl'] .kanuni-datagrid-pin-edge-left,
|
|
1675
|
+
[dir='rtl'] .kanuni-datagrid-root .kanuni-datagrid-pin-edge-left {
|
|
1676
|
+
box-shadow: var(--dg-shadow-pinned-right);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
.kanuni-datagrid-root[dir='rtl'] .kanuni-datagrid-pin-edge-right,
|
|
1680
|
+
[dir='rtl'] .kanuni-datagrid-root .kanuni-datagrid-pin-edge-right {
|
|
1681
|
+
box-shadow: var(--dg-shadow-pinned);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
/* The sort caret and the resize handle are positioned, not flowed. */
|
|
1685
|
+
.kanuni-datagrid-root[dir='rtl'] .kanuni-datagrid-resize-handle,
|
|
1686
|
+
[dir='rtl'] .kanuni-datagrid-root .kanuni-datagrid-resize-handle {
|
|
1687
|
+
right: auto;
|
|
1688
|
+
left: 0;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
/* ── Density presets ──────────────────────────────────────────
|
|
1692
|
+
The ROW HEIGHT is not set here: it is a core value (the virtualizer runs on
|
|
1693
|
+
it), so the component resolves the preset into `rowHeight` and core feeds it
|
|
1694
|
+
back as --dg-row-height. What lives here is everything CSS owns — header
|
|
1695
|
+
height, padding and type scale — so the two can never disagree about the
|
|
1696
|
+
row geometry. */
|
|
1697
|
+
.kanuni-datagrid-root.kanuni-datagrid-density-compact {
|
|
1698
|
+
--dg-header-height: 28px;
|
|
1699
|
+
--dg-cell-padding-x: 6px;
|
|
1700
|
+
--dg-font-size: 12px;
|
|
1701
|
+
--dg-header-font-size: 11px;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
.kanuni-datagrid-root.kanuni-datagrid-density-comfortable {
|
|
1705
|
+
--dg-header-height: 40px;
|
|
1706
|
+
--dg-cell-padding-x: 12px;
|
|
1707
|
+
--dg-font-size: 14px;
|
|
1708
|
+
--dg-header-font-size: 13px;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
/* ── Row context menu ─────────────────────────────────────── */
|
|
1712
|
+
.kanuni-datagrid-context-menu {
|
|
1713
|
+
position: fixed;
|
|
1714
|
+
z-index: 1200;
|
|
1715
|
+
min-width: 180px;
|
|
1716
|
+
max-width: 320px;
|
|
1717
|
+
/*
|
|
1718
|
+
* A menu longer than the screen scrolls instead of running off it: the
|
|
1719
|
+
* clamp in `GridContextMenu` can only move a menu that FITS, and an app row
|
|
1720
|
+
* menu with a dozen entries plus the built-ins does not.
|
|
1721
|
+
*
|
|
1722
|
+
* This is safe only because the SUBMENU is portaled as a sibling. While it
|
|
1723
|
+
* was a child, any overflow value other than `visible` clipped it away
|
|
1724
|
+
* entirely — which is why this rule was removed once before.
|
|
1725
|
+
*/
|
|
1726
|
+
max-height: calc(100vh - 16px);
|
|
1727
|
+
overflow-y: auto;
|
|
1728
|
+
padding: 4px;
|
|
1729
|
+
border: 1px solid var(--dg-border-strong);
|
|
1730
|
+
border-radius: var(--dg-radius);
|
|
1731
|
+
background: var(--dg-surface);
|
|
1732
|
+
color: var(--dg-text);
|
|
1733
|
+
box-shadow: var(--dg-shadow-menu);
|
|
1734
|
+
font-size: var(--dg-font-size);
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
.kanuni-datagrid-context-menu:focus {
|
|
1738
|
+
outline: none;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
.kanuni-datagrid-menu-row {
|
|
1742
|
+
position: relative;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
.kanuni-datagrid-menu-item {
|
|
1746
|
+
display: flex;
|
|
1747
|
+
align-items: center;
|
|
1748
|
+
gap: 8px;
|
|
1749
|
+
width: 100%;
|
|
1750
|
+
padding: 5px 8px;
|
|
1751
|
+
border: none;
|
|
1752
|
+
border-radius: var(--dg-radius-control);
|
|
1753
|
+
background: transparent;
|
|
1754
|
+
color: inherit;
|
|
1755
|
+
font: inherit;
|
|
1756
|
+
text-align: start;
|
|
1757
|
+
cursor: pointer;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/* Hover and keyboard highlight share one class so the two navigation modes
|
|
1761
|
+
look identical — a menu where the arrow keys highlight differently from the
|
|
1762
|
+
mouse reads as two different menus. */
|
|
1763
|
+
.kanuni-datagrid-menu-item:hover:not(:disabled),
|
|
1764
|
+
.kanuni-datagrid-menu-item--active:not(:disabled) {
|
|
1765
|
+
background: var(--dg-surface-hover);
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
.kanuni-datagrid-menu-item:disabled {
|
|
1769
|
+
opacity: 0.45;
|
|
1770
|
+
cursor: default;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
.kanuni-datagrid-menu-icon {
|
|
1774
|
+
display: inline-flex;
|
|
1775
|
+
align-items: center;
|
|
1776
|
+
flex: none;
|
|
1777
|
+
width: 16px;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
.kanuni-datagrid-menu-label {
|
|
1781
|
+
flex: 1 1 auto;
|
|
1782
|
+
overflow: hidden;
|
|
1783
|
+
text-overflow: ellipsis;
|
|
1784
|
+
white-space: nowrap;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
.kanuni-datagrid-menu-shortcut {
|
|
1788
|
+
flex: none;
|
|
1789
|
+
color: var(--dg-text-muted);
|
|
1790
|
+
font-size: 11px;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
.kanuni-datagrid-menu-caret {
|
|
1794
|
+
flex: none;
|
|
1795
|
+
color: var(--dg-text-muted);
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
.kanuni-datagrid-menu-separator {
|
|
1799
|
+
height: 1px;
|
|
1800
|
+
margin: 4px 6px;
|
|
1801
|
+
background: var(--dg-border);
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
/* Submenus open toward the inline end, so they flip in RTL for free. */
|
|
1805
|
+
/*
|
|
1806
|
+
* Positioned in VIEWPORT coordinates by `GridContextMenu`, because it is a
|
|
1807
|
+
* sibling of the menu rather than a child — see the comment there. It inherits
|
|
1808
|
+
* the menu's box from the shared class; only the positioning differs.
|
|
1809
|
+
*/
|
|
1810
|
+
.kanuni-datagrid-submenu {
|
|
1811
|
+
position: fixed;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
/* ── Toolbar icon button + export menu ────────────────────── */
|
|
1815
|
+
.kanuni-datagrid-toolbar-icon-button {
|
|
1816
|
+
display: inline-flex;
|
|
1817
|
+
align-items: center;
|
|
1818
|
+
justify-content: center;
|
|
1819
|
+
width: 28px;
|
|
1820
|
+
height: 24px;
|
|
1821
|
+
padding: 0;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
/* Spinner shown in place of the icon while an export runs, so the button does
|
|
1825
|
+
not change size and the toolbar does not reflow mid-export. */
|
|
1826
|
+
.kanuni-datagrid-toolbar-busy {
|
|
1827
|
+
width: 12px;
|
|
1828
|
+
height: 12px;
|
|
1829
|
+
border: 2px solid var(--dg-border-strong);
|
|
1830
|
+
border-top-color: var(--dg-primary);
|
|
1831
|
+
border-radius: 50%;
|
|
1832
|
+
animation: kanuni-datagrid-spin 0.7s linear infinite;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
@keyframes kanuni-datagrid-spin {
|
|
1836
|
+
to {
|
|
1837
|
+
transform: rotate(360deg);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1842
|
+
.kanuni-datagrid-toolbar-busy {
|
|
1843
|
+
animation: none;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
/* The export menu reuses the popup shell but is a plain list of actions. */
|
|
1848
|
+
.kanuni-datagrid-export-menu {
|
|
1849
|
+
width: 190px;
|
|
1850
|
+
gap: 2px;
|
|
1851
|
+
padding: 4px;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
.kanuni-datagrid-chooser-empty {
|
|
1855
|
+
padding: 8px 4px;
|
|
1856
|
+
color: var(--dg-text-muted);
|
|
1857
|
+
font-style: italic;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
/* ── Column-aligned footer row (punch #18) ─────────────────
|
|
1861
|
+
Sticky to the BOTTOM of the same scroller the header sticks to the top of, so
|
|
1862
|
+
it scrolls horizontally with the columns and stays put vertically. */
|
|
1863
|
+
/* ── Pinned top/bottom rows (P2.3) ────────────────────────── */
|
|
1864
|
+
/* Sticky in the same stack as the header and the footer. `--dg-pinned-gap`
|
|
1865
|
+
counts the header-height rows between this block and its edge (header +
|
|
1866
|
+
filter row above, footer below), so the offset follows the THEME's header
|
|
1867
|
+
height instead of a number measured in JS at the previous theme's size. */
|
|
1868
|
+
.kanuni-datagrid-pinned-rows {
|
|
1869
|
+
position: sticky;
|
|
1870
|
+
/* Above the data rows, below the header — a pinned row must not cover the
|
|
1871
|
+
header when the grid is scrolled to the very top. */
|
|
1872
|
+
z-index: 2;
|
|
1873
|
+
display: flex;
|
|
1874
|
+
flex-direction: column;
|
|
1875
|
+
background: var(--dg-surface-header);
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
.kanuni-datagrid-pinned-rows--top {
|
|
1879
|
+
top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * var(--dg-pinned-gap, 1));
|
|
1880
|
+
border-bottom: var(--dg-header-border);
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
.kanuni-datagrid-pinned-rows--bottom {
|
|
1884
|
+
bottom: calc(var(--dg-header-height) * var(--dg-pinned-gap, 0));
|
|
1885
|
+
border-top: var(--dg-header-border);
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
/* The rows inside keep the grid's cell geometry but not its striping — these
|
|
1889
|
+
are not part of the alternating rhythm of the data. */
|
|
1890
|
+
.kanuni-datagrid-pinned-rows .kanuni-datagrid-row,
|
|
1891
|
+
.kanuni-datagrid-pinned-rows .kanuni-datagrid-cell {
|
|
1892
|
+
background: var(--dg-surface-header);
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
.kanuni-datagrid-footer-row {
|
|
1896
|
+
position: sticky;
|
|
1897
|
+
bottom: 0;
|
|
1898
|
+
z-index: 3;
|
|
1899
|
+
display: flex;
|
|
1900
|
+
height: var(--dg-header-height);
|
|
1901
|
+
background: var(--dg-footer-bg);
|
|
1902
|
+
border-top: var(--dg-header-border);
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
/* Totals at the TOP (P2.8) — same row, other edge. The border swaps sides so
|
|
1906
|
+
the rule always separates the totals from the DATA, not from the header. */
|
|
1907
|
+
.kanuni-datagrid-footer-row--top {
|
|
1908
|
+
top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * var(--dg-pinned-gap, 1));
|
|
1909
|
+
bottom: auto;
|
|
1910
|
+
border-top: none;
|
|
1911
|
+
border-bottom: var(--dg-header-border);
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
.kanuni-datagrid-footer-cell {
|
|
1915
|
+
display: flex;
|
|
1916
|
+
align-items: center;
|
|
1917
|
+
gap: 5px;
|
|
1918
|
+
flex: none;
|
|
1919
|
+
height: 100%;
|
|
1920
|
+
padding: 0 var(--dg-cell-padding-x);
|
|
1921
|
+
background: var(--dg-footer-bg);
|
|
1922
|
+
border-inline-end: var(--dg-header-cell-border);
|
|
1923
|
+
font-size: var(--dg-header-font-size);
|
|
1924
|
+
white-space: nowrap;
|
|
1925
|
+
overflow: hidden;
|
|
1926
|
+
user-select: none;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
.kanuni-datagrid-footer-cell--number {
|
|
1930
|
+
justify-content: flex-end;
|
|
1931
|
+
font-variant-numeric: tabular-nums;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
.kanuni-datagrid-footer-cell--pinned {
|
|
1935
|
+
position: sticky;
|
|
1936
|
+
/* Above the unpinned footer cells, matching the header's pinned layer. */
|
|
1937
|
+
z-index: 4;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
.kanuni-datagrid-footer-cell:hover {
|
|
1941
|
+
background: var(--dg-surface-hover);
|
|
1942
|
+
cursor: context-menu;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
.kanuni-datagrid-footer-value {
|
|
1946
|
+
overflow: hidden;
|
|
1947
|
+
text-overflow: ellipsis;
|
|
1948
|
+
font-weight: 600;
|
|
1949
|
+
color: var(--dg-text);
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
/* The affordance in an empty cell: barely there until hovered, so an untouched
|
|
1953
|
+
footer reads as blank rather than as a row of dots. */
|
|
1954
|
+
.kanuni-datagrid-footer-hint {
|
|
1955
|
+
margin-inline-start: auto;
|
|
1956
|
+
color: var(--dg-border-strong);
|
|
1957
|
+
opacity: 0;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
.kanuni-datagrid-footer-cell:hover .kanuni-datagrid-footer-hint {
|
|
1961
|
+
opacity: 1;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
/* ── Focused row (gap G6) ──────────────────────────────────
|
|
1965
|
+
Deliberately NOT the selection colour: focus is where the keyboard is,
|
|
1966
|
+
selection is what an action applies to, and a user who cannot tell them
|
|
1967
|
+
apart cannot tell what "delete selected" is about to do. An inset bar on the
|
|
1968
|
+
leading edge reads as "you are here" without competing with the row tint. */
|
|
1969
|
+
/* Mixed into the row's OWN colour, never into `transparent`.
|
|
1970
|
+
Pinned cells take `background: inherit`, so a see-through row tint made them
|
|
1971
|
+
see-through too — and a sticky cell you can see through shows the columns
|
|
1972
|
+
sliding along underneath it, which reads as two rows of text printed on top
|
|
1973
|
+
of each other. Both variants below are opaque, and keep the stripe. */
|
|
1974
|
+
.kanuni-datagrid-row--focused {
|
|
1975
|
+
background: color-mix(in srgb, var(--dg-primary) 7%, var(--dg-surface));
|
|
1976
|
+
box-shadow: inset 3px 0 0 0 var(--dg-primary);
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
.kanuni-datagrid-row--odd.kanuni-datagrid-row--focused {
|
|
1980
|
+
background: color-mix(in srgb, var(--dg-primary) 7%, var(--dg-surface-stripe));
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
/* Selected AND focused: the selection tint wins, the bar still marks the row. */
|
|
1984
|
+
.kanuni-datagrid-row--focused.kanuni-datagrid-row--selected {
|
|
1985
|
+
box-shadow: inset 3px 0 0 0 var(--dg-primary);
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
[dir='rtl'] .kanuni-datagrid-row--focused,
|
|
1989
|
+
[dir='rtl'] .kanuni-datagrid-row--focused.kanuni-datagrid-row--selected {
|
|
1990
|
+
box-shadow: inset -3px 0 0 0 var(--dg-primary);
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
/* ── Row drag & drop (G7) ─────────────────────────────────── */
|
|
1994
|
+
/*
|
|
1995
|
+
* A sticky GRIP overlaid at the row's inline start, not a lead column: a third
|
|
1996
|
+
* lead column would have to be threaded through `leadColumnWidth`, every
|
|
1997
|
+
* sticky offset and every aria-colindex across six components, and those
|
|
1998
|
+
* drifting apart is exactly what desynchronizes header and body.
|
|
1999
|
+
*/
|
|
2000
|
+
.kanuni-datagrid-row-grip {
|
|
2001
|
+
position: sticky;
|
|
2002
|
+
inset-inline-start: 0;
|
|
2003
|
+
z-index: 5;
|
|
2004
|
+
display: flex;
|
|
2005
|
+
align-items: center;
|
|
2006
|
+
justify-content: center;
|
|
2007
|
+
flex: none;
|
|
2008
|
+
width: 0;
|
|
2009
|
+
overflow: visible;
|
|
2010
|
+
color: var(--dg-text-muted, #94a3b8);
|
|
2011
|
+
cursor: grab;
|
|
2012
|
+
opacity: 0;
|
|
2013
|
+
transition: opacity 120ms ease;
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
/* Takes no horizontal space (width: 0) so column geometry is untouched; the
|
|
2017
|
+
glyph is painted into the row's own leading padding. */
|
|
2018
|
+
.kanuni-datagrid-row-grip > svg {
|
|
2019
|
+
position: absolute;
|
|
2020
|
+
/* Past the row-number column when one is showing (audit finding #4): the
|
|
2021
|
+
glyph used to sit at 1px unconditionally, which put it on top of the
|
|
2022
|
+
digits once P3.5 parked a number column at the inline start — visible at
|
|
2023
|
+
six-digit row counts, where the right-aligned number reaches the cell's
|
|
2024
|
+
left edge. The adapter sets the variable; without it nothing changes. */
|
|
2025
|
+
inset-inline-start: calc(var(--dg-grip-inset, 0px) + 1px);
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
.kanuni-datagrid-row:hover .kanuni-datagrid-row-grip {
|
|
2029
|
+
opacity: 1;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
.kanuni-datagrid-row-grip:active {
|
|
2033
|
+
cursor: grabbing;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
/* The drop line. An INSET box-shadow rather than a border: a border would add
|
|
2037
|
+
a pixel to the row and shift every row below it by one as the pointer moves,
|
|
2038
|
+
which reads as the whole grid twitching. */
|
|
2039
|
+
.kanuni-datagrid-row--drop-before {
|
|
2040
|
+
box-shadow: inset 0 2px 0 0 var(--dg-primary);
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
.kanuni-datagrid-row--drop-after {
|
|
2044
|
+
box-shadow: inset 0 -2px 0 0 var(--dg-primary);
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
/* ── Tree data (P1.4) ──────────────────────────────────────
|
|
2048
|
+
Indentation is inline on the cell (depth is data); everything visual lives
|
|
2049
|
+
here so all six themes and dark mode restyle the toggle for free. */
|
|
2050
|
+
.kanuni-datagrid-tree-cell {
|
|
2051
|
+
display: inline-flex;
|
|
2052
|
+
align-items: center;
|
|
2053
|
+
gap: 4px;
|
|
2054
|
+
min-width: 0;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
.kanuni-datagrid-tree-toggle {
|
|
2058
|
+
flex: none;
|
|
2059
|
+
width: 16px;
|
|
2060
|
+
height: 16px;
|
|
2061
|
+
padding: 0;
|
|
2062
|
+
border: 0;
|
|
2063
|
+
background: transparent;
|
|
2064
|
+
color: var(--dg-text-muted, #64748b);
|
|
2065
|
+
font-size: 10px;
|
|
2066
|
+
line-height: 16px;
|
|
2067
|
+
cursor: pointer;
|
|
2068
|
+
border-radius: var(--dg-radius-control, 4px);
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
.kanuni-datagrid-tree-toggle:hover {
|
|
2072
|
+
color: var(--dg-text, #0f172a);
|
|
2073
|
+
background: var(--dg-surface-hover, #f1f5f9);
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
.kanuni-datagrid-tree-toggle--leaf {
|
|
2077
|
+
cursor: default;
|
|
2078
|
+
pointer-events: none;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
/* Shown only because a descendant matched the filter — visibly the path,
|
|
2082
|
+
not a match. */
|
|
2083
|
+
.kanuni-datagrid-tree-cell--ancestor {
|
|
2084
|
+
color: var(--dg-text-muted, #64748b);
|
|
2085
|
+
}
|
|
2086
|
+
/* ── Cell flash (P1.7) ─────────────────────────────────────
|
|
2087
|
+
The "this number just changed" pulse. Driven by a class the adapter adds
|
|
2088
|
+
and removes; the colour is a token so all six themes and dark mode inherit
|
|
2089
|
+
it, and the animation is what fades — no JS frame loop. */
|
|
2090
|
+
.kanuni-datagrid-cell--flash {
|
|
2091
|
+
animation: kanuni-datagrid-flash var(--dg-flash-duration, 700ms) ease-out;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
@keyframes kanuni-datagrid-flash {
|
|
2095
|
+
from {
|
|
2096
|
+
background-color: var(--dg-flash-bg, rgba(3, 105, 161, 0.35));
|
|
2097
|
+
}
|
|
2098
|
+
to {
|
|
2099
|
+
background-color: transparent;
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
/* A flashing grid is exactly the kind of motion that triggers vestibular
|
|
2104
|
+
symptoms. Reduced-motion keeps the SIGNAL — the cell still marks itself —
|
|
2105
|
+
and drops the animation. */
|
|
2106
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2107
|
+
.kanuni-datagrid-cell--flash {
|
|
2108
|
+
animation: none;
|
|
2109
|
+
background-color: var(--dg-flash-bg, rgba(3, 105, 161, 0.35));
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
/* ── Print layout (gap analysis P3.4) ──────────────────────────
|
|
2113
|
+
Everything that makes the grid fast on screen is what ruins it on paper. The
|
|
2114
|
+
scroller is a clipped, fixed-height box holding one screenful; the print
|
|
2115
|
+
engine gets exactly that and paginates twenty rows out of a hundred thousand.
|
|
2116
|
+
|
|
2117
|
+
This class undoes the five things that clip the content, in the order they
|
|
2118
|
+
clip it: the root's own box, the scroll container, the canvas that carries
|
|
2119
|
+
the virtual height, the absolutely-positioned viewport, and the block
|
|
2120
|
+
transform that parks the rendered window at its scroll offset.
|
|
2121
|
+
|
|
2122
|
+
The class is set by the ADAPTER, not by a bare `@media print`, because the
|
|
2123
|
+
rows themselves have to be re-rendered — CSS cannot un-virtualize a list that
|
|
2124
|
+
does not exist in the DOM. */
|
|
2125
|
+
.kanuni-datagrid-print {
|
|
2126
|
+
height: auto !important;
|
|
2127
|
+
max-height: none !important;
|
|
2128
|
+
overflow: visible !important;
|
|
2129
|
+
/* Apps mount the grid as a flex child that fills the page — the shape a
|
|
2130
|
+
screen layout wants. Flex sizing beats `height: auto`, so the box kept the
|
|
2131
|
+
viewport's height and the rows below the fold printed on top of whatever
|
|
2132
|
+
came next. `flex: none` hands the sizing back to the content.
|
|
2133
|
+
A host that constrains the grid from further out still has to release it;
|
|
2134
|
+
the grid can only unclip itself. */
|
|
2135
|
+
flex: none !important;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
.kanuni-datagrid-print .kanuni-datagrid-scroller {
|
|
2139
|
+
height: auto !important;
|
|
2140
|
+
max-height: none !important;
|
|
2141
|
+
overflow: visible !important;
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
.kanuni-datagrid-print .kanuni-datagrid-canvas,
|
|
2145
|
+
.kanuni-datagrid-print .kanuni-datagrid-rows {
|
|
2146
|
+
/* The virtual extent, in a document that is no longer virtual.
|
|
2147
|
+
`.kanuni-datagrid-rows` is the one that matters and the one that was
|
|
2148
|
+
missed first: it carries the scroll canvas's height inline, and with the
|
|
2149
|
+
viewport put back in flow inside it, that inline height decided how much
|
|
2150
|
+
of the grid reached the page — five hundred rows printed as two hundred
|
|
2151
|
+
pixels of paper. The canvas is here for the same reason one level up. */
|
|
2152
|
+
height: auto !important;
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
.kanuni-datagrid-print .kanuni-datagrid-viewport {
|
|
2156
|
+
position: static;
|
|
2157
|
+
/* The block offset is a scroll position. On paper there is no scrolling, and
|
|
2158
|
+
leaving it in pushed the first row down by however far the user happened to
|
|
2159
|
+
have scrolled. */
|
|
2160
|
+
transform: none !important;
|
|
2161
|
+
will-change: auto;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
/* Sticky is meaningless without a scrolling ancestor, but it still creates the
|
|
2165
|
+
stacking and offset behaviour — a pinned column would print on top of the
|
|
2166
|
+
cell it sits beside. In flow, each cell keeps its own width and order. */
|
|
2167
|
+
.kanuni-datagrid-print .kanuni-datagrid-header,
|
|
2168
|
+
.kanuni-datagrid-print .kanuni-datagrid-band-row,
|
|
2169
|
+
.kanuni-datagrid-print .kanuni-datagrid-filter-row,
|
|
2170
|
+
.kanuni-datagrid-print .kanuni-datagrid-pinned-rows,
|
|
2171
|
+
.kanuni-datagrid-print .kanuni-datagrid-footer-row {
|
|
2172
|
+
position: static !important;
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
/* Rules that only make sense once the ink is on paper. */
|
|
2176
|
+
@media print {
|
|
2177
|
+
/* Release the page around the grid.
|
|
2178
|
+
A grid is normally mounted as a flex child filling a viewport-height shell,
|
|
2179
|
+
and every ancestor in that chain holds it at one screen — the grid can
|
|
2180
|
+
unclip itself and still print one page. The adapter marks the ROOT ELEMENT
|
|
2181
|
+
while print layout is on, because CSS cannot reach an ancestor from a class
|
|
2182
|
+
on the grid, and only for the duration.
|
|
2183
|
+
|
|
2184
|
+
The grid's OWN wrappers are named; the host's are not, so an app with its
|
|
2185
|
+
own fixed-height shell needs the same two lines on it. */
|
|
2186
|
+
.kanuni-datagrid-printing,
|
|
2187
|
+
.kanuni-datagrid-printing body,
|
|
2188
|
+
.kanuni-datagrid-printing .kanuni-datagrid-enterprise,
|
|
2189
|
+
.kanuni-datagrid-printing .kanuni-datagrid-main {
|
|
2190
|
+
height: auto !important;
|
|
2191
|
+
max-height: none !important;
|
|
2192
|
+
overflow: visible !important;
|
|
2193
|
+
flex: none !important;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
/* A row split across a page break is unreadable — the top half of the text on
|
|
2197
|
+
one sheet and the bottom half on the next. */
|
|
2198
|
+
.kanuni-datagrid-print .kanuni-datagrid-row,
|
|
2199
|
+
.kanuni-datagrid-print .kanuni-datagrid-group-row {
|
|
2200
|
+
break-inside: avoid;
|
|
2201
|
+
page-break-inside: avoid;
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
/* Chrome and Safari drop background colours by default, which turns the
|
|
2205
|
+
header, the group banners and the row striping into undifferentiated white.
|
|
2206
|
+
The structure IS the meaning here, so ask for them back. */
|
|
2207
|
+
.kanuni-datagrid-print {
|
|
2208
|
+
-webkit-print-color-adjust: exact;
|
|
2209
|
+
print-color-adjust: exact;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
/* Controls that print as furniture: nothing on paper can be clicked, and each
|
|
2213
|
+
one costs a column of width that the data needs. */
|
|
2214
|
+
.kanuni-datagrid-print .kanuni-datagrid-toolbar,
|
|
2215
|
+
.kanuni-datagrid-print .kanuni-datagrid-filter-panel,
|
|
2216
|
+
.kanuni-datagrid-print .kanuni-datagrid-pager,
|
|
2217
|
+
.kanuni-datagrid-print .kanuni-datagrid-status-bar,
|
|
2218
|
+
.kanuni-datagrid-print .kanuni-datagrid-sidebar,
|
|
2219
|
+
.kanuni-datagrid-print .kanuni-datagrid-resize-handle,
|
|
2220
|
+
.kanuni-datagrid-print .kanuni-datagrid-filter-button {
|
|
2221
|
+
display: none !important;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
/* ── Cell notes (gap analysis P3.3) ────────────────────────────
|
|
2226
|
+
A note is something a PERSON attached to this cell, and the whole value of
|
|
2227
|
+
the marker is that you can find those cells without hovering every one of
|
|
2228
|
+
them. Excel's corner triangle, for the same reason and in the same corner. */
|
|
2229
|
+
.kanuni-datagrid-cell--noted {
|
|
2230
|
+
position: relative;
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
.kanuni-datagrid-cell--noted::after {
|
|
2234
|
+
content: '';
|
|
2235
|
+
position: absolute;
|
|
2236
|
+
top: 0;
|
|
2237
|
+
/* Logical, not `right`: in an RTL grid the marker belongs in the corner the
|
|
2238
|
+
reader's eye ends at, which is the other one. */
|
|
2239
|
+
inset-inline-end: 0;
|
|
2240
|
+
width: 0;
|
|
2241
|
+
height: 0;
|
|
2242
|
+
/* A triangle from borders — no glyph, no font dependency, no extra element in
|
|
2243
|
+
a cell the row memo is built to keep cheap. */
|
|
2244
|
+
border-top: 6px solid var(--dg-note-marker);
|
|
2245
|
+
border-inline-start: 6px solid transparent;
|
|
2246
|
+
pointer-events: none;
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
.kanuni-datagrid-note-editor {
|
|
2250
|
+
display: flex;
|
|
2251
|
+
flex-direction: column;
|
|
2252
|
+
gap: 8px;
|
|
2253
|
+
padding: 8px;
|
|
2254
|
+
min-width: 260px;
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
.kanuni-datagrid-note-input {
|
|
2258
|
+
font: inherit;
|
|
2259
|
+
color: var(--dg-text);
|
|
2260
|
+
background: var(--dg-surface);
|
|
2261
|
+
border: 1px solid var(--dg-border);
|
|
2262
|
+
border-radius: var(--dg-radius);
|
|
2263
|
+
padding: 6px 8px;
|
|
2264
|
+
resize: vertical;
|
|
2265
|
+
min-height: 64px;
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
.kanuni-datagrid-note-input:focus-visible {
|
|
2269
|
+
outline: 2px solid var(--dg-focus-ring);
|
|
2270
|
+
outline-offset: -1px;
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
.kanuni-datagrid-note-actions {
|
|
2274
|
+
display: flex;
|
|
2275
|
+
justify-content: flex-end;
|
|
2276
|
+
gap: 6px;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
/* ── Cell spans (gap analysis P3.1) ────────────────────────────
|
|
2280
|
+
The block is an absolutely-positioned child of the cell, not a taller cell.
|
|
2281
|
+
A taller cell would change the height of its ROW, and a grid with an
|
|
2282
|
+
`autoHeight` column measures rows and feeds the result back to the engine —
|
|
2283
|
+
block inflates row, row moves offsets, offsets resize block, React ends it
|
|
2284
|
+
with "Maximum update depth exceeded". Out of the flow, the cell stays the
|
|
2285
|
+
height it always was. */
|
|
2286
|
+
.kanuni-datagrid-cell--span {
|
|
2287
|
+
position: relative;
|
|
2288
|
+
/* The box reaches past the cell's own bottom edge on purpose. */
|
|
2289
|
+
overflow: visible;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
.kanuni-datagrid-span-box {
|
|
2293
|
+
position: absolute;
|
|
2294
|
+
top: 0;
|
|
2295
|
+
inset-inline: 0;
|
|
2296
|
+
display: flex;
|
|
2297
|
+
/* Top-aligned, not centred: a two-row block and a fifty-row block should put
|
|
2298
|
+
the value in the same place, and a reader looks for it beside the first row
|
|
2299
|
+
of the group. */
|
|
2300
|
+
align-items: flex-start;
|
|
2301
|
+
/* Horizontal padding matches an ordinary cell so the value lines up with the
|
|
2302
|
+
column above and below. The top padding centres it within the block's FIRST
|
|
2303
|
+
row — the grid's line-height is 1.4, so half the leftover row height puts
|
|
2304
|
+
it exactly where an unmerged cell would sit. */
|
|
2305
|
+
padding: calc((var(--dg-row-height) - 1.4em) / 2) var(--dg-cell-padding-x);
|
|
2306
|
+
/* Its own background: the row underneath ends where the row does, and the
|
|
2307
|
+
rest of the block would show whatever is behind it. */
|
|
2308
|
+
background: var(--dg-surface);
|
|
2309
|
+
/* Above the STATIC center cells of later rows (which would otherwise paint
|
|
2310
|
+
their backgrounds over everything below the block's first row) — but below
|
|
2311
|
+
the pinned columns' 2. The audit caught 3 here: a center column's block
|
|
2312
|
+
painted OVER the left-pinned column during horizontal scroll, because the
|
|
2313
|
+
box competes in the viewport's stacking context while a box inside a
|
|
2314
|
+
pinned cell is scoped by its parent's own context. */
|
|
2315
|
+
z-index: 1;
|
|
2316
|
+
border-bottom: var(--dg-row-border);
|
|
2317
|
+
overflow: hidden;
|
|
2318
|
+
white-space: nowrap;
|
|
2319
|
+
text-overflow: ellipsis;
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
.kanuni-datagrid-cell--span-covered {
|
|
2323
|
+
/* No content, no rules. The block above owns this space. */
|
|
2324
|
+
border-bottom: none;
|
|
2325
|
+
background: transparent;
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
/* The FOCUSED covered cell lifts over the block and shows its own content —
|
|
2329
|
+
this is what makes an editor opened on it visible (audit finding #3), and
|
|
2330
|
+
arrowing through a merge gives per-row feedback instead of a void. */
|
|
2331
|
+
.kanuni-datagrid-cell--span-covered.kanuni-datagrid-cell--focused {
|
|
2332
|
+
position: relative;
|
|
2333
|
+
z-index: 2;
|
|
2334
|
+
background: var(--dg-surface);
|
|
2335
|
+
border-bottom: var(--dg-row-border);
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
/* A merged block sits over rows that stripe, get selected and get hovered.
|
|
2339
|
+
Their treatment must not show THROUGH the block — only beside it. */
|
|
2340
|
+
.kanuni-datagrid-row--odd .kanuni-datagrid-span-box,
|
|
2341
|
+
.kanuni-datagrid-row--selected .kanuni-datagrid-span-box {
|
|
2342
|
+
background: var(--dg-surface);
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
/* ── Filter popup resize handle (user request, 2026-08-14) ─────
|
|
2346
|
+
One corner, chosen by where there is room to grow: bottom-right normally,
|
|
2347
|
+
bottom-left when the popup is clamped against the viewport's right edge.
|
|
2348
|
+
Drawn as the familiar diagonal ridges so it reads as "drag me". */
|
|
2349
|
+
.kanuni-datagrid-popup-resize {
|
|
2350
|
+
position: absolute;
|
|
2351
|
+
bottom: 0;
|
|
2352
|
+
width: 14px;
|
|
2353
|
+
height: 14px;
|
|
2354
|
+
z-index: 2;
|
|
2355
|
+
background:
|
|
2356
|
+
repeating-linear-gradient(
|
|
2357
|
+
-45deg,
|
|
2358
|
+
transparent 0 3px,
|
|
2359
|
+
var(--dg-border-strong) 3px 4px
|
|
2360
|
+
);
|
|
2361
|
+
opacity: 0.7;
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
.kanuni-datagrid-popup-resize--right {
|
|
2365
|
+
right: 0;
|
|
2366
|
+
cursor: nwse-resize;
|
|
2367
|
+
clip-path: polygon(100% 0, 100% 100%, 0 100%);
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
.kanuni-datagrid-popup-resize--left {
|
|
2371
|
+
left: 0;
|
|
2372
|
+
cursor: nesw-resize;
|
|
2373
|
+
clip-path: polygon(0 0, 100% 100%, 0 100%);
|
|
2374
|
+
background:
|
|
2375
|
+
repeating-linear-gradient(
|
|
2376
|
+
45deg,
|
|
2377
|
+
transparent 0 3px,
|
|
2378
|
+
var(--dg-border-strong) 3px 4px
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2352
2381
|
|
|
2353
2382
|
/* ─────────────────────────────────────────────────────────────
|
|
2354
2383
|
KANUNILABS DataGrid — packaged themes
|