@kanunilabs/datagrid-react 1.6.0 → 1.8.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/dist/styles.css CHANGED
@@ -370,6 +370,41 @@
370
370
  white-space: nowrap;
371
371
  }
372
372
 
373
+ /* ── Header text wrapping (wrapHeaderText / autoHeaderHeight) ──
374
+ A wrapping caption breaks onto more lines instead of ending in an ellipsis.
375
+ Without `autoHeaderHeight` the header keeps its fixed height and the cell
376
+ clips what does not fit — the same box every other header has. */
377
+ .kanuni-datagrid-header-cell--wrap {
378
+ white-space: normal;
379
+ }
380
+
381
+ .kanuni-datagrid-header-cell--wrap .kanuni-datagrid-header-label {
382
+ white-space: normal;
383
+ overflow-wrap: break-word;
384
+ text-overflow: clip;
385
+ }
386
+
387
+ /* `autoHeaderHeight`: the header row grows to its tallest cell. `min-height`
388
+ keeps a header with nothing to wrap exactly the height it was. The cells
389
+ drop `height: 100%`, which cannot resolve against an `auto` parent, and
390
+ stretch to the row instead — so a short caption beside a tall one still
391
+ fills the full height, with its border and hover reaching the bottom.
392
+ The rows below that are sticky under the header read the measured height
393
+ from `--dg-header-actual` (written by the renderer, only in this mode). */
394
+ .kanuni-datagrid-root[data-auto-header-height] .kanuni-datagrid-header {
395
+ height: auto;
396
+ min-height: var(--dg-header-height);
397
+ }
398
+
399
+ .kanuni-datagrid-root[data-auto-header-height] .kanuni-datagrid-header-cell {
400
+ height: auto;
401
+ align-self: stretch;
402
+ }
403
+
404
+ .kanuni-datagrid-root[data-auto-header-height] .kanuni-datagrid-header-cell--wrap {
405
+ padding-block: 4px;
406
+ }
407
+
373
408
  .kanuni-datagrid-sort-indicator {
374
409
  flex: none;
375
410
  font-size: 10px;
@@ -595,7 +630,11 @@
595
630
  border-color: var(--dg-primary);
596
631
  }
597
632
 
633
+ /* Commands: an icon and a label side by side (toolbarIcons.ts). */
598
634
  .kanuni-datagrid-toolbar > button {
635
+ display: inline-flex;
636
+ align-items: center;
637
+ gap: 5px;
599
638
  padding: 3px 10px;
600
639
  border: 1px solid var(--dg-border-strong);
601
640
  border-radius: var(--dg-radius-control);
@@ -606,6 +645,10 @@
606
645
  cursor: pointer;
607
646
  }
608
647
 
648
+ .kanuni-datagrid-icon {
649
+ flex: none;
650
+ }
651
+
609
652
  .kanuni-datagrid-toolbar > button:hover:not(:disabled) {
610
653
  background: var(--dg-surface-hover);
611
654
  }
@@ -1003,9 +1046,8 @@
1003
1046
  color: var(--dg-text);
1004
1047
  }
1005
1048
 
1006
- /* The panel is sized in whole rows by the component (span x rowHeight); this
1007
- rule only styles it. Overriding the height here would desync it from the
1008
- filler slots the virtualizer reserved. */
1049
+ /* The panel's height is the component's (the height index reserves it);
1050
+ overriding it here would desync the rows below from their offsets. */
1009
1051
  .kanuni-datagrid-detail-row {
1010
1052
  background: var(--dg-surface);
1011
1053
  border-bottom: 1px solid var(--dg-border);
@@ -1015,7 +1057,9 @@
1015
1057
  position: sticky;
1016
1058
  inset-inline-start: 0;
1017
1059
  box-sizing: border-box;
1018
- width: 100%;
1060
+ /* The VIEWPORT's width, like the edit form below: the row is as wide as
1061
+ every column, and at 100% a panel under a wide grid ran off screen. */
1062
+ width: var(--dg-viewport-width, 100%);
1019
1063
  height: 100%;
1020
1064
  padding: 8px 12px;
1021
1065
  overflow: auto;
@@ -1044,6 +1088,120 @@
1044
1088
  border-inline-start: 3px solid var(--dg-primary);
1045
1089
  }
1046
1090
 
