@kanunilabs/datagrid-react 1.6.0 → 1.7.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/index.cjs +3 -2
- package/dist/index.d.cts +431 -4
- package/dist/index.d.ts +431 -4
- package/dist/index.js +3 -2
- package/dist/styles.css +601 -6
- package/package.json +3 -2
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;
|
|
@@ -1044,6 +1079,120 @@
|
|
|
1044
1079
|
border-inline-start: 3px solid var(--dg-primary);
|
|
1045
1080
|
}
|
|
1046
1081
|
|
|
1082
|
+
/* ── Card view (narrow grids, `cardView`) ─────────────────── */
|
|
1083
|
+
/* The heights here are the ones the virtualizer places the cards by
|
|
1084
|
+
(datagrid-core presentation/cardView.ts: CARD_PADDING 21 = 10 + 10 + the
|
|
1085
|
+
1px rule, CARD_TITLE_HEIGHT 22, CARD_LINE_HEIGHT 20). Change them together.
|
|
1086
|
+
The card is sticky to the scroller's start and as wide as the VIEWPORT, the
|
|
1087
|
+
in-grid edit form's pattern above: the row under it is as wide as every
|
|
1088
|
+
column, which is exactly the sideways scroll the cards are there to end. */
|
|
1089
|
+
.kanuni-datagrid-cards .kanuni-datagrid-header,
|
|
1090
|
+
.kanuni-datagrid-cards .kanuni-datagrid-band-row,
|
|
1091
|
+
.kanuni-datagrid-cards .kanuni-datagrid-filter-row {
|
|
1092
|
+
display: none;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
.kanuni-datagrid-cards .kanuni-datagrid-scroller {
|
|
1096
|
+
overflow-x: hidden;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
.kanuni-datagrid-card {
|
|
1100
|
+
position: sticky;
|
|
1101
|
+
inset-inline-start: 0;
|
|
1102
|
+
box-sizing: border-box;
|
|
1103
|
+
width: var(--dg-viewport-width, 100%);
|
|
1104
|
+
height: 100%;
|
|
1105
|
+
padding: 10px 14px;
|
|
1106
|
+
border-bottom: 1px solid var(--dg-border);
|
|
1107
|
+
overflow: hidden;
|
|
1108
|
+
color: var(--dg-text);
|
|
1109
|
+
font-size: 13px;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
.kanuni-datagrid-card-title {
|
|
1113
|
+
height: 22px;
|
|
1114
|
+
line-height: 22px;
|
|
1115
|
+
font-weight: 600;
|
|
1116
|
+
white-space: nowrap;
|
|
1117
|
+
overflow: hidden;
|
|
1118
|
+
text-overflow: ellipsis;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
.kanuni-datagrid-card-fields {
|
|
1122
|
+
margin: 0;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
.kanuni-datagrid-card-field {
|
|
1126
|
+
display: flex;
|
|
1127
|
+
gap: 12px;
|
|
1128
|
+
height: 20px;
|
|
1129
|
+
line-height: 20px;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
.kanuni-datagrid-card-field dt,
|
|
1133
|
+
.kanuni-datagrid-card-field dd {
|
|
1134
|
+
margin: 0;
|
|
1135
|
+
min-width: 0;
|
|
1136
|
+
white-space: nowrap;
|
|
1137
|
+
overflow: hidden;
|
|
1138
|
+
text-overflow: ellipsis;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
.kanuni-datagrid-card-field dt {
|
|
1142
|
+
flex: 0 0 38%;
|
|
1143
|
+
color: var(--dg-text-muted);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
.kanuni-datagrid-card-field dd {
|
|
1147
|
+
flex: 1 1 auto;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
.kanuni-datagrid-cardbar {
|
|
1151
|
+
display: flex;
|
|
1152
|
+
align-items: center;
|
|
1153
|
+
gap: 8px;
|
|
1154
|
+
padding: 6px 10px;
|
|
1155
|
+
border-bottom: 1px solid var(--dg-border);
|
|
1156
|
+
background: var(--dg-surface-header);
|
|
1157
|
+
color: var(--dg-text);
|
|
1158
|
+
font-size: 13px;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
.kanuni-datagrid-cardbar-label {
|
|
1162
|
+
display: flex;
|
|
1163
|
+
flex: 1 1 auto;
|
|
1164
|
+
align-items: center;
|
|
1165
|
+
gap: 8px;
|
|
1166
|
+
min-width: 0;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
.kanuni-datagrid-cardbar-select,
|
|
1170
|
+
.kanuni-datagrid-cardbar-dir {
|
|
1171
|
+
min-height: 32px;
|
|
1172
|
+
border: 1px solid var(--dg-border-strong);
|
|
1173
|
+
border-radius: var(--dg-radius-control, 4px);
|
|
1174
|
+
background: var(--dg-surface);
|
|
1175
|
+
color: var(--dg-text);
|
|
1176
|
+
font: inherit;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
.kanuni-datagrid-cardbar-select {
|
|
1180
|
+
flex: 1 1 auto;
|
|
1181
|
+
min-width: 0;
|
|
1182
|
+
padding: 0 6px;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
.kanuni-datagrid-cardbar-dir {
|
|
1186
|
+
width: 32px;
|
|
1187
|
+
cursor: pointer;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
.kanuni-datagrid-cardbar-select:focus-visible,
|
|
1191
|
+
.kanuni-datagrid-cardbar-dir:focus-visible {
|
|
1192
|
+
outline: 2px solid var(--dg-focus-ring);
|
|
1193
|
+
outline-offset: 1px;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1047
1196
|
/* ── Summary bar ──────────────────────────────────────────── */
|
|
1048
1197
|
.kanuni-datagrid-summary-bar {
|
|
1049
1198
|
display: flex;
|
|
@@ -1427,7 +1576,9 @@
|
|
|
1427
1576
|
variable here so density keeps working. Sticking at the header height alone
|
|
1428
1577
|
parked this row inside the header on any banded grid, and the two drew over
|
|
1429
1578
|
each other as soon as the user scrolled. */
|
|
1430
|
-
|
|
1579
|
+
/* `--dg-header-actual` is the header's measured height under
|
|
1580
|
+
`autoHeaderHeight`; unset otherwise, when the header is the token height. */
|
|
1581
|
+
top: calc(var(--dg-band-offset, 0px) + var(--dg-header-actual, var(--dg-header-height)));
|
|
1431
1582
|
z-index: 3;
|
|
1432
1583
|
display: flex;
|
|
1433
1584
|
height: var(--dg-header-height);
|
|
@@ -1470,6 +1621,41 @@
|
|
|
1470
1621
|
border-color: var(--dg-primary);
|
|
1471
1622
|
}
|
|
1472
1623
|
|
|
1624
|
+
.kanuni-datagrid-filter-input:disabled {
|
|
1625
|
+
opacity: 0.5;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/* A column with `filterOperators`: the operator menu before the box. Capped
|
|
1629
|
+
so a long label cannot squeeze the box out of a narrow column. */
|
|
1630
|
+
.kanuni-datagrid-filter-operator {
|
|
1631
|
+
flex: 0 1 auto;
|
|
1632
|
+
max-width: 45%;
|
|
1633
|
+
min-width: 0;
|
|
1634
|
+
height: var(--dg-filter-input-height);
|
|
1635
|
+
margin-inline-end: 2px;
|
|
1636
|
+
padding: 0 2px;
|
|
1637
|
+
border: 1px solid var(--dg-border-strong);
|
|
1638
|
+
border-radius: var(--dg-radius-control);
|
|
1639
|
+
background: var(--dg-surface);
|
|
1640
|
+
color: var(--dg-text);
|
|
1641
|
+
font: inherit;
|
|
1642
|
+
font-size: var(--dg-control-font-size);
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
.kanuni-datagrid-filter-operator:focus {
|
|
1646
|
+
outline: none;
|
|
1647
|
+
border-color: var(--dg-primary);
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/* A column's own filter UI (`renderFilter`) in the filter row. */
|
|
1651
|
+
.kanuni-datagrid-filter-custom {
|
|
1652
|
+
display: flex;
|
|
1653
|
+
align-items: center;
|
|
1654
|
+
width: 100%;
|
|
1655
|
+
min-width: 0;
|
|
1656
|
+
height: 100%;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1473
1659
|
/* ── Header filter popup (portaled — see GridPortal) ──────── */
|
|
1474
1660
|
.kanuni-datagrid-portal {
|
|
1475
1661
|
/* The portal host must not paint or capture anything itself. */
|
|
@@ -1571,6 +1757,13 @@
|
|
|
1571
1757
|
font-size: var(--dg-font-size);
|
|
1572
1758
|
}
|
|
1573
1759
|
|
|
1760
|
+
/* The app's own content (`renderFilter`): it decides its size, not the list. */
|
|
1761
|
+
.kanuni-datagrid-filter-popup--custom {
|
|
1762
|
+
width: auto;
|
|
1763
|
+
min-width: 200px;
|
|
1764
|
+
max-height: none;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1574
1767
|
.kanuni-datagrid-filter-search {
|
|
1575
1768
|
width: 100%;
|
|
1576
1769
|
height: var(--dg-control-height);
|
|
@@ -1971,7 +2164,10 @@
|
|
|
1971
2164
|
/* Sticky in the same stack as the header and the footer. `--dg-pinned-gap`
|
|
1972
2165
|
counts the header-height rows between this block and its edge (header +
|
|
1973
2166
|
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.
|
|
2167
|
+
height instead of a number measured in JS at the previous theme's size.
|
|
2168
|
+
The one exception is the header itself under `autoHeaderHeight`: its
|
|
2169
|
+
height is the tallest wrapped caption, which only layout knows, so it
|
|
2170
|
+
counts as `--dg-header-actual` and the other rows of the stack as tokens. */
|
|
1975
2171
|
.kanuni-datagrid-pinned-rows {
|
|
1976
2172
|
position: sticky;
|
|
1977
2173
|
/* Above the data rows, below the header — a pinned row must not cover the
|
|
@@ -1983,7 +2179,8 @@
|
|
|
1983
2179
|
}
|
|
1984
2180
|
|
|
1985
2181
|
.kanuni-datagrid-pinned-rows--top {
|
|
1986
|
-
top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * var(--dg-pinned-gap, 1))
|
|
2182
|
+
top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * (var(--dg-pinned-gap, 1) - 1) +
|
|
2183
|
+
var(--dg-header-actual, var(--dg-header-height)));
|
|
1987
2184
|
border-bottom: var(--dg-header-border);
|
|
1988
2185
|
}
|
|
1989
2186
|
|
|
@@ -1999,8 +2196,8 @@
|
|
|
1999
2196
|
.kanuni-datagrid-sticky-groups {
|
|
2000
2197
|
position: sticky;
|
|
2001
2198
|
top: calc(
|
|
2002
|
-
var(--dg-band-offset, 0px) + var(--dg-header-height) * var(--dg-pinned-gap, 1) +
|
|
2003
|
-
var(--dg-sticky-below, 0px)
|
|
2199
|
+
var(--dg-band-offset, 0px) + var(--dg-header-height) * (var(--dg-pinned-gap, 1) - 1) +
|
|
2200
|
+
var(--dg-header-actual, var(--dg-header-height)) + var(--dg-sticky-below, 0px)
|
|
2004
2201
|
);
|
|
2005
2202
|
z-index: 2;
|
|
2006
2203
|
height: 0;
|
|
@@ -2044,7 +2241,8 @@
|
|
|
2044
2241
|
/* Totals at the TOP (P2.8) — same row, other edge. The border swaps sides so
|
|
2045
2242
|
the rule always separates the totals from the DATA, not from the header. */
|
|
2046
2243
|
.kanuni-datagrid-footer-row--top {
|
|
2047
|
-
top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * var(--dg-pinned-gap, 1))
|
|
2244
|
+
top: calc(var(--dg-band-offset, 0px) + var(--dg-header-height) * (var(--dg-pinned-gap, 1) - 1) +
|
|
2245
|
+
var(--dg-header-actual, var(--dg-header-height)));
|
|
2048
2246
|
bottom: auto;
|
|
2049
2247
|
border-top: none;
|
|
2050
2248
|
border-bottom: var(--dg-header-border);
|
|
@@ -2558,6 +2756,403 @@
|
|
|
2558
2756
|
}
|
|
2559
2757
|
}
|
|
2560
2758
|
|
|
2759
|
+
/* ══ Editing (moved from the Enterprise stylesheet, 2026-10-12) ═══════════
|
|
2760
|
+
Single-cell editing is Community now, in both renderers, so the editor,
|
|
2761
|
+
the date and lookup editors and the confirm/error dialog are styled here;
|
|
2762
|
+
the Enterprise stylesheet keeps only what its own modes add. */
|
|
2763
|
+
|
|
2764
|
+
/* ── In-cell editor ───────────────────────────────────────── */
|
|
2765
|
+
/* One line, not two boxes.
|
|
2766
|
+
The editor used to draw a 2px rounded border INSIDE a cell that already had
|
|
2767
|
+
a 2px focus outline — two concentric blue rectangles a couple of pixels
|
|
2768
|
+
apart, which is the busiest possible way to say "you are typing here". A
|
|
2769
|
+
single underline on the cell being edited says the same thing (the
|
|
2770
|
+
DevExtreme convention), and the outline is dropped for that one cell below. */
|
|
2771
|
+
.kanuni-datagrid-root .kanuni-datagrid-editor {
|
|
2772
|
+
width: 100%;
|
|
2773
|
+
min-width: 0;
|
|
2774
|
+
height: calc(var(--dg-row-height) - 2px);
|
|
2775
|
+
padding: 0 4px;
|
|
2776
|
+
border: none;
|
|
2777
|
+
border-bottom: 2px solid var(--dg-primary);
|
|
2778
|
+
border-radius: 0;
|
|
2779
|
+
background: transparent;
|
|
2780
|
+
color: var(--dg-text);
|
|
2781
|
+
font: inherit;
|
|
2782
|
+
font-size: var(--dg-font-size);
|
|
2783
|
+
margin: 0;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
/* Edge to edge of the CELL, not of its padding box.
|
|
2787
|
+
`width: 100%` resolves against the content box — the cell minus 2 ×
|
|
2788
|
+
--dg-cell-padding-x — and a negative margin only shifts a box, it does not
|
|
2789
|
+
widen it. The two together left the editor starting early and stopping short
|
|
2790
|
+
of the right edge, read as the field sliding out of its column and most
|
|
2791
|
+
visible on a pinned one, where the neighbouring cell does not move with it.
|
|
2792
|
+
|
|
2793
|
+
Applied to whatever is the cell's DIRECT CHILD, because that is the element
|
|
2794
|
+
whose `100%` means "the cell". Naming the input alone was wrong the moment an
|
|
2795
|
+
editor grew a wrapper — the date editor's box sat 8px inside its cell on the
|
|
2796
|
+
left and 9px on the right, while every plain editor beside it went edge to
|
|
2797
|
+
edge. Same reason the form's wrappers are excluded: they are not in a cell,
|
|
2798
|
+
and pulling them out by a cell's padding would misalign the form instead. */
|
|
2799
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell > .kanuni-datagrid-editor,
|
|
2800
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell > .kanuni-datagrid-date-editor {
|
|
2801
|
+
width: calc(100% + var(--dg-cell-padding-x) * 2);
|
|
2802
|
+
margin: 0 calc(var(--dg-cell-padding-x) * -1);
|
|
2803
|
+
}
|
|
2804
|
+
|
|
2805
|
+
/* In a WRAPPED cell the editor takes the LINE BOX, not the row height.
|
|
2806
|
+
A wrapping cell is as tall as its own text: one line of it is
|
|
2807
|
+
`padding-block x2 + --dg-wrap-line-height`, which is shorter than a standard
|
|
2808
|
+
row. Giving the editor the row height there made the cell taller than the
|
|
2809
|
+
text it replaced, so the row grew the moment an editor opened and every row
|
|
2810
|
+
below it hopped down — most obvious on a theme with tall rows, but the
|
|
2811
|
+
default did it too.
|
|
2812
|
+
|
|
2813
|
+
Matching the line box means the box the user types into occupies exactly the
|
|
2814
|
+
space the text did, and the row does not move at all. */
|
|
2815
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell--wrap > .kanuni-datagrid-editor,
|
|
2816
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell--wrap > .kanuni-datagrid-date-editor {
|
|
2817
|
+
height: var(--dg-wrap-line-height);
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
.kanuni-datagrid-root .kanuni-datagrid-editor:focus {
|
|
2821
|
+
outline: none;
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
.kanuni-datagrid-root .kanuni-datagrid-editor--invalid {
|
|
2825
|
+
border-bottom-color: var(--dg-danger);
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
/* The cell holding an editor gives up its focus ring — the underline is the
|
|
2829
|
+
marker now, and the ring is the other half of the double rectangle.
|
|
2830
|
+
It also goes to the plain surface colour: the row underneath may be striped,
|
|
2831
|
+
selected, focused or in-range, and a blue underline drawn over a blue-tinted
|
|
2832
|
+
cell is the one thing it must not compete with. Listed after the row-state
|
|
2833
|
+
rules and with two classes, so it outranks all of them. */
|
|
2834
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell--editing,
|
|
2835
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell--editing.kanuni-datagrid-cell--focused,
|
|
2836
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell--editing.kanuni-datagrid-cell--in-range,
|
|
2837
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell--editing.kanuni-datagrid-cell--pinned {
|
|
2838
|
+
outline: none;
|
|
2839
|
+
background: var(--dg-surface);
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
.kanuni-datagrid-root input.kanuni-datagrid-editor[type='checkbox'] {
|
|
2843
|
+
width: auto;
|
|
2844
|
+
height: auto;
|
|
2845
|
+
margin: 0;
|
|
2846
|
+
border: none;
|
|
2847
|
+
accent-color: var(--dg-primary);
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
/* Validation message floats out of the cell so it is not clipped by overflow.
|
|
2851
|
+
That only works if the editing cell LETS it out and its row sits above the
|
|
2852
|
+
next one: cells are `overflow: hidden` and every row paints over the one
|
|
2853
|
+
before it, so until 2026-10-01 the message rendered into the DOM, positioned
|
|
2854
|
+
correctly, and was never seen — the user got a red underline and no reason
|
|
2855
|
+
(found shooting the order-editing demo; measured in Chrome, the message box
|
|
2856
|
+
at the right place and fully covered). With these two rules it shows;
|
|
2857
|
+
whether either alone would do was not measured. */
|
|
2858
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell--editing {
|
|
2859
|
+
overflow: visible;
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
.kanuni-datagrid-root .kanuni-datagrid-row:has(.kanuni-datagrid-cell--editing) {
|
|
2863
|
+
position: relative;
|
|
2864
|
+
z-index: 4;
|
|
2865
|
+
overflow: visible;
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
.kanuni-datagrid-root .kanuni-datagrid-editor-error {
|
|
2869
|
+
position: absolute;
|
|
2870
|
+
z-index: 6;
|
|
2871
|
+
transform: translateY(calc(var(--dg-row-height) - 2px));
|
|
2872
|
+
max-width: 240px;
|
|
2873
|
+
padding: 2px 6px;
|
|
2874
|
+
border-radius: var(--dg-radius-control);
|
|
2875
|
+
background: var(--dg-danger);
|
|
2876
|
+
color: var(--dg-on-danger);
|
|
2877
|
+
font-size: 11px;
|
|
2878
|
+
white-space: nowrap;
|
|
2879
|
+
pointer-events: none;
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
/* ── Lookup combo box ─────────────────────────────────────── */
|
|
2883
|
+
/* Rendered in a portal on <body>, so it is NOT under .kanuni-datagrid-root and
|
|
2884
|
+
cannot inherit the theme tokens through the cascade. It reads them from
|
|
2885
|
+
:root, which is where the light/dark blocks define them. */
|
|
2886
|
+
/* ── Date editor ──────────────────────────────────────────── */
|
|
2887
|
+
/* The box and its calendar button share the cell: the input takes the space
|
|
2888
|
+
that is left, so the button never overlaps the text however narrow the
|
|
2889
|
+
column is. */
|
|
2890
|
+
.kanuni-datagrid-date-editor {
|
|
2891
|
+
display: flex;
|
|
2892
|
+
align-items: center;
|
|
2893
|
+
gap: 2px;
|
|
2894
|
+
width: 100%;
|
|
2895
|
+
min-width: 0;
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
/* Inside a box that already owns the cell — compensating twice would push the
|
|
2899
|
+
field back out by another padding on each side. */
|
|
2900
|
+
.kanuni-datagrid-date-editor > .kanuni-datagrid-editor {
|
|
2901
|
+
width: auto;
|
|
2902
|
+
margin: 0;
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
.kanuni-datagrid-date-editor > .kanuni-datagrid-editor {
|
|
2906
|
+
flex: 1;
|
|
2907
|
+
min-width: 0;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
.kanuni-datagrid-date-button {
|
|
2911
|
+
flex: none;
|
|
2912
|
+
display: inline-flex;
|
|
2913
|
+
align-items: center;
|
|
2914
|
+
justify-content: center;
|
|
2915
|
+
width: 20px;
|
|
2916
|
+
height: 20px;
|
|
2917
|
+
padding: 0;
|
|
2918
|
+
border: 0;
|
|
2919
|
+
border-radius: 3px;
|
|
2920
|
+
background: transparent;
|
|
2921
|
+
color: var(--dg-text-muted, #64748b);
|
|
2922
|
+
cursor: pointer;
|
|
2923
|
+
}
|
|
2924
|
+
|
|
2925
|
+
.kanuni-datagrid-date-button:hover:not(:disabled) {
|
|
2926
|
+
background: var(--dg-surface-hover, #f1f5f9);
|
|
2927
|
+
color: var(--dg-primary, #2563eb);
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
.kanuni-datagrid-date-button:disabled {
|
|
2931
|
+
opacity: 0.5;
|
|
2932
|
+
cursor: default;
|
|
2933
|
+
}
|
|
2934
|
+
|
|
2935
|
+
.kanuni-datagrid-date-popup {
|
|
2936
|
+
position: fixed;
|
|
2937
|
+
z-index: 1200;
|
|
2938
|
+
padding: 8px;
|
|
2939
|
+
border: 1px solid var(--dg-border-strong, #cbd5e1);
|
|
2940
|
+
border-radius: var(--dg-radius);
|
|
2941
|
+
background: var(--dg-surface, #fff);
|
|
2942
|
+
box-shadow: var(--dg-shadow-popup-sm);
|
|
2943
|
+
color: var(--dg-text, #0f172a);
|
|
2944
|
+
font-size: var(--dg-font-size);
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
.kanuni-datagrid-date-popup-head {
|
|
2948
|
+
display: flex;
|
|
2949
|
+
align-items: center;
|
|
2950
|
+
justify-content: space-between;
|
|
2951
|
+
gap: 8px;
|
|
2952
|
+
margin-bottom: 6px;
|
|
2953
|
+
font-weight: 600;
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
.kanuni-datagrid-date-popup-head button {
|
|
2957
|
+
width: 24px;
|
|
2958
|
+
height: 24px;
|
|
2959
|
+
border: 0;
|
|
2960
|
+
border-radius: var(--dg-radius-control);
|
|
2961
|
+
background: transparent;
|
|
2962
|
+
color: inherit;
|
|
2963
|
+
font-size: 16px;
|
|
2964
|
+
line-height: 1;
|
|
2965
|
+
cursor: pointer;
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
.kanuni-datagrid-date-popup-head button:hover {
|
|
2969
|
+
background: var(--dg-surface-hover, #f1f5f9);
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
/* Seven fixed columns: a calendar that reflows is not a calendar. */
|
|
2973
|
+
.kanuni-datagrid-date-popup-grid {
|
|
2974
|
+
display: grid;
|
|
2975
|
+
grid-template-columns: repeat(7, 28px);
|
|
2976
|
+
gap: 1px;
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
.kanuni-datagrid-date-weekday {
|
|
2980
|
+
display: flex;
|
|
2981
|
+
align-items: center;
|
|
2982
|
+
justify-content: center;
|
|
2983
|
+
height: 22px;
|
|
2984
|
+
color: var(--dg-text-muted, #64748b);
|
|
2985
|
+
font-size: 11px;
|
|
2986
|
+
font-weight: 600;
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
.kanuni-datagrid-date-day {
|
|
2990
|
+
height: 26px;
|
|
2991
|
+
border: 0;
|
|
2992
|
+
border-radius: var(--dg-radius-control);
|
|
2993
|
+
background: transparent;
|
|
2994
|
+
color: inherit;
|
|
2995
|
+
font: inherit;
|
|
2996
|
+
cursor: pointer;
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
.kanuni-datagrid-date-day:hover {
|
|
3000
|
+
background: var(--dg-surface-hover, #f1f5f9);
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
/* Days spilling in from the neighbouring months stay clickable — paging to
|
|
3004
|
+
pick the 1st of next month is a step nobody should have to take — but they
|
|
3005
|
+
read as context rather than as part of this month. */
|
|
3006
|
+
.kanuni-datagrid-date-day--outside {
|
|
3007
|
+
opacity: 0.4;
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
.kanuni-datagrid-date-day--selected {
|
|
3011
|
+
background: var(--dg-primary);
|
|
3012
|
+
color: var(--dg-on-primary);
|
|
3013
|
+
font-weight: 600;
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
.kanuni-datagrid-lookup-popup {
|
|
3017
|
+
position: fixed;
|
|
3018
|
+
z-index: 1200;
|
|
3019
|
+
overflow-y: auto;
|
|
3020
|
+
border: 1px solid var(--dg-border-strong, #cbd5e1);
|
|
3021
|
+
border-radius: var(--dg-radius-control);
|
|
3022
|
+
background: var(--dg-surface, #fff);
|
|
3023
|
+
box-shadow: var(--dg-shadow-popup-sm);
|
|
3024
|
+
color: var(--dg-text, #0f172a);
|
|
3025
|
+
font-size: var(--dg-font-size);
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
.kanuni-datagrid-lookup-item {
|
|
3029
|
+
padding: 4px 8px;
|
|
3030
|
+
line-height: 18px;
|
|
3031
|
+
white-space: nowrap;
|
|
3032
|
+
overflow: hidden;
|
|
3033
|
+
text-overflow: ellipsis;
|
|
3034
|
+
cursor: pointer;
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3037
|
+
.kanuni-datagrid-lookup-item--active {
|
|
3038
|
+
background: var(--dg-primary);
|
|
3039
|
+
color: var(--dg-on-primary);
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
.kanuni-datagrid-lookup-empty {
|
|
3043
|
+
padding: 4px 8px;
|
|
3044
|
+
color: var(--dg-text-muted, #64748b);
|
|
3045
|
+
font-style: italic;
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
/* ── Confirmation / error dialog ──────────────────────────── */
|
|
3049
|
+
.kanuni-datagrid-dialog-root {
|
|
3050
|
+
position: fixed;
|
|
3051
|
+
inset: 0;
|
|
3052
|
+
z-index: 1400;
|
|
3053
|
+
display: flex;
|
|
3054
|
+
align-items: center;
|
|
3055
|
+
justify-content: center;
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
.kanuni-datagrid-dialog-overlay {
|
|
3059
|
+
position: absolute;
|
|
3060
|
+
inset: 0;
|
|
3061
|
+
background: var(--dg-overlay);
|
|
3062
|
+
}
|
|
3063
|
+
|
|
3064
|
+
.kanuni-datagrid-dialog {
|
|
3065
|
+
position: relative;
|
|
3066
|
+
width: min(420px, 92vw);
|
|
3067
|
+
padding: 18px 20px 14px;
|
|
3068
|
+
border: 1px solid var(--dg-border-strong, #cbd5e1);
|
|
3069
|
+
border-radius: calc(var(--dg-radius) + 2px);
|
|
3070
|
+
background: var(--dg-surface, #fff);
|
|
3071
|
+
color: var(--dg-text, #0f172a);
|
|
3072
|
+
box-shadow: var(--dg-shadow-dialog);
|
|
3073
|
+
font-size: var(--dg-font-size);
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
.kanuni-datagrid-dialog-title {
|
|
3077
|
+
margin-bottom: 8px;
|
|
3078
|
+
font-size: 15px;
|
|
3079
|
+
font-weight: 600;
|
|
3080
|
+
}
|
|
3081
|
+
|
|
3082
|
+
.kanuni-datagrid-dialog-message {
|
|
3083
|
+
margin-bottom: 18px;
|
|
3084
|
+
line-height: 1.5;
|
|
3085
|
+
color: var(--dg-text, #0f172a);
|
|
3086
|
+
}
|
|
3087
|
+
|
|
3088
|
+
.kanuni-datagrid-dialog-actions {
|
|
3089
|
+
display: flex;
|
|
3090
|
+
justify-content: flex-end;
|
|
3091
|
+
gap: 8px;
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3094
|
+
.kanuni-datagrid-dialog-actions button {
|
|
3095
|
+
padding: 5px 14px;
|
|
3096
|
+
border: 1px solid var(--dg-border-strong, #cbd5e1);
|
|
3097
|
+
border-radius: var(--dg-radius-control);
|
|
3098
|
+
background: var(--dg-surface, #fff);
|
|
3099
|
+
color: inherit;
|
|
3100
|
+
font: inherit;
|
|
3101
|
+
font-size: var(--dg-font-size);
|
|
3102
|
+
cursor: pointer;
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
/* SCOPED TO THE ACTIONS ROW ON PURPOSE, and this is a fix rather than a style.
|
|
3106
|
+
`.kanuni-datagrid-dialog-actions button` above is (0,1,1) — a class and a
|
|
3107
|
+
type — while a bare `.kanuni-datagrid-dialog-primary` is (0,1,0). Specificity
|
|
3108
|
+
beats source order, so the generic rule won and the confirm button had never
|
|
3109
|
+
actually been painted in the accent: it rendered as a plain white button, and
|
|
3110
|
+
the only reason nobody read it as broken is that its label inherited the
|
|
3111
|
+
dialog's text colour instead of the white it was assigned. The sibling
|
|
3112
|
+
primaries elsewhere in this sheet (filter builder, form save) sidestep the
|
|
3113
|
+
same collision with `!important`; matching the selector shape is the same
|
|
3114
|
+
result without the escape hatch. */
|
|
3115
|
+
.kanuni-datagrid-dialog-actions button.kanuni-datagrid-dialog-primary {
|
|
3116
|
+
border-color: var(--dg-primary);
|
|
3117
|
+
background: var(--dg-primary);
|
|
3118
|
+
color: var(--dg-on-primary);
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
/* Composed WITH `--primary`, so it has to restate the foreground: the primary
|
|
3122
|
+
rule painted it `--dg-on-primary`, which is the right colour for the accent
|
|
3123
|
+
and the wrong one for a red fill (near-black on red in the gold themes). */
|
|
3124
|
+
.kanuni-datagrid-dialog-actions button.kanuni-datagrid-dialog-primary--danger {
|
|
3125
|
+
border-color: var(--dg-danger);
|
|
3126
|
+
background: var(--dg-danger);
|
|
3127
|
+
color: var(--dg-on-danger);
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
/* ── Forced colours (Windows high contrast) ─────────────────────
|
|
3131
|
+
The browser replaces our colours with the user's system palette and drops
|
|
3132
|
+
box-shadows and background images, so anything drawn only by a fill or a
|
|
3133
|
+
shadow disappears — the focused cell, a selected row, the editor's
|
|
3134
|
+
underline. Restated with system colours, which survive (accessibility
|
|
3135
|
+
audit, 2026-10-12: there was no forced-colors rule anywhere). */
|
|
3136
|
+
@media (forced-colors: active) {
|
|
3137
|
+
.kanuni-datagrid-root .kanuni-datagrid-cell--focused,
|
|
3138
|
+
.kanuni-datagrid-root .kanuni-datagrid-group-row--focused {
|
|
3139
|
+
outline: 2px solid Highlight;
|
|
3140
|
+
outline-offset: -2px;
|
|
3141
|
+
}
|
|
3142
|
+
.kanuni-datagrid-root .kanuni-datagrid-row--selected,
|
|
3143
|
+
.kanuni-datagrid-root .kanuni-datagrid-row--selected .kanuni-datagrid-cell {
|
|
3144
|
+
forced-color-adjust: none;
|
|
3145
|
+
background: Highlight;
|
|
3146
|
+
color: HighlightText;
|
|
3147
|
+
}
|
|
3148
|
+
.kanuni-datagrid-root .kanuni-datagrid-editor {
|
|
3149
|
+
border: 1px solid CanvasText;
|
|
3150
|
+
}
|
|
3151
|
+
.kanuni-datagrid-root :focus-visible {
|
|
3152
|
+
outline: 2px solid Highlight;
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
|
|
2561
3156
|
/* ─────────────────────────────────────────────────────────────
|
|
2562
3157
|
KANUNILABS DataGrid — packaged themes
|
|
2563
3158
|
─────────────────────────────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kanunilabs/datagrid-react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "High-performance React data grid — virtualized rows and columns, sticky pinned columns, sorting, filtering and theming",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -57,13 +57,14 @@
|
|
|
57
57
|
"access": "public"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@kanunilabs/datagrid-core": "1.
|
|
60
|
+
"@kanunilabs/datagrid-core": "1.9.0"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"react": ">=18.0.0",
|
|
64
64
|
"react-dom": ">=18.0.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
+
"axe-core": "4.10.2",
|
|
67
68
|
"jsdom": "^30.0.1",
|
|
68
69
|
"@kanunilabs/foundation": "1.0.4"
|
|
69
70
|
},
|