1091
+ /* ── Card view (narrow grids, `cardView`) ─────────────────── */
1092
+ /* The heights here are the ones the virtualizer places the cards by
1093
+ (datagrid-core presentation/cardView.ts: CARD_PADDING 21 = 10 + 10 + the
1094
+ 1px rule, CARD_TITLE_HEIGHT 22, CARD_LINE_HEIGHT 20). Change them together.
1095
+ The card is sticky to the scroller's start and as wide as the VIEWPORT, the
1096
+ in-grid edit form's pattern above: the row under it is as wide as every
1097
+ column, which is exactly the sideways scroll the cards are there to end. */
1098
+ .kanuni-datagrid-cards .kanuni-datagrid-header,
1099
+ .kanuni-datagrid-cards .kanuni-datagrid-band-row,
1100
+ .kanuni-datagrid-cards .kanuni-datagrid-filter-row {
1101
+ display: none;
1102
+ }
1103
+
1104
+ .kanuni-datagrid-cards .kanuni-datagrid-scroller {
1105
+ overflow-x: hidden;
1106
+ }
1107
+
1108
+ .kanuni-datagrid-card {
1109
+ position: sticky;
1110
+ inset-inline-start: 0;
1111
+ box-sizing: border-box;
1112
+ width: var(--dg-viewport-width, 100%);
1113
+ height: 100%;
1114
+ padding: 10px 14px;
1115
+ border-bottom: 1px solid var(--dg-border);
1116
+ overflow: hidden;
1117
+ color: var(--dg-text);
1118
+ font-size: 13px;
1119
+ }
1120
+
1121
+ .kanuni-datagrid-card-title {
1122
+ height: 22px;
1123
+ line-height: 22px;
1124
+ font-weight: 600;
1125
+ white-space: nowrap;
1126
+ overflow: hidden;
1127
+ text-overflow: ellipsis;
1128
+ }
1129
+
1130
+ .kanuni-datagrid-card-fields {
1131
+ margin: 0;
1132
+ }
1133
+
1134
+ .kanuni-datagrid-card-field {
1135
+ display: flex;
1136
+ gap: 12px;
1137
+ height: 20px;
1138
+ line-height: 20px;
1139
+ }
1140
+
1141
+ .kanuni-datagrid-card-field dt,
1142
+ .kanuni-datagrid-card-field dd {
1143
+ margin: 0;
1144
+ min-width: 0;
1145
+ white-space: nowrap;
1146
+ overflow: hidden;
1147
+ text-overflow: ellipsis;
1148
+ }
1149
+
1150
+ .kanuni-datagrid-card-field dt {
1151
+ flex: 0 0 38%;
1152
+ color: var(--dg-text-muted);
1153
+ }
1154
+
1155
+ .kanuni-datagrid-card-field dd {
1156
+ flex: 1 1 auto;
1157
+ }
1158
+
1159
+ .kanuni-datagrid-cardbar {
1160
+ display: flex;
1161
+ align-items: center;
1162
+ gap: 8px;
1163
+ padding: 6px 10px;
1164
+ border-bottom: 1px solid var(--dg-border);
1165
+ background: var(--dg-surface-header);
1166
+ color: var(--dg-text);
1167
+ font-size: 13px;
1168
+ }
1169
+
1170
+ .kanuni-datagrid-cardbar-label {
1171
+ display: flex;
1172
+ flex: 1 1 auto;
1173
+ align-items: center;
1174
+ gap: 8px;
1175
+ min-width: 0;
1176
+ }
1177
+
1178
+ .kanuni-datagrid-cardbar-select,
1179
+ .kanuni-datagrid-cardbar-dir {
1180
+ min-height: 32px;
1181
+ border: 1px solid var(--dg-border-strong);
1182
+ border-radius: var(--dg-radius-control, 4px);
1183
+ background: var(--dg-surface);
1184
+ color: var(--dg-text);
1185
+ font: inherit;
1186
+ }
1187
+
1188
+ .kanuni-datagrid-cardbar-select {
1189
+ flex: 1 1 auto;
1190
+ min-width: 0;
1191
+ padding: 0 6px;
1192
+ }
1193
+
1194
+ .kanuni-datagrid-cardbar-dir {
1195
+ width: 32px;
1196
+ cursor: pointer;
1197
+ }
1198
+
1199
+ .kanuni-datagrid-cardbar-select:focus-visible,
1200
+ .kanuni-datagrid-cardbar-dir:focus-visible {
1201
+ outline: 2px solid var(--dg-focus-ring);
1202
+ outline-offset: 1px;
1203
+ }
1204
+
1047
1205
  /* ── Summary bar ──────────────────────────────────────────── */
1048
1206
  .kanuni-datagrid-summary-bar {
1049
1207
  display: flex;
@@ -1427,7 +1585,9 @@
1427
1585
  variable here so density keeps working. Sticking at the header height alone
1428
1586
  parked this row inside the header on any banded grid, and the two drew over
1429
1587
  each other as soon as the user scrolled. */
1430
- top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height));
1588
+ /* `--dg-header-actual` is the header's measured height under
1589
+ `autoHeaderHeight`; unset otherwise, when the header is the token height. */
1590
+ top: calc(var(--dg-band-offset, 0px) + var(--dg-header-actual, var(--dg-header-height)));
1431
1591
  z-index: 3;
1432
1592
  display: flex;
1433
1593
  height: var(--dg-header-height);
@@ -1470,6 +1630,41 @@
1470
1630
  border-color: var(--dg-primary);
1471
1631
  }
1472
1632
 
1633
+ .kanuni-datagrid-filter-input:disabled {
1634
+ opacity: 0.5;
1635
+ }
1636
+
1637
+ /* A column with `filterOperators`: the operator menu before the box. Capped
1638
+ so a long label cannot squeeze the box out of a narrow column. */
1639
+ .kanuni-datagrid-filter-operator {
1640
+ flex: 0 1 auto;
1641
+ max-width: 45%;
1642
+ min-width: 0;
1643
+ height: var(--dg-filter-input-height);
1644
+ margin-inline-end: 2px;
1645
+ padding: 0 2px;
1646
+ border: 1px solid var(--dg-border-strong);
1647
+ border-radius: var(--dg-radius-control);
1648
+ background: var(--dg-surface);
1649
+ color: var(--dg-text);
1650
+ font: inherit;
1651
+ font-size: var(--dg-control-font-size);
1652
+ }
1653
+
1654
+ .kanuni-datagrid-filter-operator:focus {
1655
+ outline: none;
1656
+ border-color: var(--dg-primary);
1657
+ }
1658
+
1659
+ /* A column's own filter UI (`renderFilter`) in the filter row. */
1660
+ .kanuni-datagrid-filter-custom {
1661
+ display: flex;
1662
+ align-items: center;
1663
+ width: 100%;
1664
+ min-width: 0;
1665
+ height: 100%;
1666
+ }
1667
+
1473
1668
  /* ── Header filter popup (portaled — see GridPortal) ──────── */
1474
1669
  .kanuni-datagrid-portal {
1475
1670
  /* The portal host must not paint or capture anything itself. */
@@ -1509,6 +1704,17 @@
1509
1704
  }
1510
1705
 
1511
1706
  /* ── Search highlight (P2.4) ──────────────────────────────── */
1707
+ /* A highlighted cell's text and marks sit in ONE span: side by side in the
1708
+ flex cell, each part was a flex item and lost the spaces at its edges. The
1709
+ span carries the cell's single-line ellipsis itself, since a flex item does
1710
+ not inherit it. */
1711
+ .kanuni-datagrid-cell-text {
1712
+ min-width: 0;
1713
+ overflow: hidden;
1714
+ text-overflow: ellipsis;
1715
+ white-space: inherit;
1716
+ }
1717
+
1512
1718
  /* A <mark>, so the match is in the accessibility tree as emphasis rather than
1513
1719
  as a colour only. The browser default is a hard yellow that fights every
1514
1720
  theme; these follow the accent instead. */
@@ -1571,6 +1777,13 @@
1571
1777
  font-size: var(--dg-font-size);
1572
1778
  }
1573
1779
 
1780
+ /* The app's own content (`renderFilter`): it decides its size, not the list. */
1781
+ .kanuni-datagrid-filter-popup--custom {
1782
+ width: auto;
1783
+ min-width: 200px;
1784
+ max-height: none;
1785
+ }
1786
+
1574
1787
  .kanuni-datagrid-filter-search {
1575
1788
  width: 100%;
1576
1789
  height: var(--dg-control-height);
@@ -1958,6 +2171,14 @@
1958
2171
  padding: 4px;
1959
2172
  }
1960
2173
 
2174
+ /* The chooser says what it is: an untitled box of checkboxes read as nothing
2175
+ having opened (2026-10-14). */
2176
+ .kanuni-datagrid-chooser-title {
2177
+ padding: 2px 4px 0;
2178
+ font-weight: 600;
2179
+ color: var(--dg-text);
2180
+ }
2181
+
1961
2182
  .kanuni-datagrid-chooser-empty {
1962
2183
  padding: 8px 4px;
1963
2184
  color: var(--dg-text-muted);
@@ -1971,7 +2192,10 @@
1971
2192
  /* Sticky in the same stack as the header and the footer. `--dg-pinned-gap`
1972
2193
  counts the header-height rows between this block and its edge (header +
1973
2194
  filter row above, footer below), so the offset follows the THEME's header
1974
- height instead of a number measured in JS at the previous theme's size. */
2195
+ height instead of a number measured in JS at the previous theme's size.
2196
+ The one exception is the header itself under `autoHeaderHeight`: its
2197
+ height is the tallest wrapped caption, which only layout knows, so it
2198
+ counts as `--dg-header-actual` and the other rows of the stack as tokens. */
1975
2199
  .kanuni-datagrid-pinned-rows {
1976
2200
  position: sticky;
1977
2201
  /* Above the data rows, below the header — a pinned row must not cover the
@@ -1983,7 +2207,8 @@
1983
2207
  }
1984
2208
 
1985
2209
  .kanuni-datagrid-pinned-rows--top {
1986
- top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * var(--dg-pinned-gap, 1));
2210
+ top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * (var(--dg-pinned-gap, 1) - 1) +
2211
+ var(--dg-header-actual, var(--dg-header-height)));
1987
2212
  border-bottom: var(--dg-header-border);
1988
2213
  }
1989
2214
 
@@ -1999,8 +2224,8 @@
1999
2224
  .kanuni-datagrid-sticky-groups {
2000
2225
  position: sticky;
2001
2226
  top: calc(
2002
- var(--dg-band-offset, 0px) + var(--dg-header-height) * var(--dg-pinned-gap, 1) +
2003
- var(--dg-sticky-below, 0px)
2227
+ var(--dg-band-offset, 0px) + var(--dg-header-height) * (var(--dg-pinned-gap, 1) - 1) +
2228
+ var(--dg-header-actual, var(--dg-header-height)) + var(--dg-sticky-below, 0px)
2004
2229
  );
2005
2230
  z-index: 2;
2006
2231
  height: 0;
@@ -2044,7 +2269,8 @@
2044
2269
  /* Totals at the TOP (P2.8) — same row, other edge. The border swaps sides so
2045
2270
  the rule always separates the totals from the DATA, not from the header. */
2046
2271
  .kanuni-datagrid-footer-row--top {
2047
- top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * var(--dg-pinned-gap, 1));
2272
+ top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * (var(--dg-pinned-gap, 1) - 1) +
2273
+ var(--dg-header-actual, var(--dg-header-height)));
2048
2274
  bottom: auto;
2049
2275
  border-top: none;
2050
2276
  border-bottom: var(--dg-header-border);
@@ -2558,6 +2784,403 @@
2558
2784
  }
2559
2785
  }
2560
2786
 
2787
+ /* ══ Editing (moved from the Enterprise stylesheet, 2026-10-12) ═══════════
2788
+ Single-cell editing is Community now, in both renderers, so the editor,
2789
+ the date and lookup editors and the confirm/error dialog are styled here;
2790
+ the Enterprise stylesheet keeps only what its own modes add. */
2791
+
2792
+ /* ── In-cell editor ───────────────────────────────────────── */
2793
+ /* One line, not two boxes.
2794
+ The editor used to draw a 2px rounded border INSIDE a cell that already had
2795
+ a 2px focus outline — two concentric blue rectangles a couple of pixels
2796
+ apart, which is the busiest possible way to say "you are typing here". A
2797
+ single underline on the cell being edited says the same thing (the
2798
+ DevExtreme convention), and the outline is dropped for that one cell below. */
2799
+ .kanuni-datagrid-root .kanuni-datagrid-editor {
2800
+ width: 100%;
2801
+ min-width: 0;
2802
+ height: calc(var(--dg-row-height) - 2px);
2803
+ padding: 0 4px;
2804
+ border: none;
2805
+ border-bottom: 2px solid var(--dg-primary);
2806
+ border-radius: 0;
2807
+ background: transparent;
2808
+ color: var(--dg-text);
2809
+ font: inherit;
2810
+ font-size: var(--dg-font-size);
2811
+ margin: 0;
2812
+ }
2813
+
2814
+ /* Edge to edge of the CELL, not of its padding box.
2815
+ `width: 100%` resolves against the content box — the cell minus 2 ×
2816
+ --dg-cell-padding-x — and a negative margin only shifts a box, it does not
2817
+ widen it. The two together left the editor starting early and stopping short
2818
+ of the right edge, read as the field sliding out of its column and most
2819
+ visible on a pinned one, where the neighbouring cell does not move with it.
2820
+
2821
+ Applied to whatever is the cell's DIRECT CHILD, because that is the element
2822
+ whose `100%` means "the cell". Naming the input alone was wrong the moment an
2823
+ editor grew a wrapper — the date editor's box sat 8px inside its cell on the
2824
+ left and 9px on the right, while every plain editor beside it went edge to
2825
+ edge. Same reason the form's wrappers are excluded: they are not in a cell,
2826
+ and pulling them out by a cell's padding would misalign the form instead. */
2827
+ .kanuni-datagrid-root .kanuni-datagrid-cell > .kanuni-datagrid-editor,
2828
+ .kanuni-datagrid-root .kanuni-datagrid-cell > .kanuni-datagrid-date-editor {
2829
+ width: calc(100% + var(--dg-cell-padding-x) * 2);
2830
+ margin: 0 calc(var(--dg-cell-padding-x) * -1);
2831
+ }
2832
+
2833
+ /* In a WRAPPED cell the editor takes the LINE BOX, not the row height.
2834
+ A wrapping cell is as tall as its own text: one line of it is
2835
+ `padding-block x2 + --dg-wrap-line-height`, which is shorter than a standard
2836
+ row. Giving the editor the row height there made the cell taller than the
2837
+ text it replaced, so the row grew the moment an editor opened and every row
2838
+ below it hopped down — most obvious on a theme with tall rows, but the
2839
+ default did it too.
2840
+
2841
+ Matching the line box means the box the user types into occupies exactly the
2842
+ space the text did, and the row does not move at all. */
2843
+ .kanuni-datagrid-root .kanuni-datagrid-cell--wrap > .kanuni-datagrid-editor,
2844
+ .kanuni-datagrid-root .kanuni-datagrid-cell--wrap > .kanuni-datagrid-date-editor {
2845
+ height: var(--dg-wrap-line-height);
2846
+ }
2847
+
2848
+ .kanuni-datagrid-root .kanuni-datagrid-editor:focus {
2849
+ outline: none;
2850
+ }
2851
+
2852
+ .kanuni-datagrid-root .kanuni-datagrid-editor--invalid {
2853
+ border-bottom-color: var(--dg-danger);
2854
+ }
2855
+
2856
+ /* The cell holding an editor gives up its focus ring — the underline is the
2857
+ marker now, and the ring is the other half of the double rectangle.
2858
+ It also goes to the plain surface colour: the row underneath may be striped,
2859
+ selected, focused or in-range, and a blue underline drawn over a blue-tinted
2860
+ cell is the one thing it must not compete with. Listed after the row-state
2861
+ rules and with two classes, so it outranks all of them. */
2862
+ .kanuni-datagrid-root .kanuni-datagrid-cell--editing,
2863
+ .kanuni-datagrid-root .kanuni-datagrid-cell--editing.kanuni-datagrid-cell--focused,
2864
+ .kanuni-datagrid-root .kanuni-datagrid-cell--editing.kanuni-datagrid-cell--in-range,
2865
+ .kanuni-datagrid-root .kanuni-datagrid-cell--editing.kanuni-datagrid-cell--pinned {
2866
+ outline: none;
2867
+ background: var(--dg-surface);
2868
+ }
2869
+
2870
+ .kanuni-datagrid-root input.kanuni-datagrid-editor[type='checkbox'] {
2871
+ width: auto;
2872
+ height: auto;
2873
+ margin: 0;
2874
+ border: none;
2875
+ accent-color: var(--dg-primary);
2876
+ }
2877
+
2878
+ /* Validation message floats out of the cell so it is not clipped by overflow.
2879
+ That only works if the editing cell LETS it out and its row sits above the
2880
+ next one: cells are `overflow: hidden` and every row paints over the one
2881
+ before it, so until 2026-10-01 the message rendered into the DOM, positioned
2882
+ correctly, and was never seen — the user got a red underline and no reason
2883
+ (found shooting the order-editing demo; measured in Chrome, the message box
2884
+ at the right place and fully covered). With these two rules it shows;
2885
+ whether either alone would do was not measured. */
2886
+ .kanuni-datagrid-root .kanuni-datagrid-cell--editing {
2887
+ overflow: visible;
2888
+ }
2889
+
2890
+ .kanuni-datagrid-root .kanuni-datagrid-row:has(.kanuni-datagrid-cell--editing) {
2891
+ position: relative;
2892
+ z-index: 4;
2893
+ overflow: visible;
2894
+ }
2895
+
2896
+ .kanuni-datagrid-root .kanuni-datagrid-editor-error {
2897
+ position: absolute;
2898
+ z-index: 6;
2899
+ transform: translateY(calc(var(--dg-row-height) - 2px));
2900
+ max-width: 240px;
2901
+ padding: 2px 6px;
2902
+ border-radius: var(--dg-radius-control);
2903
+ background: var(--dg-danger);
2904
+ color: var(--dg-on-danger);
2905
+ font-size: 11px;
2906
+ white-space: nowrap;
2907
+ pointer-events: none;
2908
+ }
2909
+
2910
+ /* ── Lookup combo box ─────────────────────────────────────── */
2911
+ /* Rendered in a portal on <body>, so it is NOT under .kanuni-datagrid-root and
2912
+ cannot inherit the theme tokens through the cascade. It reads them from
2913
+ :root, which is where the light/dark blocks define them. */
2914
+ /* ── Date editor ──────────────────────────────────────────── */
2915
+ /* The box and its calendar button share the cell: the input takes the space
2916
+ that is left, so the button never overlaps the text however narrow the
2917
+ column is. */
2918
+ .kanuni-datagrid-date-editor {
2919
+ display: flex;
2920
+ align-items: center;
2921
+ gap: 2px;
2922
+ width: 100%;
2923
+ min-width: 0;
2924
+ }
2925
+
2926
+ /* Inside a box that already owns the cell — compensating twice would push the
2927
+ field back out by another padding on each side. */
2928
+ .kanuni-datagrid-date-editor > .kanuni-datagrid-editor {
2929
+ width: auto;
2930
+ margin: 0;
2931
+ }
2932
+
2933
+ .kanuni-datagrid-date-editor > .kanuni-datagrid-editor {
2934
+ flex: 1;
2935
+ min-width: 0;
2936
+ }
2937
+
2938
+ .kanuni-datagrid-date-button {
2939
+ flex: none;
2940
+ display: inline-flex;
2941
+ align-items: center;
2942
+ justify-content: center;
2943
+ width: 20px;
2944
+ height: 20px;
2945
+ padding: 0;
2946
+ border: 0;
2947
+ border-radius: 3px;
2948
+ background: transparent;
2949
+ color: var(--dg-text-muted, #64748b);
2950
+ cursor: pointer;
2951
+ }
2952
+
2953
+ .kanuni-datagrid-date-button:hover:not(:disabled) {
2954
+ background: var(--dg-surface-hover, #f1f5f9);
2955
+ color: var(--dg-primary, #2563eb);
2956
+ }
2957
+
2958
+ .kanuni-datagrid-date-button:disabled {
2959
+ opacity: 0.5;
2960
+ cursor: default;
2961
+ }
2962
+
2963
+ .kanuni-datagrid-date-popup {
2964
+ position: fixed;
2965
+ z-index: 1200;
2966
+ padding: 8px;
2967
+ border: 1px solid var(--dg-border-strong, #cbd5e1);
2968
+ border-radius: var(--dg-radius);
2969
+ background: var(--dg-surface, #fff);
2970
+ box-shadow: var(--dg-shadow-popup-sm);
2971
+ color: var(--dg-text, #0f172a);
2972
+ font-size: var(--dg-font-size);
2973
+ }
2974
+
2975
+ .kanuni-datagrid-date-popup-head {
2976
+ display: flex;
2977
+ align-items: center;
2978
+ justify-content: space-between;
2979
+ gap: 8px;
2980
+ margin-bottom: 6px;
2981
+ font-weight: 600;
2982
+ }
2983
+
2984
+ .kanuni-datagrid-date-popup-head button {
2985
+ width: 24px;
2986
+ height: 24px;
2987
+ border: 0;
2988
+ border-radius: var(--dg-radius-control);
2989
+ background: transparent;
2990
+ color: inherit;
2991
+ font-size: 16px;
2992
+ line-height: 1;
2993
+ cursor: pointer;
2994
+ }
2995
+
2996
+ .kanuni-datagrid-date-popup-head button:hover {
2997
+ background: var(--dg-surface-hover, #f1f5f9);
2998
+ }
2999
+
3000
+ /* Seven fixed columns: a calendar that reflows is not a calendar. */
3001
+ .kanuni-datagrid-date-popup-grid {
3002
+ display: grid;
3003
+ grid-template-columns: repeat(7, 28px);
3004
+ gap: 1px;
3005
+ }
3006
+
3007
+ .kanuni-datagrid-date-weekday {
3008
+ display: flex;
3009
+ align-items: center;
3010
+ justify-content: center;
3011
+ height: 22px;
3012
+ color: var(--dg-text-muted, #64748b);
3013
+ font-size: 11px;
3014
+ font-weight: 600;
3015
+ }
3016
+
3017
+ .kanuni-datagrid-date-day {
3018
+ height: 26px;
3019
+ border: 0;
3020
+ border-radius: var(--dg-radius-control);
3021
+ background: transparent;
3022
+ color: inherit;
3023
+ font: inherit;
3024
+ cursor: pointer;
3025
+ }
3026
+
3027
+ .kanuni-datagrid-date-day:hover {
3028
+ background: var(--dg-surface-hover, #f1f5f9);
3029
+ }
3030
+
3031
+ /* Days spilling in from the neighbouring months stay clickable — paging to
3032
+ pick the 1st of next month is a step nobody should have to take — but they
3033
+ read as context rather than as part of this month. */
3034
+ .kanuni-datagrid-date-day--outside {
3035
+ opacity: 0.4;
3036
+ }
3037
+
3038
+ .kanuni-datagrid-date-day--selected {
3039
+ background: var(--dg-primary);
3040
+ color: var(--dg-on-primary);
3041
+ font-weight: 600;
3042
+ }
3043
+
3044
+ .kanuni-datagrid-lookup-popup {
3045
+ position: fixed;
3046
+ z-index: 1200;
3047
+ overflow-y: auto;
3048
+ border: 1px solid var(--dg-border-strong, #cbd5e1);
3049
+ border-radius: var(--dg-radius-control);
3050
+ background: var(--dg-surface, #fff);
3051
+ box-shadow: var(--dg-shadow-popup-sm);
3052
+ color: var(--dg-text, #0f172a);
3053
+ font-size: var(--dg-font-size);
3054
+ }
3055
+
3056
+ .kanuni-datagrid-lookup-item {
3057
+ padding: 4px 8px;
3058
+ line-height: 18px;
3059
+ white-space: nowrap;
3060
+ overflow: hidden;
3061
+ text-overflow: ellipsis;
3062
+ cursor: pointer;
3063
+ }
3064
+
3065
+ .kanuni-datagrid-lookup-item--active {
3066
+ background: var(--dg-primary);
3067
+ color: var(--dg-on-primary);
3068
+ }
3069
+
3070
+ .kanuni-datagrid-lookup-empty {
3071
+ padding: 4px 8px;
3072
+ color: var(--dg-text-muted, #64748b);
3073
+ font-style: italic;
3074
+ }
3075
+
3076
+ /* ── Confirmation / error dialog ──────────────────────────── */
3077
+ .kanuni-datagrid-dialog-root {
3078
+ position: fixed;
3079
+ inset: 0;
3080
+ z-index: 1400;
3081
+ display: flex;
3082
+ align-items: center;
3083
+ justify-content: center;
3084
+ }
3085
+
3086
+ .kanuni-datagrid-dialog-overlay {
3087
+ position: absolute;
3088
+ inset: 0;
3089
+ background: var(--dg-overlay);
3090
+ }
3091
+
3092
+ .kanuni-datagrid-dialog {
3093
+ position: relative;
3094
+ width: min(420px, 92vw);
3095
+ padding: 18px 20px 14px;
3096
+ border: 1px solid var(--dg-border-strong, #cbd5e1);
3097
+ border-radius: calc(var(--dg-radius) + 2px);
3098
+ background: var(--dg-surface, #fff);
3099
+ color: var(--dg-text, #0f172a);
3100
+ box-shadow: var(--dg-shadow-dialog);
3101
+ font-size: var(--dg-font-size);
3102
+ }
3103
+
3104
+ .kanuni-datagrid-dialog-title {
3105
+ margin-bottom: 8px;
3106
+ font-size: 15px;
3107
+ font-weight: 600;
3108
+ }
3109
+
3110
+ .kanuni-datagrid-dialog-message {
3111
+ margin-bottom: 18px;
3112
+ line-height: 1.5;
3113
+ color: var(--dg-text, #0f172a);
3114
+ }
3115
+
3116
+ .kanuni-datagrid-dialog-actions {
3117
+ display: flex;
3118
+ justify-content: flex-end;
3119
+ gap: 8px;
3120
+ }
3121
+
3122
+ .kanuni-datagrid-dialog-actions button {
3123
+ padding: 5px 14px;
3124
+ border: 1px solid var(--dg-border-strong, #cbd5e1);
3125
+ border-radius: var(--dg-radius-control);
3126
+ background: var(--dg-surface, #fff);
3127
+ color: inherit;
3128
+ font: inherit;
3129
+ font-size: var(--dg-font-size);
3130
+ cursor: pointer;
3131
+ }
3132
+
3133
+ /* SCOPED TO THE ACTIONS ROW ON PURPOSE, and this is a fix rather than a style.
3134
+ `.kanuni-datagrid-dialog-actions button` above is (0,1,1) — a class and a
3135
+ type — while a bare `.kanuni-datagrid-dialog-primary` is (0,1,0). Specificity
3136
+ beats source order, so the generic rule won and the confirm button had never
3137
+ actually been painted in the accent: it rendered as a plain white button, and
3138
+ the only reason nobody read it as broken is that its label inherited the
3139
+ dialog's text colour instead of the white it was assigned. The sibling
3140
+ primaries elsewhere in this sheet (filter builder, form save) sidestep the
3141
+ same collision with `!important`; matching the selector shape is the same
3142
+ result without the escape hatch. */
3143
+ .kanuni-datagrid-dialog-actions button.kanuni-datagrid-dialog-primary {
3144
+ border-color: var(--dg-primary);
3145
+ background: var(--dg-primary);
3146
+ color: var(--dg-on-primary);
3147
+ }
3148
+
3149
+ /* Composed WITH `--primary`, so it has to restate the foreground: the primary
3150
+ rule painted it `--dg-on-primary`, which is the right colour for the accent
3151
+ and the wrong one for a red fill (near-black on red in the gold themes). */
3152
+ .kanuni-datagrid-dialog-actions button.kanuni-datagrid-dialog-primary--danger {
3153
+ border-color: var(--dg-danger);
3154
+ background: var(--dg-danger);
3155
+ color: var(--dg-on-danger);
3156
+ }
3157
+
3158
+ /* ── Forced colours (Windows high contrast) ─────────────────────
3159
+ The browser replaces our colours with the user's system palette and drops
3160
+ box-shadows and background images, so anything drawn only by a fill or a
3161
+ shadow disappears — the focused cell, a selected row, the editor's
3162
+ underline. Restated with system colours, which survive (accessibility
3163
+ audit, 2026-10-12: there was no forced-colors rule anywhere). */
3164
+ @media (forced-colors: active) {
3165
+ .kanuni-datagrid-root .kanuni-datagrid-cell--focused,
3166
+ .kanuni-datagrid-root .kanuni-datagrid-group-row--focused {
3167
+ outline: 2px solid Highlight;
3168
+ outline-offset: -2px;
3169
+ }
3170
+ .kanuni-datagrid-root .kanuni-datagrid-row--selected,
3171
+ .kanuni-datagrid-root .kanuni-datagrid-row--selected .kanuni-datagrid-cell {
3172
+ forced-color-adjust: none;
3173
+ background: Highlight;
3174
+ color: HighlightText;
3175
+ }
3176
+ .kanuni-datagrid-root .kanuni-datagrid-editor {
3177
+ border: 1px solid CanvasText;
3178
+ }
3179
+ .kanuni-datagrid-root :focus-visible {
3180
+ outline: 2px solid Highlight;
3181
+ }
3182
+ }
3183
+
2561
3184
  /* ─────────────────────────────────────────────────────────────
2562
3185
  KANUNILABS DataGrid — packaged themes
2563
3186
  ─────────────────────────────────────────────────────────────