@svgrid/grid 2.0.0 → 2.0.2
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/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +178 -178
- package/dist/SvGrid.css +2376 -2376
- package/dist/SvGrid.svelte +2764 -2764
- package/dist/SvGridDropdown.svelte +666 -666
- package/dist/cdn/svgrid.js +1 -1
- package/dist/cdn/svgrid.svelte-external.js +1 -1
- package/package.json +85 -85
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +178 -178
- package/src/SvGrid.controller.svelte.ts +2553 -2553
- package/src/SvGrid.css +2376 -2376
- package/src/SvGrid.svelte +2764 -2764
- package/src/SvGrid.types.ts +944 -944
- package/src/SvGridDropdown.svelte +666 -666
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- package/src/build-api.ts +798 -798
- package/src/cell-formatting.ts +169 -169
- package/src/cell-render.ts +469 -469
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1111 -1111
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/editing.test.ts +859 -859
- package/src/editing.ts +675 -675
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +598 -598
- package/src/flex-render.ts +3 -3
- package/src/index.ts +463 -463
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/menus.ts +582 -582
- package/src/merge-objects.ts +48 -48
- package/src/render-component.ts +28 -28
- package/src/selection.test.ts +754 -754
- package/src/selection.ts +600 -600
- package/src/server-data-source.test.ts +289 -289
- package/src/server-data-source.ts +413 -413
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +489 -489
- package/src/spreadsheet.ts +304 -304
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/svgrid-wrapper.types.ts +439 -439
- package/src/svgrid.behavior.test.ts +706 -706
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.wrapper.test.ts +40 -40
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +296 -296
package/dist/SvGrid.css
CHANGED
|
@@ -1,2376 +1,2376 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* SvGrid component styles. Extracted from SvGrid.svelte's <style> block so the
|
|
3
|
-
* component file stays focused on logic + markup. These are plain (global) CSS
|
|
4
|
-
* rules - every selector is a unique .sv-grid-* class that maps to a real class
|
|
5
|
-
* on the grid's elements, so behaviour is identical to the former scoped block.
|
|
6
|
-
* Imported for its side effect by SvGrid.svelte; the WC build inlines it and
|
|
7
|
-
* app bundlers include it via the import.
|
|
8
|
-
*/
|
|
9
|
-
/* Root wrapper: holds the shell + pager + any overlays (find, tooltips
|
|
10
|
-
* pinned to the root). Position-relative so absolutely-positioned
|
|
11
|
-
* overlays (the find-in-grid toolbar) anchor to the grid, not the
|
|
12
|
-
* viewport. */
|
|
13
|
-
.sv-grid-root {
|
|
14
|
-
position: relative;
|
|
15
|
-
}
|
|
16
|
-
.sv-grid-sr-only {
|
|
17
|
-
position: absolute;
|
|
18
|
-
width: 1px;
|
|
19
|
-
height: 1px;
|
|
20
|
-
overflow: hidden;
|
|
21
|
-
clip: rect(0 0 0 0);
|
|
22
|
-
white-space: nowrap;
|
|
23
|
-
}
|
|
24
|
-
.sv-grid-loading-overlay {
|
|
25
|
-
position: absolute;
|
|
26
|
-
inset: 0;
|
|
27
|
-
z-index: 18;
|
|
28
|
-
pointer-events: auto;
|
|
29
|
-
background: color-mix(in srgb, var(--sg-bg, #fff) 35%, transparent);
|
|
30
|
-
}
|
|
31
|
-
/* Indeterminate top progress bar - the "something is happening" signal
|
|
32
|
-
that keeps the current rows visible underneath (no flash). */
|
|
33
|
-
.sv-grid-loading-bar {
|
|
34
|
-
position: absolute;
|
|
35
|
-
top: 0;
|
|
36
|
-
left: 0;
|
|
37
|
-
right: 0;
|
|
38
|
-
height: 3px;
|
|
39
|
-
overflow: hidden;
|
|
40
|
-
background: color-mix(in srgb, var(--sg-accent, #2563eb) 18%, transparent);
|
|
41
|
-
}
|
|
42
|
-
.sv-grid-loading-bar::before {
|
|
43
|
-
content: '';
|
|
44
|
-
position: absolute;
|
|
45
|
-
inset: 0;
|
|
46
|
-
width: 40%;
|
|
47
|
-
background: var(--sg-accent, #2563eb);
|
|
48
|
-
animation: sv-grid-loading-slide 1.1s ease-in-out infinite;
|
|
49
|
-
}
|
|
50
|
-
@keyframes sv-grid-loading-slide {
|
|
51
|
-
0% { transform: translateX(-100%); }
|
|
52
|
-
100% { transform: translateX(350%); }
|
|
53
|
-
}
|
|
54
|
-
/* First-load skeleton: shimmer placeholder rows under the header. */
|
|
55
|
-
.sv-grid-skeleton {
|
|
56
|
-
position: absolute;
|
|
57
|
-
top: var(--sg-thead-h, 40px);
|
|
58
|
-
left: 0;
|
|
59
|
-
right: 0;
|
|
60
|
-
padding: 0;
|
|
61
|
-
overflow: hidden;
|
|
62
|
-
}
|
|
63
|
-
.sv-grid-skeleton-row {
|
|
64
|
-
display: flex;
|
|
65
|
-
height: var(--sg-pinned-row-h, 36px);
|
|
66
|
-
align-items: center;
|
|
67
|
-
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
68
|
-
}
|
|
69
|
-
.sv-grid-skeleton-cell {
|
|
70
|
-
flex-shrink: 0;
|
|
71
|
-
padding: 0 12px;
|
|
72
|
-
box-sizing: border-box;
|
|
73
|
-
}
|
|
74
|
-
.sv-grid-skeleton-bar {
|
|
75
|
-
display: block;
|
|
76
|
-
height: 10px;
|
|
77
|
-
width: 70%;
|
|
78
|
-
border-radius: 4px;
|
|
79
|
-
background: linear-gradient(
|
|
80
|
-
90deg,
|
|
81
|
-
color-mix(in srgb, var(--sg-muted, #94a3b8) 18%, transparent) 25%,
|
|
82
|
-
color-mix(in srgb, var(--sg-muted, #94a3b8) 32%, transparent) 50%,
|
|
83
|
-
color-mix(in srgb, var(--sg-muted, #94a3b8) 18%, transparent) 75%
|
|
84
|
-
);
|
|
85
|
-
background-size: 200% 100%;
|
|
86
|
-
animation: sv-grid-shimmer 1.3s linear infinite;
|
|
87
|
-
}
|
|
88
|
-
@keyframes sv-grid-shimmer {
|
|
89
|
-
0% { background-position: 200% 0; }
|
|
90
|
-
100% { background-position: -200% 0; }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/* Grid toolbar strip above the shell - hosts the tool-panel trigger (and is a
|
|
94
|
-
natural home for future grid-level actions). */
|
|
95
|
-
.sv-grid-toolbar {
|
|
96
|
-
display: flex;
|
|
97
|
-
align-items: center;
|
|
98
|
-
gap: 8px;
|
|
99
|
-
padding: 6px 2px;
|
|
100
|
-
}
|
|
101
|
-
.sv-grid-toolbar-btn {
|
|
102
|
-
display: inline-flex;
|
|
103
|
-
align-items: center;
|
|
104
|
-
gap: 6px;
|
|
105
|
-
padding: 6px 12px;
|
|
106
|
-
border: 1px solid var(--sg-border, #e2e8f0);
|
|
107
|
-
border-radius: var(--sg-radius, 6px);
|
|
108
|
-
background: var(--sg-bg, #fff);
|
|
109
|
-
color: var(--sg-fg, #0f172a);
|
|
110
|
-
font-size: 12.5px;
|
|
111
|
-
font-weight: 600;
|
|
112
|
-
cursor: pointer;
|
|
113
|
-
transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
|
|
114
|
-
}
|
|
115
|
-
.sv-grid-toolbar-btn:hover {
|
|
116
|
-
border-color: var(--sg-accent, #2563eb);
|
|
117
|
-
color: var(--sg-accent, #2563eb);
|
|
118
|
-
}
|
|
119
|
-
.sv-grid-toolbar-btn.is-active {
|
|
120
|
-
background: var(--sg-accent, #2563eb);
|
|
121
|
-
border-color: var(--sg-accent, #2563eb);
|
|
122
|
-
color: #fff;
|
|
123
|
-
}
|
|
124
|
-
.sv-grid-tool-panel {
|
|
125
|
-
position: absolute;
|
|
126
|
-
top: 0;
|
|
127
|
-
right: 0;
|
|
128
|
-
bottom: 0;
|
|
129
|
-
/* Above the grid's sticky header (z-index 30) and custom scrollbars (z ~900)
|
|
130
|
-
so the docked panel fully overlays the grid instead of the header/right
|
|
131
|
-
columns bleeding through its top-right. Ties the menu layer (901) but the
|
|
132
|
-
menus render later in the DOM, so a column menu opened over the panel still
|
|
133
|
-
wins. */
|
|
134
|
-
z-index: 901;
|
|
135
|
-
width: 250px;
|
|
136
|
-
display: flex;
|
|
137
|
-
flex-direction: column;
|
|
138
|
-
background: var(--sg-bg, #fff);
|
|
139
|
-
border-left: 1px solid var(--sg-border, #e2e8f0);
|
|
140
|
-
box-shadow: -8px 0 24px rgba(0, 0, 0, 0.12);
|
|
141
|
-
}
|
|
142
|
-
.sv-grid-tool-panel-head {
|
|
143
|
-
display: flex;
|
|
144
|
-
align-items: center;
|
|
145
|
-
justify-content: space-between;
|
|
146
|
-
padding: 8px 12px;
|
|
147
|
-
font-size: 12px;
|
|
148
|
-
font-weight: 700;
|
|
149
|
-
text-transform: uppercase;
|
|
150
|
-
letter-spacing: 0.05em;
|
|
151
|
-
color: var(--sg-muted, #64748b);
|
|
152
|
-
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
153
|
-
}
|
|
154
|
-
.sv-grid-tool-panel-close {
|
|
155
|
-
border: 0;
|
|
156
|
-
background: transparent;
|
|
157
|
-
color: var(--sg-muted, #64748b);
|
|
158
|
-
font-size: 14px;
|
|
159
|
-
cursor: pointer;
|
|
160
|
-
}
|
|
161
|
-
.sv-grid-tool-panel-list {
|
|
162
|
-
list-style: none;
|
|
163
|
-
margin: 0;
|
|
164
|
-
padding: 6px;
|
|
165
|
-
overflow-y: auto;
|
|
166
|
-
flex: 1;
|
|
167
|
-
}
|
|
168
|
-
.sv-grid-tool-panel-item {
|
|
169
|
-
display: flex;
|
|
170
|
-
align-items: center;
|
|
171
|
-
justify-content: space-between;
|
|
172
|
-
gap: 6px;
|
|
173
|
-
padding: 3px 6px;
|
|
174
|
-
border-radius: 5px;
|
|
175
|
-
}
|
|
176
|
-
.sv-grid-tool-panel-item:hover {
|
|
177
|
-
background: var(--sg-row-hover-bg, rgba(148, 163, 184, 0.1));
|
|
178
|
-
}
|
|
179
|
-
.sv-grid-tool-panel-vis {
|
|
180
|
-
display: flex;
|
|
181
|
-
align-items: center;
|
|
182
|
-
gap: 8px;
|
|
183
|
-
flex: 1;
|
|
184
|
-
min-width: 0;
|
|
185
|
-
font-size: 13px;
|
|
186
|
-
color: var(--sg-fg, #0f172a);
|
|
187
|
-
cursor: pointer;
|
|
188
|
-
}
|
|
189
|
-
.sv-grid-tool-panel-vis input {
|
|
190
|
-
accent-color: var(--sg-accent, #2563eb);
|
|
191
|
-
}
|
|
192
|
-
.sv-grid-tool-panel-name {
|
|
193
|
-
overflow: hidden;
|
|
194
|
-
text-overflow: ellipsis;
|
|
195
|
-
white-space: nowrap;
|
|
196
|
-
}
|
|
197
|
-
.sv-grid-tool-panel-actions {
|
|
198
|
-
display: inline-flex;
|
|
199
|
-
gap: 2px;
|
|
200
|
-
flex-shrink: 0;
|
|
201
|
-
}
|
|
202
|
-
.sv-grid-tool-panel-btn {
|
|
203
|
-
width: 22px;
|
|
204
|
-
height: 22px;
|
|
205
|
-
border: 0;
|
|
206
|
-
border-radius: 4px;
|
|
207
|
-
background: transparent;
|
|
208
|
-
color: var(--sg-muted, #64748b);
|
|
209
|
-
font-size: 13px;
|
|
210
|
-
cursor: pointer;
|
|
211
|
-
}
|
|
212
|
-
.sv-grid-tool-panel-btn:hover:not(:disabled) {
|
|
213
|
-
background: var(--sg-row-hover-bg, rgba(148, 163, 184, 0.18));
|
|
214
|
-
color: var(--sg-fg, #0f172a);
|
|
215
|
-
}
|
|
216
|
-
.sv-grid-tool-panel-btn.is-active {
|
|
217
|
-
color: var(--sg-accent, #2563eb);
|
|
218
|
-
}
|
|
219
|
-
.sv-grid-tool-panel-btn:disabled {
|
|
220
|
-
opacity: 0.35;
|
|
221
|
-
cursor: default;
|
|
222
|
-
}
|
|
223
|
-
/* Tool-panel tab bar (Columns | Filters) */
|
|
224
|
-
.sv-grid-tool-panel-tabs {
|
|
225
|
-
display: flex;
|
|
226
|
-
gap: 2px;
|
|
227
|
-
padding: 6px 6px 0;
|
|
228
|
-
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
229
|
-
}
|
|
230
|
-
.sv-grid-tool-panel-tab {
|
|
231
|
-
flex: 1;
|
|
232
|
-
padding: 6px 8px;
|
|
233
|
-
border: 0;
|
|
234
|
-
border-bottom: 2px solid transparent;
|
|
235
|
-
background: transparent;
|
|
236
|
-
color: var(--sg-muted, #64748b);
|
|
237
|
-
font-size: 12px;
|
|
238
|
-
font-weight: 600;
|
|
239
|
-
cursor: pointer;
|
|
240
|
-
}
|
|
241
|
-
.sv-grid-tool-panel-tab:hover {
|
|
242
|
-
color: var(--sg-fg, #0f172a);
|
|
243
|
-
}
|
|
244
|
-
.sv-grid-tool-panel-tab.is-active {
|
|
245
|
-
color: var(--sg-accent, #2563eb);
|
|
246
|
-
border-bottom-color: var(--sg-accent, #2563eb);
|
|
247
|
-
}
|
|
248
|
-
/* Filters tab: one control block per filterable column */
|
|
249
|
-
.sv-grid-tool-panel-filters {
|
|
250
|
-
padding: 6px;
|
|
251
|
-
overflow-y: auto;
|
|
252
|
-
flex: 1;
|
|
253
|
-
display: flex;
|
|
254
|
-
flex-direction: column;
|
|
255
|
-
gap: 8px;
|
|
256
|
-
}
|
|
257
|
-
.sv-grid-tp-filter {
|
|
258
|
-
display: flex;
|
|
259
|
-
flex-direction: column;
|
|
260
|
-
gap: 4px;
|
|
261
|
-
padding: 6px;
|
|
262
|
-
border: 1px solid var(--sg-border, #e2e8f0);
|
|
263
|
-
border-radius: 6px;
|
|
264
|
-
}
|
|
265
|
-
.sv-grid-tp-filter.is-filtered {
|
|
266
|
-
border-color: var(--sg-accent, #2563eb);
|
|
267
|
-
}
|
|
268
|
-
.sv-grid-tp-filter-head {
|
|
269
|
-
display: flex;
|
|
270
|
-
align-items: center;
|
|
271
|
-
justify-content: space-between;
|
|
272
|
-
}
|
|
273
|
-
.sv-grid-tp-filter-name {
|
|
274
|
-
font-size: 12px;
|
|
275
|
-
font-weight: 600;
|
|
276
|
-
color: var(--sg-fg, #0f172a);
|
|
277
|
-
}
|
|
278
|
-
.sv-grid-tp-filter-clear {
|
|
279
|
-
border: 0;
|
|
280
|
-
background: transparent;
|
|
281
|
-
color: var(--sg-muted, #64748b);
|
|
282
|
-
font-size: 11px;
|
|
283
|
-
cursor: pointer;
|
|
284
|
-
padding: 0 2px;
|
|
285
|
-
}
|
|
286
|
-
.sv-grid-tp-filter-clear:hover {
|
|
287
|
-
color: var(--sg-accent, #2563eb);
|
|
288
|
-
}
|
|
289
|
-
.sv-grid-tp-filter-op,
|
|
290
|
-
.sv-grid-tp-filter-input {
|
|
291
|
-
width: 100%;
|
|
292
|
-
box-sizing: border-box;
|
|
293
|
-
padding: 4px 6px;
|
|
294
|
-
font-size: 12px;
|
|
295
|
-
border: 1px solid var(--sg-border, #e2e8f0);
|
|
296
|
-
border-radius: 5px;
|
|
297
|
-
background: var(--sg-bg, #fff);
|
|
298
|
-
color: var(--sg-fg, #0f172a);
|
|
299
|
-
}
|
|
300
|
-
/* Root wrapper fill mode: only takes effect when consumer passes
|
|
301
|
-
* containerHeight='100%'. The shell becomes a flex item that expands
|
|
302
|
-
* while the pager keeps its natural height. */
|
|
303
|
-
.sv-grid-root-fill {
|
|
304
|
-
display: flex;
|
|
305
|
-
flex-direction: column;
|
|
306
|
-
height: 100%;
|
|
307
|
-
min-height: 0;
|
|
308
|
-
}
|
|
309
|
-
.sv-grid-root-fill > .sv-grid-shell {
|
|
310
|
-
flex: 1 1 0;
|
|
311
|
-
min-height: 0;
|
|
312
|
-
height: auto !important;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
.sv-grid-shell {
|
|
316
|
-
position: relative;
|
|
317
|
-
border-bottom: 1px solid var(--sg-border, #cbd5e1);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
.sv-grid-table {
|
|
321
|
-
table-layout: fixed;
|
|
322
|
-
border-collapse: separate;
|
|
323
|
-
border-spacing: 0;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
.sv-grid-table:focus {
|
|
327
|
-
outline: none;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.sv-grid-column,
|
|
331
|
-
.sv-grid-cell {
|
|
332
|
-
box-sizing: border-box;
|
|
333
|
-
overflow: hidden;
|
|
334
|
-
text-overflow: ellipsis;
|
|
335
|
-
white-space: nowrap;
|
|
336
|
-
background: var(--sg-bg, #fff);
|
|
337
|
-
color: var(--sg-fg, #0f172a);
|
|
338
|
-
user-select: none;
|
|
339
|
-
/* Grid lines ship with the grid so it looks complete out of the box - no
|
|
340
|
-
host stylesheet required. Only right + bottom per cell (border-spacing is
|
|
341
|
-
0), so adjacent cells share a single 1px line rather than doubling. */
|
|
342
|
-
border-right: 1px solid var(--sg-border, #e2e8f0);
|
|
343
|
-
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
344
|
-
}
|
|
345
|
-
/* The last cell / column in each row drops its right border so the grid's
|
|
346
|
-
right edge stays clean (the shell provides the outer frame). */
|
|
347
|
-
.sv-grid-table tr > :last-child.sv-grid-column,
|
|
348
|
-
.sv-grid-table tr > :last-child.sv-grid-cell {
|
|
349
|
-
border-right: 0;
|
|
350
|
-
}
|
|
351
|
-
/* Row hover tint - opt-in via `enableRowHover` (off by default; the class is
|
|
352
|
-
present unless the grid opts in). Uses `background-color` (not the shorthand)
|
|
353
|
-
so it never resets the fill-marquee `background-image`, and skips selected
|
|
354
|
-
cells so the selection stays visible under a hovered row. */
|
|
355
|
-
.sv-grid-table:not(.sv-grid-no-row-hover)
|
|
356
|
-
tbody tr:hover > .sv-grid-cell:not([data-selected-range="true"]) {
|
|
357
|
-
background-color: var(--sg-row-hover-bg, #eef2ff);
|
|
358
|
-
}
|
|
359
|
-
/* Only the active cell + the cell hosting the fill handle become a
|
|
360
|
-
positioning context (so the absolutely-positioned handle anchors
|
|
361
|
-
to the cell box). Adding `position: relative` to EVERY cell paints
|
|
362
|
-
each one as its own layout box and noticeably slows vertical
|
|
363
|
-
scroll on dense grids - keep it scoped. Sticky / pinned cells are
|
|
364
|
-
already positioning contexts via `position: sticky`. */
|
|
365
|
-
.sv-grid-cell-active,
|
|
366
|
-
.sv-grid-cell-has-fill-handle {
|
|
367
|
-
position: relative;
|
|
368
|
-
}
|
|
369
|
-
/* The cell that owns the fill handle gets overflow: visible so the
|
|
370
|
-
handle isn't clipped - chip cells in particular pack pills right up
|
|
371
|
-
to the cell edges, and with overflow: hidden the handle's hover
|
|
372
|
-
scale-up + the handle itself could be partially eaten by them. */
|
|
373
|
-
.sv-grid-cell-has-fill-handle {
|
|
374
|
-
overflow: visible;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/* Full-width expandable detail row (props.isDetailRow / renderDetailRow).
|
|
378
|
-
Overrides the normal single-line cell clamps so the panel spans every
|
|
379
|
-
column and grows to its natural height. Pair with virtualization={false}
|
|
380
|
-
so the variable height isn't fought by the fixed-row-height virtualizer.
|
|
381
|
-
These rules sit AFTER `.sv-grid-cell` so they win at equal specificity. */
|
|
382
|
-
.sv-grid-detail-row {
|
|
383
|
-
height: auto !important;
|
|
384
|
-
}
|
|
385
|
-
.sv-grid-detail-cell {
|
|
386
|
-
overflow: visible;
|
|
387
|
-
text-overflow: clip;
|
|
388
|
-
white-space: normal;
|
|
389
|
-
height: auto;
|
|
390
|
-
padding: 0;
|
|
391
|
-
background: var(--sg-bg, #fff);
|
|
392
|
-
user-select: text;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/* Pinned columns read as "frozen" via three layered cues:
|
|
396
|
-
1. A distinct tint (slightly stronger than the header) so the
|
|
397
|
-
pinned strip is visible even before you scroll.
|
|
398
|
-
2. A solid 1px divider on the inside edge (toward the scrollable
|
|
399
|
-
middle) and a soft drop shadow extending into the scroll area.
|
|
400
|
-
3. A thin accent stripe on the outermost edge that turns on when
|
|
401
|
-
the grid scrolls past the pin - a familiar Excel-style cue.
|
|
402
|
-
The shadow direction flips based on which side is pinned so it
|
|
403
|
-
always points toward the scrollable middle. */
|
|
404
|
-
.sv-grid-cell[data-pinned="left"],
|
|
405
|
-
.sv-grid-column[data-pinned="left"] {
|
|
406
|
-
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
407
|
-
box-shadow:
|
|
408
|
-
inset -1px 0 0 var(--sg-pinned-divider, var(--sg-border, #cbd5e1)),
|
|
409
|
-
8px 0 12px -6px rgba(15, 23, 42, 0.22);
|
|
410
|
-
}
|
|
411
|
-
.sv-grid-cell[data-pinned="right"],
|
|
412
|
-
.sv-grid-column[data-pinned="right"] {
|
|
413
|
-
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
414
|
-
box-shadow:
|
|
415
|
-
inset 1px 0 0 var(--sg-pinned-divider, var(--sg-border, #cbd5e1)),
|
|
416
|
-
-8px 0 12px -6px rgba(15, 23, 42, 0.22);
|
|
417
|
-
}
|
|
418
|
-
/* Header row pinned cells get a slightly darker variant + bolder font
|
|
419
|
-
so the frozen header itself reads as part of the grid chrome. */
|
|
420
|
-
.sv-grid-head .sv-grid-column[data-pinned="left"],
|
|
421
|
-
.sv-grid-head .sv-grid-column[data-pinned="right"] {
|
|
422
|
-
background: var(--sg-pinned-header-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 86%, var(--sg-accent, #2563eb) 14%));
|
|
423
|
-
font-weight: 600;
|
|
424
|
-
}
|
|
425
|
-
/* Zebra striping, opt-in via the `zebraRows` prop (which adds
|
|
426
|
-
`.sv-grid-row-alt` to alternating DATA rows only - pinned, group,
|
|
427
|
-
detail, and summary rows never get the class, so they keep a single
|
|
428
|
-
background). This rule sits before `.sv-grid-row-selected` /
|
|
429
|
-
`:hover` so those states paint over the stripe. */
|
|
430
|
-
.sv-grid-row-alt > .sv-grid-cell {
|
|
431
|
-
background: var(--sg-row-alt-bg, #f8fafc);
|
|
432
|
-
}
|
|
433
|
-
/* Zebra rows: keep the pinned tint visible (don't let the row-alt
|
|
434
|
-
background bleed through) by re-painting the pinned cells. */
|
|
435
|
-
.sv-grid-row-alt > .sv-grid-cell[data-pinned] {
|
|
436
|
-
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
437
|
-
}
|
|
438
|
-
/* When the row is selected, keep the pinned tint but layer the
|
|
439
|
-
selection color over it so the row still reads as selected. */
|
|
440
|
-
.sv-grid-row-selected > .sv-grid-cell[data-pinned] {
|
|
441
|
-
background:
|
|
442
|
-
linear-gradient(
|
|
443
|
-
color-mix(in srgb, var(--sg-selection-bg, #dbeafe) 65%, transparent),
|
|
444
|
-
color-mix(in srgb, var(--sg-selection-bg, #dbeafe) 65%, transparent)
|
|
445
|
-
),
|
|
446
|
-
var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
447
|
-
}
|
|
448
|
-
/* Hovered row keeps the pinned tint distinguishable from the hover. Selected
|
|
449
|
-
cells are excluded so the selection stays visible under hover. */
|
|
450
|
-
.sv-grid-table:not(.sv-grid-no-row-hover)
|
|
451
|
-
.sv-grid-row:hover > .sv-grid-cell[data-pinned]:not([data-selected-range="true"]) {
|
|
452
|
-
background:
|
|
453
|
-
linear-gradient(
|
|
454
|
-
color-mix(in srgb, var(--sg-row-hover-bg, #eef2ff) 55%, transparent),
|
|
455
|
-
color-mix(in srgb, var(--sg-row-hover-bg, #eef2ff) 55%, transparent)
|
|
456
|
-
),
|
|
457
|
-
var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
.sv-grid-column {
|
|
461
|
-
position: relative;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/* Column alignment via data-align attribute (see getColumnAlign helper).
|
|
465
|
-
* For body cells text-align is enough; for headers the label lives inside
|
|
466
|
-
* a flex container so we adjust justify-content on the sort button.
|
|
467
|
-
* Padding is biased to the aligned side: left-aligned text gets a 7px
|
|
468
|
-
* left gap; right-aligned numbers/dates get a 7px right gap. */
|
|
469
|
-
.sv-grid-cell[data-align="left"],
|
|
470
|
-
.sv-grid-column[data-align="left"] {
|
|
471
|
-
padding-left: var(--sg-cell-px, 7px);
|
|
472
|
-
}
|
|
473
|
-
.sv-grid-cell[data-align="right"] {
|
|
474
|
-
text-align: right;
|
|
475
|
-
padding-right: var(--sg-cell-px, 7px);
|
|
476
|
-
}
|
|
477
|
-
.sv-grid-cell[data-align="center"] {
|
|
478
|
-
text-align: center;
|
|
479
|
-
}
|
|
480
|
-
.sv-grid-column[data-align="right"] {
|
|
481
|
-
padding-right: var(--sg-cell-px, 7px);
|
|
482
|
-
}
|
|
483
|
-
.sv-grid-column[data-align="right"] .sv-grid-header-sort {
|
|
484
|
-
justify-content: flex-end;
|
|
485
|
-
}
|
|
486
|
-
/* The custom vertical scrollbar overlays the right 16px of the viewport
|
|
487
|
-
(absolute, z-index 40). Nudge the last column's right-aligned content in by
|
|
488
|
-
that width when the scrollbar is visible, so numbers/values aren't hidden
|
|
489
|
-
underneath it. Left/center content already clears the right edge. */
|
|
490
|
-
.sv-grid-has-vscroll .sv-grid-table tr > :last-child.sv-grid-cell[data-align="right"],
|
|
491
|
-
.sv-grid-has-vscroll .sv-grid-table tr > :last-child.sv-grid-column[data-align="right"] {
|
|
492
|
-
padding-right: calc(var(--sg-cell-px, 7px) + 16px);
|
|
493
|
-
}
|
|
494
|
-
.sv-grid-column[data-align="center"] .sv-grid-header-sort {
|
|
495
|
-
justify-content: center;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
.sv-grid-resize-handle {
|
|
499
|
-
position: absolute;
|
|
500
|
-
top: 0;
|
|
501
|
-
right: 0;
|
|
502
|
-
width: 5px;
|
|
503
|
-
height: 100%;
|
|
504
|
-
cursor: col-resize;
|
|
505
|
-
user-select: none;
|
|
506
|
-
z-index: 2;
|
|
507
|
-
transition: background-color 100ms ease;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/* An accent center pill (rather than tinting the whole strip): faint on column
|
|
511
|
-
hover, bright when the handle itself is hovered or while dragging. */
|
|
512
|
-
.sv-grid-resize-handle::after {
|
|
513
|
-
content: "";
|
|
514
|
-
position: absolute;
|
|
515
|
-
inset: 0;
|
|
516
|
-
margin: auto 2px;
|
|
517
|
-
width: 2px;
|
|
518
|
-
background: var(--sg-fg, #0f172a);
|
|
519
|
-
opacity: 0;
|
|
520
|
-
border-radius: 1px;
|
|
521
|
-
transition: background-color 100ms ease, opacity 100ms ease;
|
|
522
|
-
}
|
|
523
|
-
.sv-grid-column:hover .sv-grid-resize-handle::after {
|
|
524
|
-
opacity: 0.3;
|
|
525
|
-
}
|
|
526
|
-
.sv-grid-column:hover .sv-grid-resize-handle:hover::after,
|
|
527
|
-
.sv-grid-resize-handle.is-resizing::after {
|
|
528
|
-
background: var(--sg-accent, #2563eb);
|
|
529
|
-
opacity: 1;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/* Row resize handle (counterpart of the column resize handle). Lives
|
|
533
|
-
at the bottom of the row-header gutter cell; the consumer opts in
|
|
534
|
-
by attaching the `rowResize` action and setting
|
|
535
|
-
`cellClass: 'sv-row-gutter'` on the row-header column. */
|
|
536
|
-
.sv-grid-row-resize-handle {
|
|
537
|
-
/* Geometry + interactivity are also set inline by the rowResize
|
|
538
|
-
action so the strip works even if this stylesheet hasn't been
|
|
539
|
-
parsed yet. This rule only owns the hover/active tint. */
|
|
540
|
-
transition: background-color 100ms ease;
|
|
541
|
-
}
|
|
542
|
-
.sv-grid-row-resize-handle:hover,
|
|
543
|
-
.sv-grid-row-resize-handle.is-resizing {
|
|
544
|
-
background: color-mix(in srgb, var(--sg-accent, #0b63f3) 30%, transparent);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
.sv-grid-head {
|
|
548
|
-
position: sticky;
|
|
549
|
-
top: 0;
|
|
550
|
-
/* Above pinned body cells (z-index 30 via cellPinStyle). Without
|
|
551
|
-
this, when a row scrolls partially behind the sticky header,
|
|
552
|
-
its pinned cells (left + right) paint OVER the header while the
|
|
553
|
-
middle non-pinned cells are correctly hidden behind it, leaving
|
|
554
|
-
"floating" symbol / pnl cells at the top of the viewport. */
|
|
555
|
-
z-index: 35;
|
|
556
|
-
background: var(--sg-header-bg, #f5f7fb);
|
|
557
|
-
}
|
|
558
|
-
/* Column-header cells take the header background (and header foreground)
|
|
559
|
-
so the header row paints as a solid band rather than body-colored
|
|
560
|
-
cells over the sticky header strip. Together with the row-number
|
|
561
|
-
gutter below - which uses the same --sg-header-bg - the column headers
|
|
562
|
-
and row headers read as one continuous header chrome. */
|
|
563
|
-
.sv-grid-head .sv-grid-column {
|
|
564
|
-
background: var(--sg-header-bg, #f5f7fb);
|
|
565
|
-
/* Header typography is themeable so each design-system preset can match
|
|
566
|
-
its real grid header (e.g. Excel bold, Atlassian uppercase + subtle,
|
|
567
|
-
shadcn medium + muted). The label/sort-button inherit these. */
|
|
568
|
-
color: var(--sg-header-label-color, var(--sg-header-fg, var(--sg-fg, #0f172a)));
|
|
569
|
-
font-weight: var(--sg-header-weight, 600);
|
|
570
|
-
font-size: var(--sg-header-size, inherit);
|
|
571
|
-
text-transform: var(--sg-header-transform, none);
|
|
572
|
-
letter-spacing: var(--sg-header-tracking, normal);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
/* Header drag-to-reorder. The `draggable` attribute is only set
|
|
576
|
-
* when `enableColumnReorder` is true, so the cursor and drop
|
|
577
|
-
* indicators only show in that mode. */
|
|
578
|
-
.sv-grid-column[draggable="true"] { cursor: grab; }
|
|
579
|
-
.sv-grid-column[draggable="true"]:active { cursor: grabbing; }
|
|
580
|
-
.sv-grid-column.is-dragging { opacity: 0.55; }
|
|
581
|
-
.sv-grid-column.is-drag-target-before,
|
|
582
|
-
.sv-grid-column.is-drag-target-after {
|
|
583
|
-
position: relative;
|
|
584
|
-
}
|
|
585
|
-
.sv-grid-column.is-drag-target-before::before,
|
|
586
|
-
.sv-grid-column.is-drag-target-after::after {
|
|
587
|
-
content: "";
|
|
588
|
-
position: absolute; top: 4px; bottom: 4px; width: 3px;
|
|
589
|
-
background: linear-gradient(180deg, #6366f1, #8b5cf6);
|
|
590
|
-
border-radius: 2px;
|
|
591
|
-
box-shadow: 0 0 6px #6366f1;
|
|
592
|
-
pointer-events: none;
|
|
593
|
-
z-index: 7;
|
|
594
|
-
animation: sg-drop-pulse 700ms ease-in-out infinite alternate;
|
|
595
|
-
}
|
|
596
|
-
.sv-grid-column.is-drag-target-before::before { left: -2px; }
|
|
597
|
-
.sv-grid-column.is-drag-target-after::after { right: -2px; }
|
|
598
|
-
@keyframes sg-drop-pulse { from { opacity: 0.55; } to { opacity: 1; } }
|
|
599
|
-
|
|
600
|
-
/* Managed row dragging ----------------------------------------------------- */
|
|
601
|
-
/* The whole row is the drag source; the row-number cell shows a grip so the
|
|
602
|
-
* affordance is discoverable. Pair `rowDragManaged` with `showRowNumbers` for
|
|
603
|
-
* the grip; without row numbers the grab cursor still hints it. */
|
|
604
|
-
.sv-grid-row-draggable { cursor: grab; }
|
|
605
|
-
.sv-grid-row-draggable:active { cursor: grabbing; }
|
|
606
|
-
.sv-grid-row-draggable .sv-grid-row-number-cell {
|
|
607
|
-
position: relative;
|
|
608
|
-
padding-left: 14px;
|
|
609
|
-
}
|
|
610
|
-
.sv-grid-row-draggable .sv-grid-row-number-cell::before {
|
|
611
|
-
content: "\2807"; /* braille grip glyph */
|
|
612
|
-
position: absolute;
|
|
613
|
-
left: 3px;
|
|
614
|
-
top: 50%;
|
|
615
|
-
transform: translateY(-50%);
|
|
616
|
-
font-size: 13px;
|
|
617
|
-
line-height: 1;
|
|
618
|
-
color: var(--sg-muted, #94a3b8);
|
|
619
|
-
opacity: 0.4;
|
|
620
|
-
transition: opacity 120ms ease;
|
|
621
|
-
pointer-events: none;
|
|
622
|
-
}
|
|
623
|
-
.sv-grid-row-draggable:hover .sv-grid-row-number-cell::before { opacity: 1; }
|
|
624
|
-
|
|
625
|
-
/* Default highlight for an external drop zone (use:rowDropZone) while a
|
|
626
|
-
matching row is dragged over it. Consumers can override the class. */
|
|
627
|
-
.sv-grid-row-dropzone-over {
|
|
628
|
-
outline: 2px dashed var(--sg-accent, #6366f1);
|
|
629
|
-
outline-offset: -2px;
|
|
630
|
-
background: color-mix(in srgb, var(--sg-accent, #6366f1) 10%, transparent);
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
/* Drop indicator: a glowing line on the row's top (before) or bottom (after)
|
|
634
|
-
* edge. Painted on the cells so it spans the full row across table layouts. */
|
|
635
|
-
.sv-grid-row-drop-before > td {
|
|
636
|
-
box-shadow: inset 0 2px 0 0 var(--sg-accent, #6366f1);
|
|
637
|
-
}
|
|
638
|
-
.sv-grid-row-drop-after > td {
|
|
639
|
-
box-shadow: inset 0 -2px 0 0 var(--sg-accent, #6366f1);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
/* Pinned rows (top / bottom). Each cell sticks individually so the
|
|
643
|
-
* row tracks scroll on every browser. Top rows stick below the sticky
|
|
644
|
-
* thead via --sg-thead-h; bottom rows stick to the bottom of the
|
|
645
|
-
* scroll container. Tinted background distinguishes them from
|
|
646
|
-
* regular rows. */
|
|
647
|
-
.sv-grid-pinned-row td {
|
|
648
|
-
position: sticky;
|
|
649
|
-
z-index: 4;
|
|
650
|
-
background: var(--sg-pinned-bg, color-mix(in oklab, #6366f1 4%, #ffffff));
|
|
651
|
-
border-bottom: 1px solid var(--sg-pinned-border, color-mix(in oklab, #6366f1 24%, transparent));
|
|
652
|
-
font-weight: 600;
|
|
653
|
-
}
|
|
654
|
-
.sv-grid-pinned-row-top td { top: var(--sg-thead-h, 36px); }
|
|
655
|
-
.sv-grid-pinned-row-bottom td { bottom: 0; border-top: 1px solid var(--sg-pinned-border, color-mix(in oklab, #6366f1 24%, transparent)); }
|
|
656
|
-
/* Stack multiple pinned-top rows: row 1 stops at thead, row 2 stops
|
|
657
|
-
* at thead + 36px, etc. The component itself sets --sg-pinned-top-h
|
|
658
|
-
* to the current pinned-top stack height via $effect. */
|
|
659
|
-
.sv-grid-pinned-row-top[data-pinned-index="1"] td { top: calc(var(--sg-thead-h, 36px) + var(--sg-pinned-row-h, 32px)); }
|
|
660
|
-
.sv-grid-pinned-row-top[data-pinned-index="2"] td { top: calc(var(--sg-thead-h, 36px) + var(--sg-pinned-row-h, 32px) * 2); }
|
|
661
|
-
.sv-grid-pinned-row-top[data-pinned-index="3"] td { top: calc(var(--sg-thead-h, 36px) + var(--sg-pinned-row-h, 32px) * 3); }
|
|
662
|
-
.sv-grid-pinned-row-bottom[data-pinned-index="1"] td { bottom: var(--sg-pinned-row-h, 32px); }
|
|
663
|
-
.sv-grid-pinned-row-bottom[data-pinned-index="2"] td { bottom: calc(var(--sg-pinned-row-h, 32px) * 2); }
|
|
664
|
-
.sv-grid-pinned-row-bottom[data-pinned-index="3"] td { bottom: calc(var(--sg-pinned-row-h, 32px) * 3); }
|
|
665
|
-
/* Sticky-left columns keep their left position; just override their
|
|
666
|
-
* z so they sit above non-pinned pinned-row cells. */
|
|
667
|
-
.sv-grid-pinned-row td[data-pinned] { z-index: 5; }
|
|
668
|
-
|
|
669
|
-
/* Group header rows for multi-level (pivot-style) headers. Sits above
|
|
670
|
-
* the standard leaf header row when the column tree has nesting. */
|
|
671
|
-
.sv-grid-group-header-row {
|
|
672
|
-
background: var(--sg-header-bg, #eef2f8);
|
|
673
|
-
}
|
|
674
|
-
.sv-grid-group-header-cell {
|
|
675
|
-
box-sizing: border-box;
|
|
676
|
-
border-bottom: 1px solid var(--sg-border, #d8dee9);
|
|
677
|
-
border-right: 1px solid var(--sg-border, #e2e8f0);
|
|
678
|
-
text-align: center;
|
|
679
|
-
padding: 4px 8px;
|
|
680
|
-
font-weight: 700;
|
|
681
|
-
font-size: 11.5px;
|
|
682
|
-
color: var(--sg-header-fg, #475569);
|
|
683
|
-
text-transform: uppercase;
|
|
684
|
-
letter-spacing: 0.04em;
|
|
685
|
-
}
|
|
686
|
-
.sv-grid-group-header-placeholder {
|
|
687
|
-
background: transparent;
|
|
688
|
-
border-right-color: transparent;
|
|
689
|
-
}
|
|
690
|
-
.sv-grid-group-header-label {
|
|
691
|
-
display: inline-block;
|
|
692
|
-
max-width: 100%;
|
|
693
|
-
overflow: hidden;
|
|
694
|
-
text-overflow: ellipsis;
|
|
695
|
-
white-space: nowrap;
|
|
696
|
-
}
|
|
697
|
-
/* Collapse toggle on a collapsible column group header. */
|
|
698
|
-
.sv-grid-group-toggle {
|
|
699
|
-
display: inline-flex;
|
|
700
|
-
align-items: center;
|
|
701
|
-
gap: 5px;
|
|
702
|
-
max-width: 100%;
|
|
703
|
-
border: 0;
|
|
704
|
-
background: transparent;
|
|
705
|
-
color: inherit;
|
|
706
|
-
font: inherit;
|
|
707
|
-
letter-spacing: inherit;
|
|
708
|
-
text-transform: inherit;
|
|
709
|
-
cursor: pointer;
|
|
710
|
-
padding: 2px 6px;
|
|
711
|
-
border-radius: 5px;
|
|
712
|
-
}
|
|
713
|
-
.sv-grid-group-toggle:hover {
|
|
714
|
-
background: color-mix(in srgb, var(--sg-accent, #2563eb) 12%, transparent);
|
|
715
|
-
color: var(--sg-accent, #2563eb);
|
|
716
|
-
}
|
|
717
|
-
.sv-grid-group-caret {
|
|
718
|
-
flex-shrink: 0;
|
|
719
|
-
transition: transform 140ms ease;
|
|
720
|
-
}
|
|
721
|
-
/* Collapsed: point the caret right (like a closed disclosure). */
|
|
722
|
-
.sv-grid-group-toggle.is-collapsed .sv-grid-group-caret {
|
|
723
|
-
transform: rotate(-90deg);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
.sv-grid-foot {
|
|
727
|
-
position: sticky;
|
|
728
|
-
bottom: 0;
|
|
729
|
-
z-index: 6;
|
|
730
|
-
background: var(--sg-header-bg, #f5f7fb);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
/* Responsive (narrow) mode: smooth momentum touch panning, and let touch
|
|
734
|
-
gestures pan the grid freely (the wheel-trap contain stays for pointer). */
|
|
735
|
-
.sv-grid-container.sv-grid-narrow {
|
|
736
|
-
-webkit-overflow-scrolling: touch;
|
|
737
|
-
scroll-behavior: auto;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
.sv-grid-container {
|
|
741
|
-
scrollbar-width: auto;
|
|
742
|
-
-ms-overflow-style: auto;
|
|
743
|
-
/* Trap wheel scroll inside the grid. Without this, hitting the top
|
|
744
|
-
* or bottom edge of the grid lets the wheel event chain to the
|
|
745
|
-
* outer page (or any scrollable ancestor), so what feels like one
|
|
746
|
-
* wheel notch keeps scrolling something else - the "starts and
|
|
747
|
-
* doesn't stop" symptom. `contain` cancels the chained scroll
|
|
748
|
-
* but still bounces normally inside the grid. */
|
|
749
|
-
overscroll-behavior: contain;
|
|
750
|
-
/* Disable scroll anchoring. The virtualizer changes the top-spacer
|
|
751
|
-
* height every time the visible row window slides - modern browsers
|
|
752
|
-
* (default `overflow-anchor: auto`) react by quietly adjusting
|
|
753
|
-
* `scrollTop` to keep the visible content stable. That adjustment
|
|
754
|
-
* fires another `scroll` event, which fires `onBodyScroll`, which
|
|
755
|
-
* re-runs the virtualizer, which shifts the spacer again, which
|
|
756
|
-
* adjusts `scrollTop` again - a self-sustaining loop that
|
|
757
|
-
* presents as "the grid keeps scrolling after the wheel stops."
|
|
758
|
-
* Turning anchoring off lets the virtualizer own the scroll
|
|
759
|
-
* position outright. */
|
|
760
|
-
overflow-anchor: none;
|
|
761
|
-
/* Tell the browser that the sticky thead occupies the top
|
|
762
|
-
`--sg-thead-h` pixels of the scroll container. `scrollIntoView`
|
|
763
|
-
and friends use this to avoid scrolling a row underneath the
|
|
764
|
-
sticky header - critical for Excel-style keyboard nav. */
|
|
765
|
-
scroll-padding-top: var(--sg-thead-h, 36px);
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
.sv-grid-container.sv-grid-container-custom-scrollbars {
|
|
769
|
-
scrollbar-width: none;
|
|
770
|
-
-ms-overflow-style: none;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
.sv-grid-container.sv-grid-container-custom-scrollbars::-webkit-scrollbar {
|
|
774
|
-
display: none;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
.sv-grid-scrollbar {
|
|
778
|
-
position: absolute;
|
|
779
|
-
/* Above pinned columns (z-index 30 via cellPinStyle) so a frozen
|
|
780
|
-
left/right column never paints over the custom scrollbar - that
|
|
781
|
-
was the bug behind "vertical scrollbar hidden under the frozen
|
|
782
|
-
column" in grids with a pinned column at the right edge. */
|
|
783
|
-
z-index: 40;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
/* Scrollbars anchor to the inline-end edge so RTL flips them to the
|
|
787
|
-
* left automatically. `inset-inline-end` resolves to `right` in LTR
|
|
788
|
-
* and `left` in RTL, no manual override needed. */
|
|
789
|
-
.sv-grid-scrollbar-vertical {
|
|
790
|
-
top: 0;
|
|
791
|
-
inset-inline-end: 0;
|
|
792
|
-
width: 16px;
|
|
793
|
-
height: calc(100% - 16px);
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
.sv-grid-scrollbar-corner {
|
|
797
|
-
position: absolute;
|
|
798
|
-
top: 0;
|
|
799
|
-
inset-inline-end: 0;
|
|
800
|
-
width: 16px;
|
|
801
|
-
background: var(--sg-header-bg, #f5f7fb);
|
|
802
|
-
border-bottom: 1px solid rgba(15, 23, 42, 0.08);
|
|
803
|
-
/* Above pinned header cells (z-index 30) so the gap above the
|
|
804
|
-
vertical scrollbar stays clean over a frozen column. */
|
|
805
|
-
z-index: 41;
|
|
806
|
-
pointer-events: none;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
.sv-grid-scrollbar-corner-br {
|
|
810
|
-
position: absolute;
|
|
811
|
-
inset-inline-end: 0;
|
|
812
|
-
bottom: 0;
|
|
813
|
-
width: 16px;
|
|
814
|
-
height: 16px;
|
|
815
|
-
background: var(--sg-header-bg, #eef2f8);
|
|
816
|
-
box-shadow: inset 0 0 0 1px rgba(15, 23, 42, 0.05);
|
|
817
|
-
z-index: 42;
|
|
818
|
-
pointer-events: none;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
.sv-grid-scrollbar-horizontal {
|
|
822
|
-
inset-inline-start: 0;
|
|
823
|
-
bottom: 0;
|
|
824
|
-
width: calc(100% - 16px);
|
|
825
|
-
height: 16px;
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
.sv-grid-selection-column,
|
|
829
|
-
.sv-grid-selection-cell {
|
|
830
|
-
text-align: center;
|
|
831
|
-
padding-inline: 6px;
|
|
832
|
-
position: sticky;
|
|
833
|
-
z-index: 4;
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
/* Row-number column: sticks to the left edge of the scroll viewport. */
|
|
837
|
-
.sv-grid-row-number-column,
|
|
838
|
-
.sv-grid-row-number-cell {
|
|
839
|
-
text-align: right;
|
|
840
|
-
padding-inline: 8px;
|
|
841
|
-
position: sticky;
|
|
842
|
-
left: 0;
|
|
843
|
-
z-index: 4;
|
|
844
|
-
/* Match the column-header band so the row-number gutter and the column
|
|
845
|
-
headers form one continuous header chrome. */
|
|
846
|
-
background: var(--sg-header-bg, #f5f7fb);
|
|
847
|
-
color: var(--sg-muted, #64748b);
|
|
848
|
-
font-variant-numeric: tabular-nums;
|
|
849
|
-
}
|
|
850
|
-
.sv-grid-row-number-head {
|
|
851
|
-
color: inherit;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
.sv-grid-filter-row-control {
|
|
855
|
-
display: flex;
|
|
856
|
-
flex-direction: row;
|
|
857
|
-
align-items: center;
|
|
858
|
-
gap: 4px;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
.sv-grid-filter-operator {
|
|
862
|
-
flex: 2 1 0;
|
|
863
|
-
min-width: 0;
|
|
864
|
-
box-sizing: border-box;
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
.sv-grid-filter-value {
|
|
868
|
-
flex: 3 1 0;
|
|
869
|
-
min-width: 0;
|
|
870
|
-
box-sizing: border-box;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
/* Brief glow used when the user clicks the funnel icon while the grid
|
|
874
|
-
is in row-filter mode - draws attention to where the input is so
|
|
875
|
-
the click isn't a no-op. */
|
|
876
|
-
.sv-grid-filter-value.sv-grid-filter-value-pulse {
|
|
877
|
-
animation: sv-grid-filter-pulse 700ms ease-out;
|
|
878
|
-
}
|
|
879
|
-
@keyframes sv-grid-filter-pulse {
|
|
880
|
-
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--sg-accent, #0b63f3) 55%, transparent); }
|
|
881
|
-
70% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--sg-accent, #0b63f3) 0%, transparent); }
|
|
882
|
-
100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--sg-accent, #0b63f3) 0%, transparent); }
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
.sv-grid-checkbox {
|
|
886
|
-
position: relative;
|
|
887
|
-
display: inline-flex;
|
|
888
|
-
align-items: center;
|
|
889
|
-
justify-content: center;
|
|
890
|
-
width: 16px;
|
|
891
|
-
height: 16px;
|
|
892
|
-
margin: 0 auto;
|
|
893
|
-
border: 1px solid var(--sg-input-border, #8794a8);
|
|
894
|
-
border-radius: 4px;
|
|
895
|
-
background: var(--sg-input-bg, #fff);
|
|
896
|
-
color: var(--sg-accent, #0b63f3);
|
|
897
|
-
cursor: pointer;
|
|
898
|
-
transition:
|
|
899
|
-
border-color 120ms ease,
|
|
900
|
-
background-color 120ms ease;
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
/* Full-row editor: inline editors shown for every editable cell of a row
|
|
904
|
-
in full-row edit. Fill the cell so the row reads as a form line. */
|
|
905
|
-
.sv-grid-fr-editor {
|
|
906
|
-
width: 100%;
|
|
907
|
-
box-sizing: border-box;
|
|
908
|
-
}
|
|
909
|
-
select.sv-grid-fr-editor {
|
|
910
|
-
height: 100%;
|
|
911
|
-
min-height: 26px;
|
|
912
|
-
}
|
|
913
|
-
.sv-grid-fr-checkbox {
|
|
914
|
-
width: 16px;
|
|
915
|
-
height: 16px;
|
|
916
|
-
accent-color: var(--sg-accent, #3b82f6);
|
|
917
|
-
cursor: pointer;
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
/* Color editor - native HTML color input filling the cell so the
|
|
921
|
-
swatch is clickable across the whole cell area, not just the tiny
|
|
922
|
-
OS default size. */
|
|
923
|
-
.sv-grid-cell-editor-color {
|
|
924
|
-
width: 100%;
|
|
925
|
-
height: 100%;
|
|
926
|
-
padding: 2px;
|
|
927
|
-
border: 0;
|
|
928
|
-
background: transparent;
|
|
929
|
-
cursor: pointer;
|
|
930
|
-
}
|
|
931
|
-
.sv-grid-cell-editor-color::-webkit-color-swatch-wrapper { padding: 0; }
|
|
932
|
-
.sv-grid-cell-editor-color::-webkit-color-swatch { border: 0; border-radius: 4px; }
|
|
933
|
-
.sv-grid-cell-editor-color::-moz-color-swatch { border: 0; border-radius: 4px; }
|
|
934
|
-
|
|
935
|
-
/* Rating editor - 5 clickable stars + clear button. Renders inline
|
|
936
|
-
in the cell when editorType="rating"; the readonly view renders a
|
|
937
|
-
filled-star count via the default text path (or a snippet cell). */
|
|
938
|
-
.sv-grid-rating-editor {
|
|
939
|
-
display: inline-flex;
|
|
940
|
-
align-items: center;
|
|
941
|
-
gap: 1px;
|
|
942
|
-
height: 100%;
|
|
943
|
-
}
|
|
944
|
-
.sv-grid-rating-star {
|
|
945
|
-
border: 0; background: transparent; cursor: pointer;
|
|
946
|
-
color: var(--sg-rating-empty, #cbd5e1);
|
|
947
|
-
font-size: 18px; line-height: 1;
|
|
948
|
-
padding: 0 1px;
|
|
949
|
-
transition: color 80ms ease, transform 80ms ease;
|
|
950
|
-
}
|
|
951
|
-
.sv-grid-rating-star-on { color: var(--sg-rating-on, #f59e0b); }
|
|
952
|
-
.sv-grid-rating-star:hover { color: var(--sg-rating-hover, #fbbf24); transform: scale(1.12); }
|
|
953
|
-
.sv-grid-rating-clear {
|
|
954
|
-
margin-left: 4px;
|
|
955
|
-
border: 0; background: transparent;
|
|
956
|
-
color: var(--sg-muted, #94a3b8);
|
|
957
|
-
cursor: pointer; font-size: 14px;
|
|
958
|
-
}
|
|
959
|
-
.sv-grid-rating-clear:hover { color: var(--sg-fg, #0f172a); }
|
|
960
|
-
|
|
961
|
-
.sv-grid-checkbox[aria-checked="true"],
|
|
962
|
-
.sv-grid-checkbox[aria-checked="mixed"] {
|
|
963
|
-
border-color: var(--sg-accent, #2563eb);
|
|
964
|
-
background: var(--sg-accent, #2563eb);
|
|
965
|
-
color: var(--sg-on-accent, #fff);
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
.sv-grid-checkbox[aria-checked="true"]::after {
|
|
969
|
-
content: "";
|
|
970
|
-
width: 5px;
|
|
971
|
-
height: 9px;
|
|
972
|
-
border-right: 2px solid currentColor;
|
|
973
|
-
border-bottom: 2px solid currentColor;
|
|
974
|
-
transform: rotate(40deg) translate(-1px, -1px);
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
.sv-grid-checkbox[aria-checked="mixed"]::after {
|
|
978
|
-
content: "";
|
|
979
|
-
width: 8px;
|
|
980
|
-
height: 2px;
|
|
981
|
-
border-radius: 1px;
|
|
982
|
-
background: currentColor;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
.sv-grid-checkbox:focus-visible {
|
|
986
|
-
outline: 2px solid var(--sg-accent, #0b63f3);
|
|
987
|
-
outline-offset: 1px;
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
.sv-grid-checkbox-readonly {
|
|
991
|
-
cursor: default;
|
|
992
|
-
pointer-events: none;
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
/* Built-in cell flash (ColumnDef `cellFlash`). Theme-tinted fade; consumers
|
|
996
|
-
can pass their own class (e.g. up/down colouring) via `cellFlash.className`. */
|
|
997
|
-
@keyframes sv-grid-cell-flash-kf {
|
|
998
|
-
0% { background-color: var(--sg-cell-flash, color-mix(in oklab, var(--sg-accent, #3b82f6) 42%, transparent)); }
|
|
999
|
-
100% { background-color: transparent; }
|
|
1000
|
-
}
|
|
1001
|
-
.sv-grid-cell-flash {
|
|
1002
|
-
animation: sv-grid-cell-flash-kf 0.7s ease-out;
|
|
1003
|
-
}
|
|
1004
|
-
@media (prefers-reduced-motion: reduce) {
|
|
1005
|
-
.sv-grid-cell-flash { animation: none; }
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
.sv-grid-cell-active,
|
|
1009
|
-
.sv-grid-cell-editing {
|
|
1010
|
-
position: relative;
|
|
1011
|
-
/* High enough to sit above pinned-column stacking contexts (z=4) so the
|
|
1012
|
-
list/chips popover overlays adjacent cells instead of being clipped. */
|
|
1013
|
-
z-index: 20;
|
|
1014
|
-
/* `box-shadow: inset` is bound to the cell's own box, so the ring
|
|
1015
|
-
clips cleanly with the cell when it's partially scrolled out of
|
|
1016
|
-
view (and shows up consistently at the right edge of a virtualized
|
|
1017
|
-
window). The previous `outline + outline-offset: -2px` rendered at
|
|
1018
|
-
the wrong position on clipped cells in some browsers - it's drawn
|
|
1019
|
-
outside the layout box, so the negative offset's effect was
|
|
1020
|
-
inconsistent at scroll boundaries. */
|
|
1021
|
-
box-shadow: inset 0 0 0 2px var(--sg-accent, #0b63f3);
|
|
1022
|
-
outline: none;
|
|
1023
|
-
/* Let list/chips popouts extend past the cell box (default cells have
|
|
1024
|
-
overflow: hidden so the chips picker would otherwise be clipped). */
|
|
1025
|
-
overflow: visible;
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
/* Excel-style fill handle: a 7×7 blue square anchored to the bottom-
|
|
1029
|
-
right corner of the active cell or selection range. The parent
|
|
1030
|
-
`.sv-grid-cell` is `position: relative` (sticky cells override
|
|
1031
|
-
that with sticky - which is also a positioning context), so the
|
|
1032
|
-
handle anchors to the cell box. */
|
|
1033
|
-
/* Excel-style per-cell note indicator. A small triangle in the top-
|
|
1034
|
-
* right corner that, when hovered, shows the note tooltip. The
|
|
1035
|
-
* triangle IS the hot-zone (12×12 square clipped to a triangle via
|
|
1036
|
-
* border tricks); a transparent invisible square on top widens the
|
|
1037
|
-
* hit target a little so it's not painful to hit. */
|
|
1038
|
-
.sv-grid-cell-note-corner {
|
|
1039
|
-
position: absolute;
|
|
1040
|
-
top: 0; right: 0;
|
|
1041
|
-
width: 12px; height: 12px;
|
|
1042
|
-
/* Triangle: 0,0 → 12,0 → 12,12 */
|
|
1043
|
-
background:
|
|
1044
|
-
linear-gradient(45deg, transparent 50%, var(--sg-note-corner, #f59e0b) 50%);
|
|
1045
|
-
cursor: help;
|
|
1046
|
-
z-index: 6;
|
|
1047
|
-
}
|
|
1048
|
-
/* Slightly larger invisible hit area so users don't have to land on
|
|
1049
|
-
a 6×6 pixel triangle. */
|
|
1050
|
-
.sv-grid-cell-note-corner::before {
|
|
1051
|
-
content: '';
|
|
1052
|
-
position: absolute;
|
|
1053
|
-
inset: -3px -3px 0 0;
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
.sv-grid-fill-handle {
|
|
1057
|
-
position: absolute;
|
|
1058
|
-
/* Sit fully INSIDE the cell box so `overflow: hidden` doesn't clip
|
|
1059
|
-
most of the handle. Previously the handle stuck 3px outside the
|
|
1060
|
-
cell and the visible 4×4 corner was nearly unclickable - especially
|
|
1061
|
-
on chip cells where pills filled the bottom edge. */
|
|
1062
|
-
bottom: 0;
|
|
1063
|
-
right: 0;
|
|
1064
|
-
width: 9px;
|
|
1065
|
-
height: 9px;
|
|
1066
|
-
background: var(--sg-accent, #0b63f3);
|
|
1067
|
-
border: 1px solid var(--sg-bg, #ffffff);
|
|
1068
|
-
box-sizing: border-box;
|
|
1069
|
-
cursor: crosshair;
|
|
1070
|
-
z-index: 15;
|
|
1071
|
-
touch-action: none;
|
|
1072
|
-
}
|
|
1073
|
-
.sv-grid-fill-handle:hover {
|
|
1074
|
-
transform: scale(1.3);
|
|
1075
|
-
transform-origin: bottom right;
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
/* Excel-style fill-handle drag marquee: ONE thin dashed rectangle around the
|
|
1079
|
-
whole target range (source + fill-into area), not a thick per-cell border or
|
|
1080
|
-
fill tint. Drawn as four background-image layers on the boundary cells - no
|
|
1081
|
-
`border`, so it paints on the compositor with zero reflow while dragging.
|
|
1082
|
-
`--sg-fill-marquee` overrides the colour (defaults to a theme-aware grey). */
|
|
1083
|
-
/* Step 1 (LOW specificity): default every edge layer to `none`, then light up
|
|
1084
|
-
the edges this cell sits on. These are custom properties, which the
|
|
1085
|
-
`background:` shorthands on selected / zebra cells do NOT reset - so they can
|
|
1086
|
-
safely live at low specificity and just compete with each other by source
|
|
1087
|
-
order (gradient rules come after the defaults, so they win). */
|
|
1088
|
-
.sv-grid-cell[data-fill-top="true"],
|
|
1089
|
-
.sv-grid-cell[data-fill-bottom="true"],
|
|
1090
|
-
.sv-grid-cell[data-fill-left="true"],
|
|
1091
|
-
.sv-grid-cell[data-fill-right="true"] {
|
|
1092
|
-
--sv-fill-top: none;
|
|
1093
|
-
--sv-fill-bottom: none;
|
|
1094
|
-
--sv-fill-left: none;
|
|
1095
|
-
--sv-fill-right: none;
|
|
1096
|
-
/* Match the selection colour (the grid accent) by default. */
|
|
1097
|
-
--sv-fill-color: var(--sg-fill-marquee, var(--sg-accent, #2563eb));
|
|
1098
|
-
}
|
|
1099
|
-
.sv-grid-cell[data-fill-top="true"] {
|
|
1100
|
-
--sv-fill-top: repeating-linear-gradient(to right, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1101
|
-
}
|
|
1102
|
-
.sv-grid-cell[data-fill-bottom="true"] {
|
|
1103
|
-
--sv-fill-bottom: repeating-linear-gradient(to right, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1104
|
-
}
|
|
1105
|
-
.sv-grid-cell[data-fill-left="true"] {
|
|
1106
|
-
--sv-fill-left: repeating-linear-gradient(to bottom, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1107
|
-
}
|
|
1108
|
-
.sv-grid-cell[data-fill-right="true"] {
|
|
1109
|
-
--sv-fill-right: repeating-linear-gradient(to bottom, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1110
|
-
}
|
|
1111
|
-
/* Step 2 (HIGH specificity, `.sv-grid-table` prefix): compose the resolved edge
|
|
1112
|
-
layers into `background-image` so it beats the `background:` shorthands on
|
|
1113
|
-
selected / zebra / hover cells (which would otherwise reset background-image).
|
|
1114
|
-
The cell keeps its background-COLOR; this only adds the dashed lines on top. */
|
|
1115
|
-
.sv-grid-table .sv-grid-cell[data-fill-top="true"],
|
|
1116
|
-
.sv-grid-table .sv-grid-cell[data-fill-bottom="true"],
|
|
1117
|
-
.sv-grid-table .sv-grid-cell[data-fill-left="true"],
|
|
1118
|
-
.sv-grid-table .sv-grid-cell[data-fill-right="true"] {
|
|
1119
|
-
background-image: var(--sv-fill-top), var(--sv-fill-bottom),
|
|
1120
|
-
var(--sv-fill-left), var(--sv-fill-right);
|
|
1121
|
-
background-repeat: no-repeat;
|
|
1122
|
-
background-size: 100% 2px, 100% 2px, 2px 100%, 2px 100%;
|
|
1123
|
-
background-position: top left, bottom left, top left, top right;
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
.sv-grid-cell-editing {
|
|
1127
|
-
padding: 0;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
/* The four CSS variables let us compose a single box-shadow that draws the
|
|
1131
|
-
selection rectangle outline only on the cells that sit on its edges. */
|
|
1132
|
-
.sv-grid-cell[data-selected-range="true"] {
|
|
1133
|
-
background: var(--sg-selection-bg, #eef4ff);
|
|
1134
|
-
--sv-range-top: 0 0 0 transparent;
|
|
1135
|
-
--sv-range-bottom: 0 0 0 transparent;
|
|
1136
|
-
--sv-range-left: 0 0 0 transparent;
|
|
1137
|
-
--sv-range-right: 0 0 0 transparent;
|
|
1138
|
-
box-shadow: var(--sv-range-top), var(--sv-range-bottom),
|
|
1139
|
-
var(--sv-range-left), var(--sv-range-right);
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
/* Selection-range edges as inset box-shadows. We tried `border-*: 2px
|
|
1143
|
-
solid` to dodge the 1px-notch artifact at cell boundaries, but
|
|
1144
|
-
border width changes force a full table reflow on every selection
|
|
1145
|
-
change - catastrophic during keyboard nav (every arrow key pressed
|
|
1146
|
-
re-flows ~30 visible rows + their neighbours under border-collapse).
|
|
1147
|
-
Box-shadow inset paints on the compositor only - no reflow. */
|
|
1148
|
-
.sv-grid-cell[data-range-top="true"] {
|
|
1149
|
-
--sv-range-top: inset 0 2px 0 var(--sg-accent, #0b63f3);
|
|
1150
|
-
}
|
|
1151
|
-
.sv-grid-cell[data-range-bottom="true"] {
|
|
1152
|
-
--sv-range-bottom: inset 0 -2px 0 var(--sg-accent, #0b63f3);
|
|
1153
|
-
}
|
|
1154
|
-
.sv-grid-cell[data-range-left="true"] {
|
|
1155
|
-
--sv-range-left: inset 2px 0 0 var(--sg-accent, #0b63f3);
|
|
1156
|
-
}
|
|
1157
|
-
.sv-grid-cell[data-range-right="true"] {
|
|
1158
|
-
--sv-range-right: inset -2px 0 0 var(--sg-accent, #0b63f3);
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
/* Lift edge cells above their neighbours so the inset shadow renders
|
|
1162
|
-
above adjacent cells' (collapsed) borders rather than under them. */
|
|
1163
|
-
.sv-grid-cell[data-range-top="true"],
|
|
1164
|
-
.sv-grid-cell[data-range-bottom="true"],
|
|
1165
|
-
.sv-grid-cell[data-range-left="true"],
|
|
1166
|
-
.sv-grid-cell[data-range-right="true"],
|
|
1167
|
-
.sv-grid-cell.sv-merge-edge-right,
|
|
1168
|
-
.sv-grid-cell.sv-merge-edge-bottom {
|
|
1169
|
-
position: relative;
|
|
1170
|
-
z-index: 3;
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
/* Inherited selection-range edges from `spreadsheetLayout` merges.
|
|
1174
|
-
Same inset-shadow technique - no layout impact when toggling. */
|
|
1175
|
-
.sv-grid-cell.sv-merge-edge-right {
|
|
1176
|
-
box-shadow: inset -2px 0 0 var(--sg-accent, #0b63f3);
|
|
1177
|
-
}
|
|
1178
|
-
.sv-grid-cell.sv-merge-edge-bottom {
|
|
1179
|
-
box-shadow: inset 0 -2px 0 var(--sg-accent, #0b63f3);
|
|
1180
|
-
}
|
|
1181
|
-
.sv-grid-cell.sv-merge-in-range {
|
|
1182
|
-
background: var(--sg-selection-bg, #eef4ff);
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
.sv-grid-cell-editor {
|
|
1186
|
-
position: absolute;
|
|
1187
|
-
inset: 0;
|
|
1188
|
-
width: 100%;
|
|
1189
|
-
height: 100%;
|
|
1190
|
-
border: 0;
|
|
1191
|
-
outline: none;
|
|
1192
|
-
background: var(--sg-input-bg, #fff);
|
|
1193
|
-
box-sizing: border-box;
|
|
1194
|
-
font: inherit;
|
|
1195
|
-
color: inherit;
|
|
1196
|
-
padding: 0 8px;
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
/* Base look for the filter inputs (header filter row, per-column filter, the
|
|
1200
|
-
global search box, and the menu's search / condition inputs). The inline cell
|
|
1201
|
-
editor is styled separately - it's borderless and fills its cell. Shipping
|
|
1202
|
-
this with the grid means filters look like real inputs with no host
|
|
1203
|
-
stylesheet. */
|
|
1204
|
-
.sv-grid-column-filter,
|
|
1205
|
-
.sv-grid-filter-value,
|
|
1206
|
-
.sv-grid-global-filter input,
|
|
1207
|
-
.sv-grid-menu-search,
|
|
1208
|
-
.sv-grid-menu-condition-value {
|
|
1209
|
-
background: var(--sg-input-bg, #fff);
|
|
1210
|
-
color: var(--sg-fg, #0f172a);
|
|
1211
|
-
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1212
|
-
border-radius: 5px;
|
|
1213
|
-
padding: 0 8px;
|
|
1214
|
-
box-sizing: border-box;
|
|
1215
|
-
font: inherit;
|
|
1216
|
-
}
|
|
1217
|
-
.sv-grid-column-filter {
|
|
1218
|
-
height: 28px;
|
|
1219
|
-
width: 100%;
|
|
1220
|
-
font-size: 0.8125rem;
|
|
1221
|
-
}
|
|
1222
|
-
.sv-grid-column-filter::placeholder,
|
|
1223
|
-
.sv-grid-filter-value::placeholder,
|
|
1224
|
-
.sv-grid-menu-search::placeholder,
|
|
1225
|
-
.sv-grid-global-filter input::placeholder,
|
|
1226
|
-
.sv-grid-cell-editor::placeholder {
|
|
1227
|
-
color: var(--sg-muted, #64748b);
|
|
1228
|
-
opacity: 0.7;
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
/* Calm, theme-aware focus (a soft box-shadow rather than a hard outline). */
|
|
1232
|
-
.sv-grid-global-filter input:focus,
|
|
1233
|
-
.sv-grid-filter-value:focus,
|
|
1234
|
-
.sv-grid-column-filter:focus,
|
|
1235
|
-
.sv-grid-menu-search:focus,
|
|
1236
|
-
.sv-grid-menu-condition-value:focus,
|
|
1237
|
-
.sv-grid-menu-operator:focus,
|
|
1238
|
-
.sv-grid-cell-editor:focus {
|
|
1239
|
-
outline: none;
|
|
1240
|
-
border-color: var(--sg-accent, #2563eb);
|
|
1241
|
-
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 30%, transparent);
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
.sv-grid-cell-editor-number,
|
|
1245
|
-
.sv-grid-cell-editor-date,
|
|
1246
|
-
.sv-grid-cell-editor-datetime {
|
|
1247
|
-
width: 100%;
|
|
1248
|
-
height: 100%;
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
/* List editor: native <select>. For multi-select it grows into a
|
|
1252
|
-
small popup-style listbox that hangs over the cell. */
|
|
1253
|
-
.sv-grid-cell-editor-list {
|
|
1254
|
-
width: 100%;
|
|
1255
|
-
height: 100%;
|
|
1256
|
-
appearance: auto;
|
|
1257
|
-
font: inherit;
|
|
1258
|
-
color: inherit;
|
|
1259
|
-
background: var(--sg-input-bg, #fff);
|
|
1260
|
-
border: 0;
|
|
1261
|
-
padding: 0 6px;
|
|
1262
|
-
box-sizing: border-box;
|
|
1263
|
-
}
|
|
1264
|
-
.sv-grid-cell-editor-list[multiple] {
|
|
1265
|
-
position: absolute;
|
|
1266
|
-
top: 0;
|
|
1267
|
-
left: 0;
|
|
1268
|
-
right: 0;
|
|
1269
|
-
height: auto;
|
|
1270
|
-
min-height: 100%;
|
|
1271
|
-
max-height: 220px;
|
|
1272
|
-
z-index: 5;
|
|
1273
|
-
padding: 4px;
|
|
1274
|
-
border: 1px solid var(--sg-accent, #0b63f3);
|
|
1275
|
-
background: var(--sg-input-bg, #fff);
|
|
1276
|
-
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
/* Free-form chips editor (multi-value cell with removable tokens).
|
|
1280
|
-
Absolutely positioned so it can grow downward past the row without
|
|
1281
|
-
pushing other rows. Theme-aware background + accent border + drop
|
|
1282
|
-
shadow so it reads as a popout above the next row, not as part of
|
|
1283
|
-
it. The cell's outer outline is suppressed for this editor - this
|
|
1284
|
-
popout already provides its own border. */
|
|
1285
|
-
.sv-grid-cell-editor-chips {
|
|
1286
|
-
position: absolute;
|
|
1287
|
-
top: -1px;
|
|
1288
|
-
left: -1px;
|
|
1289
|
-
right: -1px;
|
|
1290
|
-
min-height: calc(100% + 2px);
|
|
1291
|
-
background: var(--sg-header-bg, var(--sg-bg, #ffffff));
|
|
1292
|
-
color: var(--sg-fg, #0f172a);
|
|
1293
|
-
padding: 4px 6px;
|
|
1294
|
-
box-sizing: border-box;
|
|
1295
|
-
z-index: 1000;
|
|
1296
|
-
overflow: visible;
|
|
1297
|
-
display: flex;
|
|
1298
|
-
align-items: flex-start;
|
|
1299
|
-
border: 1px solid var(--sg-accent, #2563eb);
|
|
1300
|
-
border-radius: var(--sg-radius, 6px);
|
|
1301
|
-
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45);
|
|
1302
|
-
}
|
|
1303
|
-
/* When the chips editor is mounted, the parent .sv-grid-cell-editing
|
|
1304
|
-
no longer needs its own outline - the popout already shows one. */
|
|
1305
|
-
.sv-grid-cell-editing:has(.sv-grid-cell-editor-chips) {
|
|
1306
|
-
outline: none;
|
|
1307
|
-
}
|
|
1308
|
-
.sv-grid-chips-row {
|
|
1309
|
-
display: flex;
|
|
1310
|
-
flex-wrap: wrap;
|
|
1311
|
-
align-items: center;
|
|
1312
|
-
gap: 4px;
|
|
1313
|
-
width: 100%;
|
|
1314
|
-
}
|
|
1315
|
-
.sv-grid-chip-input {
|
|
1316
|
-
flex: 1 1 80px;
|
|
1317
|
-
min-width: 60px;
|
|
1318
|
-
border: 0;
|
|
1319
|
-
outline: none;
|
|
1320
|
-
background: transparent;
|
|
1321
|
-
font: inherit;
|
|
1322
|
-
color: inherit;
|
|
1323
|
-
padding: 2px 4px;
|
|
1324
|
-
}
|
|
1325
|
-
.sv-grid-chip-picker {
|
|
1326
|
-
flex: 1 1 100px;
|
|
1327
|
-
min-width: 80px;
|
|
1328
|
-
font: inherit;
|
|
1329
|
-
color: inherit;
|
|
1330
|
-
background: var(--sg-bg, #fff);
|
|
1331
|
-
border: 1px solid rgba(15, 23, 42, 0.15);
|
|
1332
|
-
border-radius: 4px;
|
|
1333
|
-
padding: 2px 4px;
|
|
1334
|
-
}
|
|
1335
|
-
.sv-grid-chip-commit {
|
|
1336
|
-
font-size: 11px;
|
|
1337
|
-
text-transform: uppercase;
|
|
1338
|
-
letter-spacing: 0.04em;
|
|
1339
|
-
color: #fff;
|
|
1340
|
-
background: var(--sg-accent, #0b63f3);
|
|
1341
|
-
border: 0;
|
|
1342
|
-
border-radius: 4px;
|
|
1343
|
-
padding: 3px 8px;
|
|
1344
|
-
cursor: pointer;
|
|
1345
|
-
}
|
|
1346
|
-
.sv-grid-chip-commit:hover {
|
|
1347
|
-
filter: brightness(1.1);
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
/* Chip badge - used both in readonly cell display and inside the
|
|
1351
|
-
chips editor. Subtle neutral chrome, scales to the row's font size.
|
|
1352
|
-
Readonly chip rows stay on one line (the parent <td> has
|
|
1353
|
-
overflow: hidden, so overflowing chips get clipped - widen the
|
|
1354
|
-
column or open the editor to see all). */
|
|
1355
|
-
.sv-grid-chips-display {
|
|
1356
|
-
display: inline-flex;
|
|
1357
|
-
flex-wrap: nowrap;
|
|
1358
|
-
align-items: center;
|
|
1359
|
-
gap: 4px;
|
|
1360
|
-
max-width: 100%;
|
|
1361
|
-
vertical-align: middle;
|
|
1362
|
-
}
|
|
1363
|
-
.sv-grid-sparkline {
|
|
1364
|
-
display: inline-block;
|
|
1365
|
-
vertical-align: middle;
|
|
1366
|
-
overflow: visible;
|
|
1367
|
-
}
|
|
1368
|
-
/* Conditional formatting overlays - behind the cell text. */
|
|
1369
|
-
.sv-grid-cf-bg {
|
|
1370
|
-
position: absolute;
|
|
1371
|
-
inset: 0;
|
|
1372
|
-
z-index: 0;
|
|
1373
|
-
pointer-events: none;
|
|
1374
|
-
}
|
|
1375
|
-
.sv-grid-cf-bar {
|
|
1376
|
-
position: absolute;
|
|
1377
|
-
left: 0;
|
|
1378
|
-
top: 0;
|
|
1379
|
-
bottom: 0;
|
|
1380
|
-
z-index: 0;
|
|
1381
|
-
opacity: 0.85;
|
|
1382
|
-
border-radius: 0 2px 2px 0;
|
|
1383
|
-
pointer-events: none;
|
|
1384
|
-
}
|
|
1385
|
-
.sv-grid-cell-cf {
|
|
1386
|
-
position: relative;
|
|
1387
|
-
}
|
|
1388
|
-
/* Declarative cell validation (column `validate` hook). Invalid cells get a
|
|
1389
|
-
* soft red wash and a red inset ring - Handsontable's `htInvalid` look. The
|
|
1390
|
-
* value keeps rendering as-is; the message (if any) shows as the tooltip. */
|
|
1391
|
-
.sv-grid-cell-invalid {
|
|
1392
|
-
background-color: var(--sg-invalid-bg, rgba(239, 68, 68, 0.14)) !important;
|
|
1393
|
-
box-shadow: inset 0 0 0 1px var(--sg-invalid-border, rgba(239, 68, 68, 0.45));
|
|
1394
|
-
color: var(--sg-invalid-fg, #b91c1c);
|
|
1395
|
-
}
|
|
1396
|
-
.sv-grid-cell-invalid.sv-grid-cell-active {
|
|
1397
|
-
box-shadow: inset 0 0 0 2px var(--sg-invalid-border, rgba(239, 68, 68, 0.7));
|
|
1398
|
-
}
|
|
1399
|
-
.sv-grid-status-bar {
|
|
1400
|
-
display: flex;
|
|
1401
|
-
flex-wrap: wrap;
|
|
1402
|
-
align-items: center;
|
|
1403
|
-
gap: 18px;
|
|
1404
|
-
padding: 6px 14px;
|
|
1405
|
-
font-size: 12px;
|
|
1406
|
-
color: var(--sg-fg);
|
|
1407
|
-
background: var(--sg-header-bg, #f1f5f9);
|
|
1408
|
-
border: 1px solid var(--sg-border, #e2e8f0);
|
|
1409
|
-
border-top: 0;
|
|
1410
|
-
flex-shrink: 0;
|
|
1411
|
-
}
|
|
1412
|
-
.sv-grid-status-item {
|
|
1413
|
-
font-variant-numeric: tabular-nums;
|
|
1414
|
-
font-weight: 600;
|
|
1415
|
-
}
|
|
1416
|
-
.sv-grid-status-label {
|
|
1417
|
-
color: var(--sg-muted, #64748b);
|
|
1418
|
-
font-weight: 400;
|
|
1419
|
-
margin-right: 5px;
|
|
1420
|
-
}
|
|
1421
|
-
.sv-grid-cf-content {
|
|
1422
|
-
position: relative;
|
|
1423
|
-
z-index: 1;
|
|
1424
|
-
}
|
|
1425
|
-
.sv-grid-cf-icon {
|
|
1426
|
-
margin-right: 5px;
|
|
1427
|
-
font-size: 0.95em;
|
|
1428
|
-
}
|
|
1429
|
-
.sv-grid-chip {
|
|
1430
|
-
display: inline-flex;
|
|
1431
|
-
align-items: center;
|
|
1432
|
-
gap: 4px;
|
|
1433
|
-
padding: 2px 8px;
|
|
1434
|
-
font-size: 0.85em;
|
|
1435
|
-
line-height: 1.4;
|
|
1436
|
-
background: color-mix(in srgb, var(--sg-accent, #2563eb) 18%, transparent);
|
|
1437
|
-
color: var(--sg-fg, inherit);
|
|
1438
|
-
border-radius: 999px;
|
|
1439
|
-
border: 1px solid
|
|
1440
|
-
color-mix(in srgb, var(--sg-accent, #2563eb) 35%, transparent);
|
|
1441
|
-
white-space: nowrap;
|
|
1442
|
-
}
|
|
1443
|
-
.sv-grid-chip-removable {
|
|
1444
|
-
padding-right: 2px;
|
|
1445
|
-
}
|
|
1446
|
-
.sv-grid-chip-remove {
|
|
1447
|
-
display: inline-flex;
|
|
1448
|
-
align-items: center;
|
|
1449
|
-
justify-content: center;
|
|
1450
|
-
width: 16px;
|
|
1451
|
-
height: 16px;
|
|
1452
|
-
border: 0;
|
|
1453
|
-
border-radius: 999px;
|
|
1454
|
-
background: color-mix(in srgb, var(--sg-fg, #0f172a) 18%, transparent);
|
|
1455
|
-
color: inherit;
|
|
1456
|
-
font-size: 12px;
|
|
1457
|
-
line-height: 1;
|
|
1458
|
-
cursor: pointer;
|
|
1459
|
-
padding: 0;
|
|
1460
|
-
}
|
|
1461
|
-
.sv-grid-chip-remove:hover {
|
|
1462
|
-
background: rgba(220, 38, 38, 0.7);
|
|
1463
|
-
color: #fff;
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
.sv-grid-icon {
|
|
1467
|
-
width: 1em;
|
|
1468
|
-
height: 1em;
|
|
1469
|
-
display: block;
|
|
1470
|
-
flex: none;
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
.sv-grid-header-cell {
|
|
1474
|
-
display: flex;
|
|
1475
|
-
align-items: center;
|
|
1476
|
-
gap: 2px;
|
|
1477
|
-
width: 100%;
|
|
1478
|
-
min-width: 0;
|
|
1479
|
-
/* Themeable header height. Defaults to auto (content-driven); presets
|
|
1480
|
-
can set a taller header (e.g. Material's roomy data-table header).
|
|
1481
|
-
The component measures the rendered header height for sticky offsets,
|
|
1482
|
-
so this propagates to --sg-thead-h automatically. */
|
|
1483
|
-
min-height: var(--sg-header-min-height, auto);
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
.sv-grid-header-sort {
|
|
1487
|
-
display: flex;
|
|
1488
|
-
align-items: center;
|
|
1489
|
-
gap: 4px;
|
|
1490
|
-
flex: 1 1 auto;
|
|
1491
|
-
min-width: 0;
|
|
1492
|
-
padding: 2px;
|
|
1493
|
-
border: 0;
|
|
1494
|
-
background: transparent;
|
|
1495
|
-
font: inherit;
|
|
1496
|
-
color: inherit;
|
|
1497
|
-
text-align: left;
|
|
1498
|
-
cursor: pointer;
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
/* Custom (function-valued) header rendering. Behaves like the sort
|
|
1502
|
-
button but is a div so the consumer's snippet can contain its own
|
|
1503
|
-
interactive elements (menu buttons, dropdowns, etc) - button-in-
|
|
1504
|
-
button DOM is invalid. */
|
|
1505
|
-
.sv-grid-header-custom {
|
|
1506
|
-
display: flex;
|
|
1507
|
-
align-items: center;
|
|
1508
|
-
gap: 4px;
|
|
1509
|
-
flex: 1 1 auto;
|
|
1510
|
-
min-width: 0;
|
|
1511
|
-
padding: 2px;
|
|
1512
|
-
text-align: left;
|
|
1513
|
-
cursor: pointer;
|
|
1514
|
-
outline: none;
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
.sv-grid-header-label {
|
|
1518
|
-
/* min-width:0 lets this flex item shrink below its text width so a long
|
|
1519
|
-
label truncates to a single line with an ellipsis instead of overflowing
|
|
1520
|
-
or forcing the header taller. flex:0 1 auto keeps the sort icon snug to
|
|
1521
|
-
the label rather than pushed to the far edge. */
|
|
1522
|
-
flex: 0 1 auto;
|
|
1523
|
-
min-width: 0;
|
|
1524
|
-
overflow: hidden;
|
|
1525
|
-
text-overflow: ellipsis;
|
|
1526
|
-
white-space: nowrap;
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
.sv-grid-header-icon {
|
|
1530
|
-
display: inline-flex;
|
|
1531
|
-
flex: none;
|
|
1532
|
-
font-size: 13px;
|
|
1533
|
-
color: var(--sg-muted, #5b6b85);
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
.sv-grid-header-icon-hint {
|
|
1537
|
-
opacity: 0;
|
|
1538
|
-
transition: opacity 120ms ease;
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
.sv-grid-column:hover .sv-grid-header-icon-hint {
|
|
1542
|
-
opacity: 0.4;
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
.sv-grid-header-icon-flag {
|
|
1546
|
-
color: var(--sg-accent, #0b63f3);
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
.sv-grid-col-menu-btn {
|
|
1550
|
-
display: inline-flex;
|
|
1551
|
-
align-items: center;
|
|
1552
|
-
justify-content: center;
|
|
1553
|
-
flex: none;
|
|
1554
|
-
/* Collapse to zero width when invisible so a non-hovered column
|
|
1555
|
-
has no dead space at the right; the funnel (when an active
|
|
1556
|
-
filter is on) can then sit flush against the right edge.
|
|
1557
|
-
Hover / focus / open expand it back to 22px. */
|
|
1558
|
-
width: 0;
|
|
1559
|
-
height: 22px;
|
|
1560
|
-
padding: 0;
|
|
1561
|
-
border: 0;
|
|
1562
|
-
border-radius: 4px;
|
|
1563
|
-
background: transparent;
|
|
1564
|
-
color: var(--sg-muted, #5b6b85);
|
|
1565
|
-
font-size: 15px;
|
|
1566
|
-
cursor: pointer;
|
|
1567
|
-
opacity: 0;
|
|
1568
|
-
pointer-events: none;
|
|
1569
|
-
overflow: hidden;
|
|
1570
|
-
transition:
|
|
1571
|
-
width 140ms ease,
|
|
1572
|
-
opacity 140ms ease,
|
|
1573
|
-
background-color 120ms ease;
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
.sv-grid-column:hover .sv-grid-col-menu-btn,
|
|
1577
|
-
.sv-grid-col-menu-btn.is-open,
|
|
1578
|
-
.sv-grid-col-menu-btn:focus-visible {
|
|
1579
|
-
width: 22px;
|
|
1580
|
-
opacity: 1;
|
|
1581
|
-
pointer-events: auto;
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
/* The funnel filter button is a sibling .sv-grid-col-menu-btn that
|
|
1585
|
-
ALSO carries .sv-grid-col-filter-btn. When the column has an
|
|
1586
|
-
active filter (.is-active) the funnel stays visible at full size
|
|
1587
|
-
even when the column isn't hovered - so users can see at a glance
|
|
1588
|
-
which columns are filtered. Because the 3-dot menu still collapses
|
|
1589
|
-
to width: 0 in that state, the funnel ends up flush against the
|
|
1590
|
-
right edge. On hover, the menu expands back to 22px and pushes the
|
|
1591
|
-
funnel left naturally. */
|
|
1592
|
-
.sv-grid-col-filter-btn.is-active {
|
|
1593
|
-
width: 22px;
|
|
1594
|
-
opacity: 1;
|
|
1595
|
-
pointer-events: auto;
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
.sv-grid-col-menu-btn:hover,
|
|
1599
|
-
.sv-grid-col-menu-btn.is-open {
|
|
1600
|
-
background: var(--sg-row-hover-bg, #e2e8f3);
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
.sv-grid-filter-operator-btn {
|
|
1604
|
-
display: inline-flex;
|
|
1605
|
-
align-items: center;
|
|
1606
|
-
gap: 2px;
|
|
1607
|
-
flex: none;
|
|
1608
|
-
height: 24px;
|
|
1609
|
-
padding: 0 4px;
|
|
1610
|
-
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1611
|
-
border-radius: 4px;
|
|
1612
|
-
background: var(--sg-input-bg, #fff);
|
|
1613
|
-
color: var(--sg-fg, #334155);
|
|
1614
|
-
font-size: 13px;
|
|
1615
|
-
cursor: pointer;
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1618
|
-
.sv-grid-filter-operator-btn.is-open {
|
|
1619
|
-
border-color: var(--sg-accent, #0b63f3);
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
.sv-grid-caret {
|
|
1623
|
-
display: inline-flex;
|
|
1624
|
-
font-size: 9px;
|
|
1625
|
-
color: var(--sg-muted, #94a3b8);
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
.sv-grid-menu-backdrop {
|
|
1629
|
-
position: fixed;
|
|
1630
|
-
inset: 0;
|
|
1631
|
-
z-index: 900;
|
|
1632
|
-
background: transparent;
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
.sv-grid-menu {
|
|
1636
|
-
position: fixed;
|
|
1637
|
-
z-index: 901;
|
|
1638
|
-
max-height: calc(100vh - 24px);
|
|
1639
|
-
overflow: auto;
|
|
1640
|
-
padding: 4px;
|
|
1641
|
-
background: var(--sg-bg, #fff);
|
|
1642
|
-
border: 1px solid var(--sg-border, #d6dee9);
|
|
1643
|
-
border-radius: 8px;
|
|
1644
|
-
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);
|
|
1645
|
-
font-size: 13px;
|
|
1646
|
-
color: var(--sg-fg, #1f2937);
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
.sv-grid-column-menu {
|
|
1650
|
-
width: 260px;
|
|
1651
|
-
}
|
|
1652
|
-
/* Column-menu tab bar (General / Filter / Columns). */
|
|
1653
|
-
.sv-grid-menu-tabs {
|
|
1654
|
-
display: flex;
|
|
1655
|
-
gap: 2px;
|
|
1656
|
-
margin: -2px -2px 4px;
|
|
1657
|
-
padding: 2px 2px 0;
|
|
1658
|
-
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
1659
|
-
}
|
|
1660
|
-
.sv-grid-menu-tab {
|
|
1661
|
-
flex: 1;
|
|
1662
|
-
padding: 6px 6px;
|
|
1663
|
-
border: 0;
|
|
1664
|
-
border-bottom: 2px solid transparent;
|
|
1665
|
-
background: transparent;
|
|
1666
|
-
color: var(--sg-muted, #64748b);
|
|
1667
|
-
font-size: 12px;
|
|
1668
|
-
font-weight: 600;
|
|
1669
|
-
cursor: pointer;
|
|
1670
|
-
}
|
|
1671
|
-
.sv-grid-menu-tab:hover {
|
|
1672
|
-
color: var(--sg-fg, #0f172a);
|
|
1673
|
-
}
|
|
1674
|
-
.sv-grid-menu-tab.is-active {
|
|
1675
|
-
color: var(--sg-accent, #2563eb);
|
|
1676
|
-
border-bottom-color: var(--sg-accent, #2563eb);
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
.sv-grid-operator-menu {
|
|
1680
|
-
width: 184px;
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
.sv-grid-context-menu {
|
|
1684
|
-
min-width: 180px;
|
|
1685
|
-
}
|
|
1686
|
-
|
|
1687
|
-
.sv-grid-comment-editor {
|
|
1688
|
-
width: 240px;
|
|
1689
|
-
padding: 8px;
|
|
1690
|
-
}
|
|
1691
|
-
.sv-grid-comment-textarea {
|
|
1692
|
-
width: 100%;
|
|
1693
|
-
resize: vertical;
|
|
1694
|
-
min-height: 60px;
|
|
1695
|
-
padding: 6px 8px;
|
|
1696
|
-
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1697
|
-
border-radius: 4px;
|
|
1698
|
-
background: var(--sg-input-bg, #fff);
|
|
1699
|
-
color: var(--sg-fg, #0f172a);
|
|
1700
|
-
font: inherit;
|
|
1701
|
-
font-size: 13px;
|
|
1702
|
-
outline: none;
|
|
1703
|
-
}
|
|
1704
|
-
.sv-grid-comment-textarea:focus {
|
|
1705
|
-
border-color: var(--sg-accent, #6366f1);
|
|
1706
|
-
}
|
|
1707
|
-
.sv-grid-comment-actions {
|
|
1708
|
-
display: flex;
|
|
1709
|
-
align-items: center;
|
|
1710
|
-
gap: 6px;
|
|
1711
|
-
margin-top: 8px;
|
|
1712
|
-
}
|
|
1713
|
-
.sv-grid-comment-spacer {
|
|
1714
|
-
flex: 1 1 auto;
|
|
1715
|
-
}
|
|
1716
|
-
.sv-grid-comment-actions button {
|
|
1717
|
-
padding: 4px 10px;
|
|
1718
|
-
border-radius: 4px;
|
|
1719
|
-
border: 1px solid var(--sg-border, #cbd5e1);
|
|
1720
|
-
background: var(--sg-bg, #fff);
|
|
1721
|
-
color: var(--sg-fg, #1f2937);
|
|
1722
|
-
font: inherit;
|
|
1723
|
-
font-size: 12px;
|
|
1724
|
-
cursor: pointer;
|
|
1725
|
-
}
|
|
1726
|
-
.sv-grid-comment-save {
|
|
1727
|
-
border-color: var(--sg-accent, #6366f1) !important;
|
|
1728
|
-
background: var(--sg-accent, #6366f1) !important;
|
|
1729
|
-
color: #fff !important;
|
|
1730
|
-
}
|
|
1731
|
-
.sv-grid-comment-remove {
|
|
1732
|
-
color: var(--sg-danger, #dc2626) !important;
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
|
-
.sv-grid-menu-item {
|
|
1736
|
-
display: flex;
|
|
1737
|
-
align-items: center;
|
|
1738
|
-
gap: 8px;
|
|
1739
|
-
width: 100%;
|
|
1740
|
-
padding: 7px 10px;
|
|
1741
|
-
border: 0;
|
|
1742
|
-
border-radius: 5px;
|
|
1743
|
-
background: transparent;
|
|
1744
|
-
color: inherit;
|
|
1745
|
-
font: inherit;
|
|
1746
|
-
text-align: left;
|
|
1747
|
-
cursor: pointer;
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
.sv-grid-menu-item:hover {
|
|
1751
|
-
background: var(--sg-row-hover-bg, #eef2f8);
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
.sv-grid-menu-item[aria-checked="true"] {
|
|
1755
|
-
background: var(--sg-selection-bg, #eaf2ff);
|
|
1756
|
-
color: var(--sg-accent, #0b63f3);
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
.sv-grid-menu-sep {
|
|
1760
|
-
height: 1px;
|
|
1761
|
-
margin: 4px 6px;
|
|
1762
|
-
background: var(--sg-border, #e6ebf2);
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
.sv-grid-menu-filter {
|
|
1766
|
-
padding: 4px 6px 6px;
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
.sv-grid-menu-filter-head {
|
|
1770
|
-
display: flex;
|
|
1771
|
-
align-items: center;
|
|
1772
|
-
gap: 6px;
|
|
1773
|
-
padding: 2px 0 6px;
|
|
1774
|
-
font-weight: 600;
|
|
1775
|
-
color: var(--sg-muted, #475569);
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
.sv-grid-menu-search {
|
|
1779
|
-
width: 100%;
|
|
1780
|
-
height: 28px;
|
|
1781
|
-
box-sizing: border-box;
|
|
1782
|
-
margin-bottom: 6px;
|
|
1783
|
-
padding: 0 8px;
|
|
1784
|
-
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1785
|
-
border-radius: 5px;
|
|
1786
|
-
font: inherit;
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
.sv-grid-menu-filter-row {
|
|
1790
|
-
display: flex;
|
|
1791
|
-
gap: 4px;
|
|
1792
|
-
margin-bottom: 6px;
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
|
-
.sv-grid-menu-operator-group {
|
|
1796
|
-
display: flex;
|
|
1797
|
-
gap: 2px;
|
|
1798
|
-
margin-bottom: 6px;
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
.sv-grid-menu-operator-btn {
|
|
1802
|
-
flex: 1 1 0;
|
|
1803
|
-
display: inline-flex;
|
|
1804
|
-
align-items: center;
|
|
1805
|
-
justify-content: center;
|
|
1806
|
-
height: 28px;
|
|
1807
|
-
padding: 0;
|
|
1808
|
-
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1809
|
-
border-radius: 4px;
|
|
1810
|
-
background: var(--sg-input-bg, #fff);
|
|
1811
|
-
color: var(--sg-fg, #475569);
|
|
1812
|
-
cursor: pointer;
|
|
1813
|
-
transition:
|
|
1814
|
-
background-color 100ms ease,
|
|
1815
|
-
color 100ms ease,
|
|
1816
|
-
border-color 100ms ease;
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
.sv-grid-menu-operator-btn:hover {
|
|
1820
|
-
background: var(--sg-row-hover-bg, #eef2f8);
|
|
1821
|
-
}
|
|
1822
|
-
|
|
1823
|
-
.sv-grid-menu-operator-btn.is-active {
|
|
1824
|
-
background: var(--sg-accent, #0b63f3);
|
|
1825
|
-
border-color: var(--sg-accent, #0b63f3);
|
|
1826
|
-
color: #fff;
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
|
-
.sv-grid-menu-condition-value {
|
|
1830
|
-
flex: 1 1 0;
|
|
1831
|
-
min-width: 0;
|
|
1832
|
-
height: 28px;
|
|
1833
|
-
box-sizing: border-box;
|
|
1834
|
-
padding: 0 8px;
|
|
1835
|
-
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1836
|
-
border-radius: 5px;
|
|
1837
|
-
font: inherit;
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
/* Multi-condition filter: AND/OR join toggle + add-condition button. */
|
|
1841
|
-
.sv-grid-menu-add-cond {
|
|
1842
|
-
align-self: flex-start;
|
|
1843
|
-
margin-top: 2px;
|
|
1844
|
-
padding: 3px 8px;
|
|
1845
|
-
font-size: 11.5px;
|
|
1846
|
-
font-weight: 600;
|
|
1847
|
-
color: var(--sg-accent, #3b82f6);
|
|
1848
|
-
background: transparent;
|
|
1849
|
-
border: 1px dashed var(--sg-border, #cbd5e1);
|
|
1850
|
-
border-radius: 5px;
|
|
1851
|
-
cursor: pointer;
|
|
1852
|
-
}
|
|
1853
|
-
.sv-grid-menu-add-cond:hover {
|
|
1854
|
-
background: color-mix(in oklab, var(--sg-accent, #3b82f6) 8%, transparent);
|
|
1855
|
-
border-style: solid;
|
|
1856
|
-
}
|
|
1857
|
-
.sv-grid-menu-join {
|
|
1858
|
-
display: inline-flex;
|
|
1859
|
-
align-items: center;
|
|
1860
|
-
gap: 2px;
|
|
1861
|
-
margin: 2px 0;
|
|
1862
|
-
}
|
|
1863
|
-
.sv-grid-menu-join button {
|
|
1864
|
-
padding: 2px 10px;
|
|
1865
|
-
font-size: 11px;
|
|
1866
|
-
font-weight: 700;
|
|
1867
|
-
color: var(--sg-muted, #64748b);
|
|
1868
|
-
background: transparent;
|
|
1869
|
-
border: 1px solid var(--sg-border, #cbd5e1);
|
|
1870
|
-
cursor: pointer;
|
|
1871
|
-
}
|
|
1872
|
-
.sv-grid-menu-join button:first-child { border-radius: 5px 0 0 5px; }
|
|
1873
|
-
.sv-grid-menu-join button:nth-child(2) { border-radius: 0; border-left: 0; }
|
|
1874
|
-
.sv-grid-menu-join button.is-on {
|
|
1875
|
-
background: var(--sg-accent, #3b82f6);
|
|
1876
|
-
color: #fff;
|
|
1877
|
-
border-color: var(--sg-accent, #3b82f6);
|
|
1878
|
-
}
|
|
1879
|
-
.sv-grid-menu-join-x {
|
|
1880
|
-
margin-left: auto;
|
|
1881
|
-
border-radius: 5px !important;
|
|
1882
|
-
font-size: 14px;
|
|
1883
|
-
line-height: 1;
|
|
1884
|
-
padding: 2px 7px !important;
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
.sv-grid-facet-list {
|
|
1888
|
-
max-height: 200px;
|
|
1889
|
-
overflow: auto;
|
|
1890
|
-
border: 1px solid var(--sg-border, #eaeef4);
|
|
1891
|
-
border-radius: 5px;
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
.sv-grid-facet {
|
|
1895
|
-
display: flex;
|
|
1896
|
-
align-items: center;
|
|
1897
|
-
gap: 8px;
|
|
1898
|
-
padding: 4px 8px;
|
|
1899
|
-
cursor: pointer;
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
.sv-grid-facet:hover {
|
|
1903
|
-
background: var(--sg-row-hover-bg, #f4f6fa);
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
|
-
.sv-grid-facet-all {
|
|
1907
|
-
border-bottom: 1px solid var(--sg-border, #eaeef4);
|
|
1908
|
-
font-weight: 600;
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
.sv-grid-facet input {
|
|
1912
|
-
flex: none;
|
|
1913
|
-
}
|
|
1914
|
-
|
|
1915
|
-
.sv-grid-facet-label {
|
|
1916
|
-
overflow: hidden;
|
|
1917
|
-
text-overflow: ellipsis;
|
|
1918
|
-
white-space: nowrap;
|
|
1919
|
-
}
|
|
1920
|
-
|
|
1921
|
-
.sv-grid-facet-empty,
|
|
1922
|
-
.sv-grid-facet-note {
|
|
1923
|
-
padding: 6px 8px;
|
|
1924
|
-
color: var(--sg-muted, #94a3b8);
|
|
1925
|
-
font-size: 12px;
|
|
1926
|
-
}
|
|
1927
|
-
|
|
1928
|
-
.sv-grid-menu-actions {
|
|
1929
|
-
display: flex;
|
|
1930
|
-
justify-content: flex-end;
|
|
1931
|
-
gap: 6px;
|
|
1932
|
-
padding-top: 8px;
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
|
-
.sv-grid-menu-btn {
|
|
1936
|
-
padding: 5px 10px;
|
|
1937
|
-
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1938
|
-
border-radius: 5px;
|
|
1939
|
-
background: var(--sg-input-bg, #fff);
|
|
1940
|
-
color: var(--sg-fg, #334155);
|
|
1941
|
-
font: inherit;
|
|
1942
|
-
cursor: pointer;
|
|
1943
|
-
}
|
|
1944
|
-
|
|
1945
|
-
.sv-grid-menu-btn-primary {
|
|
1946
|
-
background: var(--sg-accent, #0b63f3);
|
|
1947
|
-
border-color: var(--sg-accent, #0b63f3);
|
|
1948
|
-
color: #fff;
|
|
1949
|
-
}
|
|
1950
|
-
|
|
1951
|
-
.sv-grid-menu-item:disabled,
|
|
1952
|
-
.sv-grid-menu-btn:disabled {
|
|
1953
|
-
opacity: 0.4;
|
|
1954
|
-
cursor: default;
|
|
1955
|
-
}
|
|
1956
|
-
|
|
1957
|
-
.sv-grid-menu-item:disabled:hover {
|
|
1958
|
-
background: transparent;
|
|
1959
|
-
}
|
|
1960
|
-
|
|
1961
|
-
.sv-grid-group-row > .sv-grid-cell {
|
|
1962
|
-
background: var(--sg-header-bg, #eef2f8);
|
|
1963
|
-
font-weight: 600;
|
|
1964
|
-
cursor: pointer;
|
|
1965
|
-
}
|
|
1966
|
-
|
|
1967
|
-
.sv-grid-row-selected > .sv-grid-cell {
|
|
1968
|
-
background: var(--sg-selection-bg, #eaf2ff);
|
|
1969
|
-
}
|
|
1970
|
-
|
|
1971
|
-
.sv-grid-group-cell {
|
|
1972
|
-
padding: 0 12px;
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
.sv-grid-group-content {
|
|
1976
|
-
display: inline-flex;
|
|
1977
|
-
align-items: center;
|
|
1978
|
-
gap: 6px;
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
.sv-grid-group-toggle {
|
|
1982
|
-
border: 0;
|
|
1983
|
-
background: transparent;
|
|
1984
|
-
padding: 0 4px 0 0;
|
|
1985
|
-
font-size: 11px;
|
|
1986
|
-
line-height: 1;
|
|
1987
|
-
color: var(--sg-fg, #334155);
|
|
1988
|
-
cursor: pointer;
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
.sv-grid-group-count {
|
|
1992
|
-
margin-left: 6px;
|
|
1993
|
-
font-weight: 400;
|
|
1994
|
-
font-size: 12px;
|
|
1995
|
-
color: var(--sg-muted, #64748b);
|
|
1996
|
-
}
|
|
1997
|
-
.sv-grid-group-agg {
|
|
1998
|
-
margin-left: 10px;
|
|
1999
|
-
font-size: 12px;
|
|
2000
|
-
font-weight: 600;
|
|
2001
|
-
color: var(--sg-fg);
|
|
2002
|
-
white-space: nowrap;
|
|
2003
|
-
}
|
|
2004
|
-
.sv-grid-group-agg-label {
|
|
2005
|
-
font-weight: 400;
|
|
2006
|
-
color: var(--sg-muted, #64748b);
|
|
2007
|
-
margin-right: 4px;
|
|
2008
|
-
}
|
|
2009
|
-
.sv-grid-group-agg-label::after {
|
|
2010
|
-
content: ":";
|
|
2011
|
-
}
|
|
2012
|
-
|
|
2013
|
-
.sv-grid-group-child-indent {
|
|
2014
|
-
display: inline-block;
|
|
2015
|
-
width: 18px;
|
|
2016
|
-
}
|
|
2017
|
-
|
|
2018
|
-
/* ---- Custom hover tooltip (column .tooltip + cell notes) ---- */
|
|
2019
|
-
.sv-grid-tooltip {
|
|
2020
|
-
position: fixed;
|
|
2021
|
-
z-index: 10000;
|
|
2022
|
-
max-width: 280px;
|
|
2023
|
-
padding: 8px 10px;
|
|
2024
|
-
background: #0f172a;
|
|
2025
|
-
color: #f1f5f9;
|
|
2026
|
-
border-radius: var(--sg-radius, 6px);
|
|
2027
|
-
font-size: 12px;
|
|
2028
|
-
line-height: 1.45;
|
|
2029
|
-
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.28);
|
|
2030
|
-
pointer-events: none;
|
|
2031
|
-
white-space: pre-wrap;
|
|
2032
|
-
word-wrap: break-word;
|
|
2033
|
-
}
|
|
2034
|
-
[data-theme="dark"] .sv-grid-tooltip { background: #f1f5f9; color: #0f172a; }
|
|
2035
|
-
|
|
2036
|
-
/* ---- Textarea editor ---- */
|
|
2037
|
-
.sv-grid-cell-editor-textarea {
|
|
2038
|
-
width: 100%;
|
|
2039
|
-
min-height: 80px;
|
|
2040
|
-
padding: 6px 8px;
|
|
2041
|
-
border: 1px solid var(--sg-accent, #6366f1);
|
|
2042
|
-
background: var(--sg-bg, #fff);
|
|
2043
|
-
color: var(--sg-fg, #0f172a);
|
|
2044
|
-
border-radius: 4px;
|
|
2045
|
-
font-family: inherit;
|
|
2046
|
-
font-size: 13px;
|
|
2047
|
-
line-height: 1.4;
|
|
2048
|
-
resize: vertical;
|
|
2049
|
-
outline: none;
|
|
2050
|
-
box-shadow: var(--sg-focus-ring, 0 0 0 2px rgba(99, 102, 241, 0.40));
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
.sv-grid-autocomplete {
|
|
2054
|
-
position: relative;
|
|
2055
|
-
width: 100%;
|
|
2056
|
-
}
|
|
2057
|
-
.sv-grid-autocomplete-list {
|
|
2058
|
-
position: absolute;
|
|
2059
|
-
left: 0;
|
|
2060
|
-
right: 0;
|
|
2061
|
-
top: 100%;
|
|
2062
|
-
z-index: 30;
|
|
2063
|
-
max-height: 220px;
|
|
2064
|
-
overflow: auto;
|
|
2065
|
-
margin-top: 2px;
|
|
2066
|
-
padding: 4px;
|
|
2067
|
-
background: var(--sg-bg, #fff);
|
|
2068
|
-
border: 1px solid var(--sg-border, #d6dee9);
|
|
2069
|
-
border-radius: var(--sg-radius, 6px);
|
|
2070
|
-
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);
|
|
2071
|
-
}
|
|
2072
|
-
.sv-grid-autocomplete-option {
|
|
2073
|
-
display: block;
|
|
2074
|
-
width: 100%;
|
|
2075
|
-
text-align: left;
|
|
2076
|
-
padding: 5px 8px;
|
|
2077
|
-
border: 0;
|
|
2078
|
-
border-radius: 4px;
|
|
2079
|
-
background: transparent;
|
|
2080
|
-
color: var(--sg-fg, #1f2937);
|
|
2081
|
-
font: inherit;
|
|
2082
|
-
font-size: 13px;
|
|
2083
|
-
cursor: pointer;
|
|
2084
|
-
white-space: nowrap;
|
|
2085
|
-
overflow: hidden;
|
|
2086
|
-
text-overflow: ellipsis;
|
|
2087
|
-
}
|
|
2088
|
-
.sv-grid-autocomplete-option:hover,
|
|
2089
|
-
.sv-grid-autocomplete-option[aria-selected="true"] {
|
|
2090
|
-
background: var(--sg-row-hover-bg, rgba(99, 102, 241, 0.10));
|
|
2091
|
-
}
|
|
2092
|
-
|
|
2093
|
-
/* ---- Find-in-grid overlay ---- */
|
|
2094
|
-
.sv-grid-find {
|
|
2095
|
-
position: absolute;
|
|
2096
|
-
top: 8px; right: 8px;
|
|
2097
|
-
z-index: 60;
|
|
2098
|
-
display: inline-flex; align-items: center; gap: 4px;
|
|
2099
|
-
padding: 6px 8px;
|
|
2100
|
-
background: var(--sg-bg, #fff);
|
|
2101
|
-
color: var(--sg-fg, #0f172a);
|
|
2102
|
-
border: 1px solid var(--sg-border, #cbd5e1);
|
|
2103
|
-
border-radius: 8px;
|
|
2104
|
-
box-shadow: 0 10px 25px rgba(15, 23, 42, 0.18);
|
|
2105
|
-
min-width: 280px;
|
|
2106
|
-
}
|
|
2107
|
-
.sv-grid-find-icon {
|
|
2108
|
-
width: 14px; height: 14px;
|
|
2109
|
-
color: var(--sg-muted, #94a3b8);
|
|
2110
|
-
flex: none;
|
|
2111
|
-
margin-left: 2px;
|
|
2112
|
-
}
|
|
2113
|
-
.sv-grid-find-input {
|
|
2114
|
-
flex: 1; min-width: 0;
|
|
2115
|
-
border: 0; outline: none;
|
|
2116
|
-
background: transparent;
|
|
2117
|
-
color: inherit;
|
|
2118
|
-
font-size: 13px;
|
|
2119
|
-
padding: 2px 6px;
|
|
2120
|
-
}
|
|
2121
|
-
.sv-grid-find-input::placeholder { color: var(--sg-muted, #94a3b8); }
|
|
2122
|
-
.sv-grid-find-count {
|
|
2123
|
-
font-size: 11px;
|
|
2124
|
-
color: var(--sg-muted, #64748b);
|
|
2125
|
-
font-variant-numeric: tabular-nums;
|
|
2126
|
-
padding: 0 4px;
|
|
2127
|
-
white-space: nowrap;
|
|
2128
|
-
}
|
|
2129
|
-
.sv-grid-find-step, .sv-grid-find-close {
|
|
2130
|
-
display: inline-flex; align-items: center; justify-content: center;
|
|
2131
|
-
width: 22px; height: 22px;
|
|
2132
|
-
background: transparent;
|
|
2133
|
-
border: 0;
|
|
2134
|
-
color: var(--sg-muted, #64748b);
|
|
2135
|
-
border-radius: 4px;
|
|
2136
|
-
cursor: pointer;
|
|
2137
|
-
font-size: 12px;
|
|
2138
|
-
}
|
|
2139
|
-
.sv-grid-find-step:hover:not(:disabled),
|
|
2140
|
-
.sv-grid-find-close:hover {
|
|
2141
|
-
background: var(--sg-row-hover-bg, rgba(148, 163, 184, 0.12));
|
|
2142
|
-
color: var(--sg-fg, #0f172a);
|
|
2143
|
-
}
|
|
2144
|
-
.sv-grid-find-step:disabled { opacity: 0.30; cursor: default; }
|
|
2145
|
-
|
|
2146
|
-
/* ---- Pagination footer (GridFooter.svelte) ------------------------------
|
|
2147
|
-
Ships with the grid so the pager is styled out of the box - previously this
|
|
2148
|
-
lived only in the examples' host stylesheet, leaving bare consumers with an
|
|
2149
|
-
unstyled pager. Uses --sg-* tokens with fallbacks like the rest of the theme. */
|
|
2150
|
-
.sv-grid-pagination {
|
|
2151
|
-
display: flex;
|
|
2152
|
-
align-items: center;
|
|
2153
|
-
justify-content: flex-end;
|
|
2154
|
-
gap: 24px;
|
|
2155
|
-
padding: 12px 16px;
|
|
2156
|
-
margin-top: 0;
|
|
2157
|
-
border: 1px solid var(--sg-border, #e2e8f0);
|
|
2158
|
-
border-top: 0;
|
|
2159
|
-
border-radius: 0 0 var(--sg-radius, 6px) var(--sg-radius, 6px);
|
|
2160
|
-
background: var(--sg-header-bg, #f1f5f9);
|
|
2161
|
-
color: var(--sg-fg, #0f172a);
|
|
2162
|
-
font-size: 13px;
|
|
2163
|
-
}
|
|
2164
|
-
/* Top-positioned pager: rounded + bordered on the top edge instead. */
|
|
2165
|
-
.sv-grid-pagination-top {
|
|
2166
|
-
border-top: 1px solid var(--sg-border, #e2e8f0);
|
|
2167
|
-
border-bottom: 0;
|
|
2168
|
-
border-radius: var(--sg-radius, 6px) var(--sg-radius, 6px) 0 0;
|
|
2169
|
-
}
|
|
2170
|
-
.sv-grid-pagination-pagesize {
|
|
2171
|
-
display: inline-flex;
|
|
2172
|
-
align-items: center;
|
|
2173
|
-
gap: 8px;
|
|
2174
|
-
color: var(--sg-muted, #64748b);
|
|
2175
|
-
}
|
|
2176
|
-
.sv-grid-pagination-pagesize-dd {
|
|
2177
|
-
position: relative;
|
|
2178
|
-
height: 28px;
|
|
2179
|
-
min-width: 62px;
|
|
2180
|
-
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
2181
|
-
border-radius: 5px;
|
|
2182
|
-
background: var(--sg-input-bg, #fff);
|
|
2183
|
-
}
|
|
2184
|
-
.sv-grid-pagination-pagesize-dd .sv-grid-dropdown,
|
|
2185
|
-
.sv-grid-pagination-pagesize-dd .sv-grid-dropdown-trigger {
|
|
2186
|
-
background: transparent;
|
|
2187
|
-
border-radius: 5px;
|
|
2188
|
-
font-size: 13px;
|
|
2189
|
-
color: var(--sg-fg, #0f172a);
|
|
2190
|
-
}
|
|
2191
|
-
.sv-grid-pagination-pagesize-dd:focus-within {
|
|
2192
|
-
border-color: var(--sg-accent, #2563eb);
|
|
2193
|
-
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 30%, transparent);
|
|
2194
|
-
}
|
|
2195
|
-
.sv-grid-pagination-range {
|
|
2196
|
-
color: var(--sg-fg, #0f172a);
|
|
2197
|
-
}
|
|
2198
|
-
.sv-grid-pagination-nav {
|
|
2199
|
-
display: inline-flex;
|
|
2200
|
-
align-items: center;
|
|
2201
|
-
gap: 4px;
|
|
2202
|
-
}
|
|
2203
|
-
.sv-grid-pagination-btn {
|
|
2204
|
-
display: inline-flex;
|
|
2205
|
-
align-items: center;
|
|
2206
|
-
justify-content: center;
|
|
2207
|
-
width: 28px;
|
|
2208
|
-
height: 28px;
|
|
2209
|
-
padding: 0;
|
|
2210
|
-
border: 0;
|
|
2211
|
-
border-radius: 5px;
|
|
2212
|
-
background: transparent;
|
|
2213
|
-
color: var(--sg-fg, #0f172a);
|
|
2214
|
-
font: inherit;
|
|
2215
|
-
font-size: 16px;
|
|
2216
|
-
cursor: pointer;
|
|
2217
|
-
transition: background-color 100ms ease, color 100ms ease;
|
|
2218
|
-
}
|
|
2219
|
-
.sv-grid-pagination-btn:hover:not(:disabled) {
|
|
2220
|
-
background: var(--sg-input-bg, #fff);
|
|
2221
|
-
color: var(--sg-accent, #2563eb);
|
|
2222
|
-
}
|
|
2223
|
-
.sv-grid-pagination-btn:disabled {
|
|
2224
|
-
color: var(--sg-muted, #64748b);
|
|
2225
|
-
opacity: 0.4;
|
|
2226
|
-
cursor: default;
|
|
2227
|
-
}
|
|
2228
|
-
.sv-grid-pagination-label {
|
|
2229
|
-
margin: 0 8px;
|
|
2230
|
-
color: var(--sg-fg, #0f172a);
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
/* ---- Menu / filter chrome completeness -----------------------------------
|
|
2234
|
-
These styles previously lived only in the examples' host stylesheet. Shipping
|
|
2235
|
-
them here means the column menu, filter/choose-columns popovers, facet
|
|
2236
|
-
checklist, operator select, filter buttons, and the scrollbar corner are
|
|
2237
|
-
fully themed out of the box for any consumer - all via --sg-* tokens. */
|
|
2238
|
-
|
|
2239
|
-
/* Hide the scrollbar corner when its scrollbar is absent. */
|
|
2240
|
-
.sv-grid-no-scroll .sv-grid-scrollbar-corner,
|
|
2241
|
-
.sv-grid-no-scroll .sv-grid-scrollbar {
|
|
2242
|
-
display: none;
|
|
2243
|
-
}
|
|
2244
|
-
/* Scrollbar corner divider uses the theme border token. */
|
|
2245
|
-
.sv-grid-scrollbar-corner,
|
|
2246
|
-
.sv-grid-scrollbar-corner-br {
|
|
2247
|
-
border-bottom-color: var(--sg-border, #e2e8f0);
|
|
2248
|
-
}
|
|
2249
|
-
|
|
2250
|
-
/* Filter operator <select> (the dropdown that replaced the radio group). */
|
|
2251
|
-
.sv-grid-menu-operator-select {
|
|
2252
|
-
width: 100%;
|
|
2253
|
-
height: 28px;
|
|
2254
|
-
padding: 0 22px 0 8px;
|
|
2255
|
-
margin-bottom: 6px;
|
|
2256
|
-
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
2257
|
-
border-radius: 5px;
|
|
2258
|
-
background: var(--sg-input-bg, #fff);
|
|
2259
|
-
color: var(--sg-fg, #0f172a);
|
|
2260
|
-
font: inherit;
|
|
2261
|
-
font-size: 13px;
|
|
2262
|
-
cursor: pointer;
|
|
2263
|
-
appearance: none;
|
|
2264
|
-
-webkit-appearance: none;
|
|
2265
|
-
background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
|
|
2266
|
-
linear-gradient(135deg, currentColor 50%, transparent 50%);
|
|
2267
|
-
background-position: calc(100% - 12px) 12px, calc(100% - 8px) 12px;
|
|
2268
|
-
background-size: 4px 4px, 4px 4px;
|
|
2269
|
-
background-repeat: no-repeat;
|
|
2270
|
-
}
|
|
2271
|
-
.sv-grid-menu-operator-select:focus {
|
|
2272
|
-
outline: none;
|
|
2273
|
-
border-color: var(--sg-accent, #2563eb);
|
|
2274
|
-
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 40%, transparent);
|
|
2275
|
-
}
|
|
2276
|
-
/* Operator toggle button (active) uses the on-accent token. */
|
|
2277
|
-
.sv-grid-menu-operator-btn.is-active {
|
|
2278
|
-
color: var(--sg-on-accent, #fff);
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
|
-
/* Filter-only + choose-columns popover widths. */
|
|
2282
|
-
.sv-grid-filter-menu {
|
|
2283
|
-
width: 260px;
|
|
2284
|
-
}
|
|
2285
|
-
.sv-grid-choose-columns-menu {
|
|
2286
|
-
width: 240px;
|
|
2287
|
-
}
|
|
2288
|
-
.sv-grid-choose-columns-menu .sv-grid-facet-list {
|
|
2289
|
-
max-height: 280px;
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
/* Native checkboxes in the facet list pick up the theme accent. */
|
|
2293
|
-
.sv-grid-facet input[type="checkbox"] {
|
|
2294
|
-
accent-color: var(--sg-accent, #2563eb);
|
|
2295
|
-
width: 14px;
|
|
2296
|
-
height: 14px;
|
|
2297
|
-
cursor: pointer;
|
|
2298
|
-
}
|
|
2299
|
-
|
|
2300
|
-
/* Submenu right-chevron on the "Choose columns" item. */
|
|
2301
|
-
.sv-grid-menu-item-chevron {
|
|
2302
|
-
margin-left: auto;
|
|
2303
|
-
transform: rotate(-90deg);
|
|
2304
|
-
opacity: 0.55;
|
|
2305
|
-
}
|
|
2306
|
-
.sv-grid-menu-item.is-open .sv-grid-menu-item-chevron {
|
|
2307
|
-
opacity: 1;
|
|
2308
|
-
}
|
|
2309
|
-
|
|
2310
|
-
/* Magnifier prefix on the menu's search input. */
|
|
2311
|
-
.sv-grid-menu-search {
|
|
2312
|
-
padding-left: 28px;
|
|
2313
|
-
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='6'/%3E%3Cpath d='M20 20l-4.5-4.5'/%3E%3C/svg%3E");
|
|
2314
|
-
background-position: 8px center;
|
|
2315
|
-
background-repeat: no-repeat;
|
|
2316
|
-
background-size: 14px 14px;
|
|
2317
|
-
}
|
|
2318
|
-
|
|
2319
|
-
/* Filter menu vertical rhythm. */
|
|
2320
|
-
.sv-grid-menu-filter > * + * {
|
|
2321
|
-
margin-top: 6px;
|
|
2322
|
-
}
|
|
2323
|
-
|
|
2324
|
-
/* Column-header filter (funnel) button tints accent when a filter is active. */
|
|
2325
|
-
.sv-grid-col-filter-btn.is-active {
|
|
2326
|
-
color: var(--sg-accent, #2563eb);
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
|
-
/* Row-number gutter: neutralize the right border so it fuses with the header. */
|
|
2330
|
-
.sv-grid-row-number-column,
|
|
2331
|
-
.sv-grid-row-number-cell {
|
|
2332
|
-
border-right-color: var(--sg-header-bg, #f1f5f9);
|
|
2333
|
-
}
|
|
2334
|
-
|
|
2335
|
-
/* Group row label colour. */
|
|
2336
|
-
.sv-grid-group-row > .sv-grid-cell {
|
|
2337
|
-
color: var(--sg-header-fg, #0f172a);
|
|
2338
|
-
}
|
|
2339
|
-
|
|
2340
|
-
/* Filter-row operator button: soften when idle, accent border on hover/open. */
|
|
2341
|
-
.sv-grid-filter-operator-btn {
|
|
2342
|
-
opacity: 0.85;
|
|
2343
|
-
}
|
|
2344
|
-
.sv-grid-filter-operator-btn:hover {
|
|
2345
|
-
opacity: 1;
|
|
2346
|
-
border-color: var(--sg-accent, #2563eb);
|
|
2347
|
-
}
|
|
2348
|
-
.sv-grid-filter-operator-btn.is-open {
|
|
2349
|
-
opacity: 1;
|
|
2350
|
-
}
|
|
2351
|
-
|
|
2352
|
-
/* Kanban board mode search box (board.searchable) */
|
|
2353
|
-
.sv-grid-board-search {
|
|
2354
|
-
display: flex;
|
|
2355
|
-
align-items: center;
|
|
2356
|
-
gap: 6px;
|
|
2357
|
-
margin-bottom: 8px;
|
|
2358
|
-
padding: 5px 9px;
|
|
2359
|
-
border: 1px solid var(--sg-border, #e4e7ec);
|
|
2360
|
-
border-radius: var(--sg-radius, 8px);
|
|
2361
|
-
background: var(--sg-bg, #fff);
|
|
2362
|
-
color: color-mix(in srgb, var(--sg-fg, #101828) 55%, transparent);
|
|
2363
|
-
max-width: 280px;
|
|
2364
|
-
}
|
|
2365
|
-
.sv-grid-board-search:focus-within {
|
|
2366
|
-
border-color: var(--sg-accent, #3b82f6);
|
|
2367
|
-
}
|
|
2368
|
-
.sv-grid-board-search input {
|
|
2369
|
-
flex: 1 1 auto;
|
|
2370
|
-
border: 0;
|
|
2371
|
-
outline: none;
|
|
2372
|
-
background: transparent;
|
|
2373
|
-
font: inherit;
|
|
2374
|
-
font-size: 0.82rem;
|
|
2375
|
-
color: var(--sg-fg, #101828);
|
|
2376
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* SvGrid component styles. Extracted from SvGrid.svelte's <style> block so the
|
|
3
|
+
* component file stays focused on logic + markup. These are plain (global) CSS
|
|
4
|
+
* rules - every selector is a unique .sv-grid-* class that maps to a real class
|
|
5
|
+
* on the grid's elements, so behaviour is identical to the former scoped block.
|
|
6
|
+
* Imported for its side effect by SvGrid.svelte; the WC build inlines it and
|
|
7
|
+
* app bundlers include it via the import.
|
|
8
|
+
*/
|
|
9
|
+
/* Root wrapper: holds the shell + pager + any overlays (find, tooltips
|
|
10
|
+
* pinned to the root). Position-relative so absolutely-positioned
|
|
11
|
+
* overlays (the find-in-grid toolbar) anchor to the grid, not the
|
|
12
|
+
* viewport. */
|
|
13
|
+
.sv-grid-root {
|
|
14
|
+
position: relative;
|
|
15
|
+
}
|
|
16
|
+
.sv-grid-sr-only {
|
|
17
|
+
position: absolute;
|
|
18
|
+
width: 1px;
|
|
19
|
+
height: 1px;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
clip: rect(0 0 0 0);
|
|
22
|
+
white-space: nowrap;
|
|
23
|
+
}
|
|
24
|
+
.sv-grid-loading-overlay {
|
|
25
|
+
position: absolute;
|
|
26
|
+
inset: 0;
|
|
27
|
+
z-index: 18;
|
|
28
|
+
pointer-events: auto;
|
|
29
|
+
background: color-mix(in srgb, var(--sg-bg, #fff) 35%, transparent);
|
|
30
|
+
}
|
|
31
|
+
/* Indeterminate top progress bar - the "something is happening" signal
|
|
32
|
+
that keeps the current rows visible underneath (no flash). */
|
|
33
|
+
.sv-grid-loading-bar {
|
|
34
|
+
position: absolute;
|
|
35
|
+
top: 0;
|
|
36
|
+
left: 0;
|
|
37
|
+
right: 0;
|
|
38
|
+
height: 3px;
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
background: color-mix(in srgb, var(--sg-accent, #2563eb) 18%, transparent);
|
|
41
|
+
}
|
|
42
|
+
.sv-grid-loading-bar::before {
|
|
43
|
+
content: '';
|
|
44
|
+
position: absolute;
|
|
45
|
+
inset: 0;
|
|
46
|
+
width: 40%;
|
|
47
|
+
background: var(--sg-accent, #2563eb);
|
|
48
|
+
animation: sv-grid-loading-slide 1.1s ease-in-out infinite;
|
|
49
|
+
}
|
|
50
|
+
@keyframes sv-grid-loading-slide {
|
|
51
|
+
0% { transform: translateX(-100%); }
|
|
52
|
+
100% { transform: translateX(350%); }
|
|
53
|
+
}
|
|
54
|
+
/* First-load skeleton: shimmer placeholder rows under the header. */
|
|
55
|
+
.sv-grid-skeleton {
|
|
56
|
+
position: absolute;
|
|
57
|
+
top: var(--sg-thead-h, 40px);
|
|
58
|
+
left: 0;
|
|
59
|
+
right: 0;
|
|
60
|
+
padding: 0;
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
}
|
|
63
|
+
.sv-grid-skeleton-row {
|
|
64
|
+
display: flex;
|
|
65
|
+
height: var(--sg-pinned-row-h, 36px);
|
|
66
|
+
align-items: center;
|
|
67
|
+
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
68
|
+
}
|
|
69
|
+
.sv-grid-skeleton-cell {
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
padding: 0 12px;
|
|
72
|
+
box-sizing: border-box;
|
|
73
|
+
}
|
|
74
|
+
.sv-grid-skeleton-bar {
|
|
75
|
+
display: block;
|
|
76
|
+
height: 10px;
|
|
77
|
+
width: 70%;
|
|
78
|
+
border-radius: 4px;
|
|
79
|
+
background: linear-gradient(
|
|
80
|
+
90deg,
|
|
81
|
+
color-mix(in srgb, var(--sg-muted, #94a3b8) 18%, transparent) 25%,
|
|
82
|
+
color-mix(in srgb, var(--sg-muted, #94a3b8) 32%, transparent) 50%,
|
|
83
|
+
color-mix(in srgb, var(--sg-muted, #94a3b8) 18%, transparent) 75%
|
|
84
|
+
);
|
|
85
|
+
background-size: 200% 100%;
|
|
86
|
+
animation: sv-grid-shimmer 1.3s linear infinite;
|
|
87
|
+
}
|
|
88
|
+
@keyframes sv-grid-shimmer {
|
|
89
|
+
0% { background-position: 200% 0; }
|
|
90
|
+
100% { background-position: -200% 0; }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Grid toolbar strip above the shell - hosts the tool-panel trigger (and is a
|
|
94
|
+
natural home for future grid-level actions). */
|
|
95
|
+
.sv-grid-toolbar {
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
gap: 8px;
|
|
99
|
+
padding: 6px 2px;
|
|
100
|
+
}
|
|
101
|
+
.sv-grid-toolbar-btn {
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
gap: 6px;
|
|
105
|
+
padding: 6px 12px;
|
|
106
|
+
border: 1px solid var(--sg-border, #e2e8f0);
|
|
107
|
+
border-radius: var(--sg-radius, 6px);
|
|
108
|
+
background: var(--sg-bg, #fff);
|
|
109
|
+
color: var(--sg-fg, #0f172a);
|
|
110
|
+
font-size: 12.5px;
|
|
111
|
+
font-weight: 600;
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
|
|
114
|
+
}
|
|
115
|
+
.sv-grid-toolbar-btn:hover {
|
|
116
|
+
border-color: var(--sg-accent, #2563eb);
|
|
117
|
+
color: var(--sg-accent, #2563eb);
|
|
118
|
+
}
|
|
119
|
+
.sv-grid-toolbar-btn.is-active {
|
|
120
|
+
background: var(--sg-accent, #2563eb);
|
|
121
|
+
border-color: var(--sg-accent, #2563eb);
|
|
122
|
+
color: #fff;
|
|
123
|
+
}
|
|
124
|
+
.sv-grid-tool-panel {
|
|
125
|
+
position: absolute;
|
|
126
|
+
top: 0;
|
|
127
|
+
right: 0;
|
|
128
|
+
bottom: 0;
|
|
129
|
+
/* Above the grid's sticky header (z-index 30) and custom scrollbars (z ~900)
|
|
130
|
+
so the docked panel fully overlays the grid instead of the header/right
|
|
131
|
+
columns bleeding through its top-right. Ties the menu layer (901) but the
|
|
132
|
+
menus render later in the DOM, so a column menu opened over the panel still
|
|
133
|
+
wins. */
|
|
134
|
+
z-index: 901;
|
|
135
|
+
width: 250px;
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
background: var(--sg-bg, #fff);
|
|
139
|
+
border-left: 1px solid var(--sg-border, #e2e8f0);
|
|
140
|
+
box-shadow: -8px 0 24px rgba(0, 0, 0, 0.12);
|
|
141
|
+
}
|
|
142
|
+
.sv-grid-tool-panel-head {
|
|
143
|
+
display: flex;
|
|
144
|
+
align-items: center;
|
|
145
|
+
justify-content: space-between;
|
|
146
|
+
padding: 8px 12px;
|
|
147
|
+
font-size: 12px;
|
|
148
|
+
font-weight: 700;
|
|
149
|
+
text-transform: uppercase;
|
|
150
|
+
letter-spacing: 0.05em;
|
|
151
|
+
color: var(--sg-muted, #64748b);
|
|
152
|
+
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
153
|
+
}
|
|
154
|
+
.sv-grid-tool-panel-close {
|
|
155
|
+
border: 0;
|
|
156
|
+
background: transparent;
|
|
157
|
+
color: var(--sg-muted, #64748b);
|
|
158
|
+
font-size: 14px;
|
|
159
|
+
cursor: pointer;
|
|
160
|
+
}
|
|
161
|
+
.sv-grid-tool-panel-list {
|
|
162
|
+
list-style: none;
|
|
163
|
+
margin: 0;
|
|
164
|
+
padding: 6px;
|
|
165
|
+
overflow-y: auto;
|
|
166
|
+
flex: 1;
|
|
167
|
+
}
|
|
168
|
+
.sv-grid-tool-panel-item {
|
|
169
|
+
display: flex;
|
|
170
|
+
align-items: center;
|
|
171
|
+
justify-content: space-between;
|
|
172
|
+
gap: 6px;
|
|
173
|
+
padding: 3px 6px;
|
|
174
|
+
border-radius: 5px;
|
|
175
|
+
}
|
|
176
|
+
.sv-grid-tool-panel-item:hover {
|
|
177
|
+
background: var(--sg-row-hover-bg, rgba(148, 163, 184, 0.1));
|
|
178
|
+
}
|
|
179
|
+
.sv-grid-tool-panel-vis {
|
|
180
|
+
display: flex;
|
|
181
|
+
align-items: center;
|
|
182
|
+
gap: 8px;
|
|
183
|
+
flex: 1;
|
|
184
|
+
min-width: 0;
|
|
185
|
+
font-size: 13px;
|
|
186
|
+
color: var(--sg-fg, #0f172a);
|
|
187
|
+
cursor: pointer;
|
|
188
|
+
}
|
|
189
|
+
.sv-grid-tool-panel-vis input {
|
|
190
|
+
accent-color: var(--sg-accent, #2563eb);
|
|
191
|
+
}
|
|
192
|
+
.sv-grid-tool-panel-name {
|
|
193
|
+
overflow: hidden;
|
|
194
|
+
text-overflow: ellipsis;
|
|
195
|
+
white-space: nowrap;
|
|
196
|
+
}
|
|
197
|
+
.sv-grid-tool-panel-actions {
|
|
198
|
+
display: inline-flex;
|
|
199
|
+
gap: 2px;
|
|
200
|
+
flex-shrink: 0;
|
|
201
|
+
}
|
|
202
|
+
.sv-grid-tool-panel-btn {
|
|
203
|
+
width: 22px;
|
|
204
|
+
height: 22px;
|
|
205
|
+
border: 0;
|
|
206
|
+
border-radius: 4px;
|
|
207
|
+
background: transparent;
|
|
208
|
+
color: var(--sg-muted, #64748b);
|
|
209
|
+
font-size: 13px;
|
|
210
|
+
cursor: pointer;
|
|
211
|
+
}
|
|
212
|
+
.sv-grid-tool-panel-btn:hover:not(:disabled) {
|
|
213
|
+
background: var(--sg-row-hover-bg, rgba(148, 163, 184, 0.18));
|
|
214
|
+
color: var(--sg-fg, #0f172a);
|
|
215
|
+
}
|
|
216
|
+
.sv-grid-tool-panel-btn.is-active {
|
|
217
|
+
color: var(--sg-accent, #2563eb);
|
|
218
|
+
}
|
|
219
|
+
.sv-grid-tool-panel-btn:disabled {
|
|
220
|
+
opacity: 0.35;
|
|
221
|
+
cursor: default;
|
|
222
|
+
}
|
|
223
|
+
/* Tool-panel tab bar (Columns | Filters) */
|
|
224
|
+
.sv-grid-tool-panel-tabs {
|
|
225
|
+
display: flex;
|
|
226
|
+
gap: 2px;
|
|
227
|
+
padding: 6px 6px 0;
|
|
228
|
+
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
229
|
+
}
|
|
230
|
+
.sv-grid-tool-panel-tab {
|
|
231
|
+
flex: 1;
|
|
232
|
+
padding: 6px 8px;
|
|
233
|
+
border: 0;
|
|
234
|
+
border-bottom: 2px solid transparent;
|
|
235
|
+
background: transparent;
|
|
236
|
+
color: var(--sg-muted, #64748b);
|
|
237
|
+
font-size: 12px;
|
|
238
|
+
font-weight: 600;
|
|
239
|
+
cursor: pointer;
|
|
240
|
+
}
|
|
241
|
+
.sv-grid-tool-panel-tab:hover {
|
|
242
|
+
color: var(--sg-fg, #0f172a);
|
|
243
|
+
}
|
|
244
|
+
.sv-grid-tool-panel-tab.is-active {
|
|
245
|
+
color: var(--sg-accent, #2563eb);
|
|
246
|
+
border-bottom-color: var(--sg-accent, #2563eb);
|
|
247
|
+
}
|
|
248
|
+
/* Filters tab: one control block per filterable column */
|
|
249
|
+
.sv-grid-tool-panel-filters {
|
|
250
|
+
padding: 6px;
|
|
251
|
+
overflow-y: auto;
|
|
252
|
+
flex: 1;
|
|
253
|
+
display: flex;
|
|
254
|
+
flex-direction: column;
|
|
255
|
+
gap: 8px;
|
|
256
|
+
}
|
|
257
|
+
.sv-grid-tp-filter {
|
|
258
|
+
display: flex;
|
|
259
|
+
flex-direction: column;
|
|
260
|
+
gap: 4px;
|
|
261
|
+
padding: 6px;
|
|
262
|
+
border: 1px solid var(--sg-border, #e2e8f0);
|
|
263
|
+
border-radius: 6px;
|
|
264
|
+
}
|
|
265
|
+
.sv-grid-tp-filter.is-filtered {
|
|
266
|
+
border-color: var(--sg-accent, #2563eb);
|
|
267
|
+
}
|
|
268
|
+
.sv-grid-tp-filter-head {
|
|
269
|
+
display: flex;
|
|
270
|
+
align-items: center;
|
|
271
|
+
justify-content: space-between;
|
|
272
|
+
}
|
|
273
|
+
.sv-grid-tp-filter-name {
|
|
274
|
+
font-size: 12px;
|
|
275
|
+
font-weight: 600;
|
|
276
|
+
color: var(--sg-fg, #0f172a);
|
|
277
|
+
}
|
|
278
|
+
.sv-grid-tp-filter-clear {
|
|
279
|
+
border: 0;
|
|
280
|
+
background: transparent;
|
|
281
|
+
color: var(--sg-muted, #64748b);
|
|
282
|
+
font-size: 11px;
|
|
283
|
+
cursor: pointer;
|
|
284
|
+
padding: 0 2px;
|
|
285
|
+
}
|
|
286
|
+
.sv-grid-tp-filter-clear:hover {
|
|
287
|
+
color: var(--sg-accent, #2563eb);
|
|
288
|
+
}
|
|
289
|
+
.sv-grid-tp-filter-op,
|
|
290
|
+
.sv-grid-tp-filter-input {
|
|
291
|
+
width: 100%;
|
|
292
|
+
box-sizing: border-box;
|
|
293
|
+
padding: 4px 6px;
|
|
294
|
+
font-size: 12px;
|
|
295
|
+
border: 1px solid var(--sg-border, #e2e8f0);
|
|
296
|
+
border-radius: 5px;
|
|
297
|
+
background: var(--sg-bg, #fff);
|
|
298
|
+
color: var(--sg-fg, #0f172a);
|
|
299
|
+
}
|
|
300
|
+
/* Root wrapper fill mode: only takes effect when consumer passes
|
|
301
|
+
* containerHeight='100%'. The shell becomes a flex item that expands
|
|
302
|
+
* while the pager keeps its natural height. */
|
|
303
|
+
.sv-grid-root-fill {
|
|
304
|
+
display: flex;
|
|
305
|
+
flex-direction: column;
|
|
306
|
+
height: 100%;
|
|
307
|
+
min-height: 0;
|
|
308
|
+
}
|
|
309
|
+
.sv-grid-root-fill > .sv-grid-shell {
|
|
310
|
+
flex: 1 1 0;
|
|
311
|
+
min-height: 0;
|
|
312
|
+
height: auto !important;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.sv-grid-shell {
|
|
316
|
+
position: relative;
|
|
317
|
+
border-bottom: 1px solid var(--sg-border, #cbd5e1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.sv-grid-table {
|
|
321
|
+
table-layout: fixed;
|
|
322
|
+
border-collapse: separate;
|
|
323
|
+
border-spacing: 0;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.sv-grid-table:focus {
|
|
327
|
+
outline: none;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.sv-grid-column,
|
|
331
|
+
.sv-grid-cell {
|
|
332
|
+
box-sizing: border-box;
|
|
333
|
+
overflow: hidden;
|
|
334
|
+
text-overflow: ellipsis;
|
|
335
|
+
white-space: nowrap;
|
|
336
|
+
background: var(--sg-bg, #fff);
|
|
337
|
+
color: var(--sg-fg, #0f172a);
|
|
338
|
+
user-select: none;
|
|
339
|
+
/* Grid lines ship with the grid so it looks complete out of the box - no
|
|
340
|
+
host stylesheet required. Only right + bottom per cell (border-spacing is
|
|
341
|
+
0), so adjacent cells share a single 1px line rather than doubling. */
|
|
342
|
+
border-right: 1px solid var(--sg-border, #e2e8f0);
|
|
343
|
+
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
344
|
+
}
|
|
345
|
+
/* The last cell / column in each row drops its right border so the grid's
|
|
346
|
+
right edge stays clean (the shell provides the outer frame). */
|
|
347
|
+
.sv-grid-table tr > :last-child.sv-grid-column,
|
|
348
|
+
.sv-grid-table tr > :last-child.sv-grid-cell {
|
|
349
|
+
border-right: 0;
|
|
350
|
+
}
|
|
351
|
+
/* Row hover tint - opt-in via `enableRowHover` (off by default; the class is
|
|
352
|
+
present unless the grid opts in). Uses `background-color` (not the shorthand)
|
|
353
|
+
so it never resets the fill-marquee `background-image`, and skips selected
|
|
354
|
+
cells so the selection stays visible under a hovered row. */
|
|
355
|
+
.sv-grid-table:not(.sv-grid-no-row-hover)
|
|
356
|
+
tbody tr:hover > .sv-grid-cell:not([data-selected-range="true"]) {
|
|
357
|
+
background-color: var(--sg-row-hover-bg, #eef2ff);
|
|
358
|
+
}
|
|
359
|
+
/* Only the active cell + the cell hosting the fill handle become a
|
|
360
|
+
positioning context (so the absolutely-positioned handle anchors
|
|
361
|
+
to the cell box). Adding `position: relative` to EVERY cell paints
|
|
362
|
+
each one as its own layout box and noticeably slows vertical
|
|
363
|
+
scroll on dense grids - keep it scoped. Sticky / pinned cells are
|
|
364
|
+
already positioning contexts via `position: sticky`. */
|
|
365
|
+
.sv-grid-cell-active,
|
|
366
|
+
.sv-grid-cell-has-fill-handle {
|
|
367
|
+
position: relative;
|
|
368
|
+
}
|
|
369
|
+
/* The cell that owns the fill handle gets overflow: visible so the
|
|
370
|
+
handle isn't clipped - chip cells in particular pack pills right up
|
|
371
|
+
to the cell edges, and with overflow: hidden the handle's hover
|
|
372
|
+
scale-up + the handle itself could be partially eaten by them. */
|
|
373
|
+
.sv-grid-cell-has-fill-handle {
|
|
374
|
+
overflow: visible;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* Full-width expandable detail row (props.isDetailRow / renderDetailRow).
|
|
378
|
+
Overrides the normal single-line cell clamps so the panel spans every
|
|
379
|
+
column and grows to its natural height. Pair with virtualization={false}
|
|
380
|
+
so the variable height isn't fought by the fixed-row-height virtualizer.
|
|
381
|
+
These rules sit AFTER `.sv-grid-cell` so they win at equal specificity. */
|
|
382
|
+
.sv-grid-detail-row {
|
|
383
|
+
height: auto !important;
|
|
384
|
+
}
|
|
385
|
+
.sv-grid-detail-cell {
|
|
386
|
+
overflow: visible;
|
|
387
|
+
text-overflow: clip;
|
|
388
|
+
white-space: normal;
|
|
389
|
+
height: auto;
|
|
390
|
+
padding: 0;
|
|
391
|
+
background: var(--sg-bg, #fff);
|
|
392
|
+
user-select: text;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Pinned columns read as "frozen" via three layered cues:
|
|
396
|
+
1. A distinct tint (slightly stronger than the header) so the
|
|
397
|
+
pinned strip is visible even before you scroll.
|
|
398
|
+
2. A solid 1px divider on the inside edge (toward the scrollable
|
|
399
|
+
middle) and a soft drop shadow extending into the scroll area.
|
|
400
|
+
3. A thin accent stripe on the outermost edge that turns on when
|
|
401
|
+
the grid scrolls past the pin - a familiar Excel-style cue.
|
|
402
|
+
The shadow direction flips based on which side is pinned so it
|
|
403
|
+
always points toward the scrollable middle. */
|
|
404
|
+
.sv-grid-cell[data-pinned="left"],
|
|
405
|
+
.sv-grid-column[data-pinned="left"] {
|
|
406
|
+
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
407
|
+
box-shadow:
|
|
408
|
+
inset -1px 0 0 var(--sg-pinned-divider, var(--sg-border, #cbd5e1)),
|
|
409
|
+
8px 0 12px -6px rgba(15, 23, 42, 0.22);
|
|
410
|
+
}
|
|
411
|
+
.sv-grid-cell[data-pinned="right"],
|
|
412
|
+
.sv-grid-column[data-pinned="right"] {
|
|
413
|
+
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
414
|
+
box-shadow:
|
|
415
|
+
inset 1px 0 0 var(--sg-pinned-divider, var(--sg-border, #cbd5e1)),
|
|
416
|
+
-8px 0 12px -6px rgba(15, 23, 42, 0.22);
|
|
417
|
+
}
|
|
418
|
+
/* Header row pinned cells get a slightly darker variant + bolder font
|
|
419
|
+
so the frozen header itself reads as part of the grid chrome. */
|
|
420
|
+
.sv-grid-head .sv-grid-column[data-pinned="left"],
|
|
421
|
+
.sv-grid-head .sv-grid-column[data-pinned="right"] {
|
|
422
|
+
background: var(--sg-pinned-header-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 86%, var(--sg-accent, #2563eb) 14%));
|
|
423
|
+
font-weight: 600;
|
|
424
|
+
}
|
|
425
|
+
/* Zebra striping, opt-in via the `zebraRows` prop (which adds
|
|
426
|
+
`.sv-grid-row-alt` to alternating DATA rows only - pinned, group,
|
|
427
|
+
detail, and summary rows never get the class, so they keep a single
|
|
428
|
+
background). This rule sits before `.sv-grid-row-selected` /
|
|
429
|
+
`:hover` so those states paint over the stripe. */
|
|
430
|
+
.sv-grid-row-alt > .sv-grid-cell {
|
|
431
|
+
background: var(--sg-row-alt-bg, #f8fafc);
|
|
432
|
+
}
|
|
433
|
+
/* Zebra rows: keep the pinned tint visible (don't let the row-alt
|
|
434
|
+
background bleed through) by re-painting the pinned cells. */
|
|
435
|
+
.sv-grid-row-alt > .sv-grid-cell[data-pinned] {
|
|
436
|
+
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
437
|
+
}
|
|
438
|
+
/* When the row is selected, keep the pinned tint but layer the
|
|
439
|
+
selection color over it so the row still reads as selected. */
|
|
440
|
+
.sv-grid-row-selected > .sv-grid-cell[data-pinned] {
|
|
441
|
+
background:
|
|
442
|
+
linear-gradient(
|
|
443
|
+
color-mix(in srgb, var(--sg-selection-bg, #dbeafe) 65%, transparent),
|
|
444
|
+
color-mix(in srgb, var(--sg-selection-bg, #dbeafe) 65%, transparent)
|
|
445
|
+
),
|
|
446
|
+
var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
447
|
+
}
|
|
448
|
+
/* Hovered row keeps the pinned tint distinguishable from the hover. Selected
|
|
449
|
+
cells are excluded so the selection stays visible under hover. */
|
|
450
|
+
.sv-grid-table:not(.sv-grid-no-row-hover)
|
|
451
|
+
.sv-grid-row:hover > .sv-grid-cell[data-pinned]:not([data-selected-range="true"]) {
|
|
452
|
+
background:
|
|
453
|
+
linear-gradient(
|
|
454
|
+
color-mix(in srgb, var(--sg-row-hover-bg, #eef2ff) 55%, transparent),
|
|
455
|
+
color-mix(in srgb, var(--sg-row-hover-bg, #eef2ff) 55%, transparent)
|
|
456
|
+
),
|
|
457
|
+
var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.sv-grid-column {
|
|
461
|
+
position: relative;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/* Column alignment via data-align attribute (see getColumnAlign helper).
|
|
465
|
+
* For body cells text-align is enough; for headers the label lives inside
|
|
466
|
+
* a flex container so we adjust justify-content on the sort button.
|
|
467
|
+
* Padding is biased to the aligned side: left-aligned text gets a 7px
|
|
468
|
+
* left gap; right-aligned numbers/dates get a 7px right gap. */
|
|
469
|
+
.sv-grid-cell[data-align="left"],
|
|
470
|
+
.sv-grid-column[data-align="left"] {
|
|
471
|
+
padding-left: var(--sg-cell-px, 7px);
|
|
472
|
+
}
|
|
473
|
+
.sv-grid-cell[data-align="right"] {
|
|
474
|
+
text-align: right;
|
|
475
|
+
padding-right: var(--sg-cell-px, 7px);
|
|
476
|
+
}
|
|
477
|
+
.sv-grid-cell[data-align="center"] {
|
|
478
|
+
text-align: center;
|
|
479
|
+
}
|
|
480
|
+
.sv-grid-column[data-align="right"] {
|
|
481
|
+
padding-right: var(--sg-cell-px, 7px);
|
|
482
|
+
}
|
|
483
|
+
.sv-grid-column[data-align="right"] .sv-grid-header-sort {
|
|
484
|
+
justify-content: flex-end;
|
|
485
|
+
}
|
|
486
|
+
/* The custom vertical scrollbar overlays the right 16px of the viewport
|
|
487
|
+
(absolute, z-index 40). Nudge the last column's right-aligned content in by
|
|
488
|
+
that width when the scrollbar is visible, so numbers/values aren't hidden
|
|
489
|
+
underneath it. Left/center content already clears the right edge. */
|
|
490
|
+
.sv-grid-has-vscroll .sv-grid-table tr > :last-child.sv-grid-cell[data-align="right"],
|
|
491
|
+
.sv-grid-has-vscroll .sv-grid-table tr > :last-child.sv-grid-column[data-align="right"] {
|
|
492
|
+
padding-right: calc(var(--sg-cell-px, 7px) + 16px);
|
|
493
|
+
}
|
|
494
|
+
.sv-grid-column[data-align="center"] .sv-grid-header-sort {
|
|
495
|
+
justify-content: center;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.sv-grid-resize-handle {
|
|
499
|
+
position: absolute;
|
|
500
|
+
top: 0;
|
|
501
|
+
right: 0;
|
|
502
|
+
width: 5px;
|
|
503
|
+
height: 100%;
|
|
504
|
+
cursor: col-resize;
|
|
505
|
+
user-select: none;
|
|
506
|
+
z-index: 2;
|
|
507
|
+
transition: background-color 100ms ease;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* An accent center pill (rather than tinting the whole strip): faint on column
|
|
511
|
+
hover, bright when the handle itself is hovered or while dragging. */
|
|
512
|
+
.sv-grid-resize-handle::after {
|
|
513
|
+
content: "";
|
|
514
|
+
position: absolute;
|
|
515
|
+
inset: 0;
|
|
516
|
+
margin: auto 2px;
|
|
517
|
+
width: 2px;
|
|
518
|
+
background: var(--sg-fg, #0f172a);
|
|
519
|
+
opacity: 0;
|
|
520
|
+
border-radius: 1px;
|
|
521
|
+
transition: background-color 100ms ease, opacity 100ms ease;
|
|
522
|
+
}
|
|
523
|
+
.sv-grid-column:hover .sv-grid-resize-handle::after {
|
|
524
|
+
opacity: 0.3;
|
|
525
|
+
}
|
|
526
|
+
.sv-grid-column:hover .sv-grid-resize-handle:hover::after,
|
|
527
|
+
.sv-grid-resize-handle.is-resizing::after {
|
|
528
|
+
background: var(--sg-accent, #2563eb);
|
|
529
|
+
opacity: 1;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/* Row resize handle (counterpart of the column resize handle). Lives
|
|
533
|
+
at the bottom of the row-header gutter cell; the consumer opts in
|
|
534
|
+
by attaching the `rowResize` action and setting
|
|
535
|
+
`cellClass: 'sv-row-gutter'` on the row-header column. */
|
|
536
|
+
.sv-grid-row-resize-handle {
|
|
537
|
+
/* Geometry + interactivity are also set inline by the rowResize
|
|
538
|
+
action so the strip works even if this stylesheet hasn't been
|
|
539
|
+
parsed yet. This rule only owns the hover/active tint. */
|
|
540
|
+
transition: background-color 100ms ease;
|
|
541
|
+
}
|
|
542
|
+
.sv-grid-row-resize-handle:hover,
|
|
543
|
+
.sv-grid-row-resize-handle.is-resizing {
|
|
544
|
+
background: color-mix(in srgb, var(--sg-accent, #0b63f3) 30%, transparent);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.sv-grid-head {
|
|
548
|
+
position: sticky;
|
|
549
|
+
top: 0;
|
|
550
|
+
/* Above pinned body cells (z-index 30 via cellPinStyle). Without
|
|
551
|
+
this, when a row scrolls partially behind the sticky header,
|
|
552
|
+
its pinned cells (left + right) paint OVER the header while the
|
|
553
|
+
middle non-pinned cells are correctly hidden behind it, leaving
|
|
554
|
+
"floating" symbol / pnl cells at the top of the viewport. */
|
|
555
|
+
z-index: 35;
|
|
556
|
+
background: var(--sg-header-bg, #f5f7fb);
|
|
557
|
+
}
|
|
558
|
+
/* Column-header cells take the header background (and header foreground)
|
|
559
|
+
so the header row paints as a solid band rather than body-colored
|
|
560
|
+
cells over the sticky header strip. Together with the row-number
|
|
561
|
+
gutter below - which uses the same --sg-header-bg - the column headers
|
|
562
|
+
and row headers read as one continuous header chrome. */
|
|
563
|
+
.sv-grid-head .sv-grid-column {
|
|
564
|
+
background: var(--sg-header-bg, #f5f7fb);
|
|
565
|
+
/* Header typography is themeable so each design-system preset can match
|
|
566
|
+
its real grid header (e.g. Excel bold, Atlassian uppercase + subtle,
|
|
567
|
+
shadcn medium + muted). The label/sort-button inherit these. */
|
|
568
|
+
color: var(--sg-header-label-color, var(--sg-header-fg, var(--sg-fg, #0f172a)));
|
|
569
|
+
font-weight: var(--sg-header-weight, 600);
|
|
570
|
+
font-size: var(--sg-header-size, inherit);
|
|
571
|
+
text-transform: var(--sg-header-transform, none);
|
|
572
|
+
letter-spacing: var(--sg-header-tracking, normal);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/* Header drag-to-reorder. The `draggable` attribute is only set
|
|
576
|
+
* when `enableColumnReorder` is true, so the cursor and drop
|
|
577
|
+
* indicators only show in that mode. */
|
|
578
|
+
.sv-grid-column[draggable="true"] { cursor: grab; }
|
|
579
|
+
.sv-grid-column[draggable="true"]:active { cursor: grabbing; }
|
|
580
|
+
.sv-grid-column.is-dragging { opacity: 0.55; }
|
|
581
|
+
.sv-grid-column.is-drag-target-before,
|
|
582
|
+
.sv-grid-column.is-drag-target-after {
|
|
583
|
+
position: relative;
|
|
584
|
+
}
|
|
585
|
+
.sv-grid-column.is-drag-target-before::before,
|
|
586
|
+
.sv-grid-column.is-drag-target-after::after {
|
|
587
|
+
content: "";
|
|
588
|
+
position: absolute; top: 4px; bottom: 4px; width: 3px;
|
|
589
|
+
background: linear-gradient(180deg, #6366f1, #8b5cf6);
|
|
590
|
+
border-radius: 2px;
|
|
591
|
+
box-shadow: 0 0 6px #6366f1;
|
|
592
|
+
pointer-events: none;
|
|
593
|
+
z-index: 7;
|
|
594
|
+
animation: sg-drop-pulse 700ms ease-in-out infinite alternate;
|
|
595
|
+
}
|
|
596
|
+
.sv-grid-column.is-drag-target-before::before { left: -2px; }
|
|
597
|
+
.sv-grid-column.is-drag-target-after::after { right: -2px; }
|
|
598
|
+
@keyframes sg-drop-pulse { from { opacity: 0.55; } to { opacity: 1; } }
|
|
599
|
+
|
|
600
|
+
/* Managed row dragging ----------------------------------------------------- */
|
|
601
|
+
/* The whole row is the drag source; the row-number cell shows a grip so the
|
|
602
|
+
* affordance is discoverable. Pair `rowDragManaged` with `showRowNumbers` for
|
|
603
|
+
* the grip; without row numbers the grab cursor still hints it. */
|
|
604
|
+
.sv-grid-row-draggable { cursor: grab; }
|
|
605
|
+
.sv-grid-row-draggable:active { cursor: grabbing; }
|
|
606
|
+
.sv-grid-row-draggable .sv-grid-row-number-cell {
|
|
607
|
+
position: relative;
|
|
608
|
+
padding-left: 14px;
|
|
609
|
+
}
|
|
610
|
+
.sv-grid-row-draggable .sv-grid-row-number-cell::before {
|
|
611
|
+
content: "\2807"; /* braille grip glyph */
|
|
612
|
+
position: absolute;
|
|
613
|
+
left: 3px;
|
|
614
|
+
top: 50%;
|
|
615
|
+
transform: translateY(-50%);
|
|
616
|
+
font-size: 13px;
|
|
617
|
+
line-height: 1;
|
|
618
|
+
color: var(--sg-muted, #94a3b8);
|
|
619
|
+
opacity: 0.4;
|
|
620
|
+
transition: opacity 120ms ease;
|
|
621
|
+
pointer-events: none;
|
|
622
|
+
}
|
|
623
|
+
.sv-grid-row-draggable:hover .sv-grid-row-number-cell::before { opacity: 1; }
|
|
624
|
+
|
|
625
|
+
/* Default highlight for an external drop zone (use:rowDropZone) while a
|
|
626
|
+
matching row is dragged over it. Consumers can override the class. */
|
|
627
|
+
.sv-grid-row-dropzone-over {
|
|
628
|
+
outline: 2px dashed var(--sg-accent, #6366f1);
|
|
629
|
+
outline-offset: -2px;
|
|
630
|
+
background: color-mix(in srgb, var(--sg-accent, #6366f1) 10%, transparent);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/* Drop indicator: a glowing line on the row's top (before) or bottom (after)
|
|
634
|
+
* edge. Painted on the cells so it spans the full row across table layouts. */
|
|
635
|
+
.sv-grid-row-drop-before > td {
|
|
636
|
+
box-shadow: inset 0 2px 0 0 var(--sg-accent, #6366f1);
|
|
637
|
+
}
|
|
638
|
+
.sv-grid-row-drop-after > td {
|
|
639
|
+
box-shadow: inset 0 -2px 0 0 var(--sg-accent, #6366f1);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/* Pinned rows (top / bottom). Each cell sticks individually so the
|
|
643
|
+
* row tracks scroll on every browser. Top rows stick below the sticky
|
|
644
|
+
* thead via --sg-thead-h; bottom rows stick to the bottom of the
|
|
645
|
+
* scroll container. Tinted background distinguishes them from
|
|
646
|
+
* regular rows. */
|
|
647
|
+
.sv-grid-pinned-row td {
|
|
648
|
+
position: sticky;
|
|
649
|
+
z-index: 4;
|
|
650
|
+
background: var(--sg-pinned-bg, color-mix(in oklab, #6366f1 4%, #ffffff));
|
|
651
|
+
border-bottom: 1px solid var(--sg-pinned-border, color-mix(in oklab, #6366f1 24%, transparent));
|
|
652
|
+
font-weight: 600;
|
|
653
|
+
}
|
|
654
|
+
.sv-grid-pinned-row-top td { top: var(--sg-thead-h, 36px); }
|
|
655
|
+
.sv-grid-pinned-row-bottom td { bottom: 0; border-top: 1px solid var(--sg-pinned-border, color-mix(in oklab, #6366f1 24%, transparent)); }
|
|
656
|
+
/* Stack multiple pinned-top rows: row 1 stops at thead, row 2 stops
|
|
657
|
+
* at thead + 36px, etc. The component itself sets --sg-pinned-top-h
|
|
658
|
+
* to the current pinned-top stack height via $effect. */
|
|
659
|
+
.sv-grid-pinned-row-top[data-pinned-index="1"] td { top: calc(var(--sg-thead-h, 36px) + var(--sg-pinned-row-h, 32px)); }
|
|
660
|
+
.sv-grid-pinned-row-top[data-pinned-index="2"] td { top: calc(var(--sg-thead-h, 36px) + var(--sg-pinned-row-h, 32px) * 2); }
|
|
661
|
+
.sv-grid-pinned-row-top[data-pinned-index="3"] td { top: calc(var(--sg-thead-h, 36px) + var(--sg-pinned-row-h, 32px) * 3); }
|
|
662
|
+
.sv-grid-pinned-row-bottom[data-pinned-index="1"] td { bottom: var(--sg-pinned-row-h, 32px); }
|
|
663
|
+
.sv-grid-pinned-row-bottom[data-pinned-index="2"] td { bottom: calc(var(--sg-pinned-row-h, 32px) * 2); }
|
|
664
|
+
.sv-grid-pinned-row-bottom[data-pinned-index="3"] td { bottom: calc(var(--sg-pinned-row-h, 32px) * 3); }
|
|
665
|
+
/* Sticky-left columns keep their left position; just override their
|
|
666
|
+
* z so they sit above non-pinned pinned-row cells. */
|
|
667
|
+
.sv-grid-pinned-row td[data-pinned] { z-index: 5; }
|
|
668
|
+
|
|
669
|
+
/* Group header rows for multi-level (pivot-style) headers. Sits above
|
|
670
|
+
* the standard leaf header row when the column tree has nesting. */
|
|
671
|
+
.sv-grid-group-header-row {
|
|
672
|
+
background: var(--sg-header-bg, #eef2f8);
|
|
673
|
+
}
|
|
674
|
+
.sv-grid-group-header-cell {
|
|
675
|
+
box-sizing: border-box;
|
|
676
|
+
border-bottom: 1px solid var(--sg-border, #d8dee9);
|
|
677
|
+
border-right: 1px solid var(--sg-border, #e2e8f0);
|
|
678
|
+
text-align: center;
|
|
679
|
+
padding: 4px 8px;
|
|
680
|
+
font-weight: 700;
|
|
681
|
+
font-size: 11.5px;
|
|
682
|
+
color: var(--sg-header-fg, #475569);
|
|
683
|
+
text-transform: uppercase;
|
|
684
|
+
letter-spacing: 0.04em;
|
|
685
|
+
}
|
|
686
|
+
.sv-grid-group-header-placeholder {
|
|
687
|
+
background: transparent;
|
|
688
|
+
border-right-color: transparent;
|
|
689
|
+
}
|
|
690
|
+
.sv-grid-group-header-label {
|
|
691
|
+
display: inline-block;
|
|
692
|
+
max-width: 100%;
|
|
693
|
+
overflow: hidden;
|
|
694
|
+
text-overflow: ellipsis;
|
|
695
|
+
white-space: nowrap;
|
|
696
|
+
}
|
|
697
|
+
/* Collapse toggle on a collapsible column group header. */
|
|
698
|
+
.sv-grid-group-toggle {
|
|
699
|
+
display: inline-flex;
|
|
700
|
+
align-items: center;
|
|
701
|
+
gap: 5px;
|
|
702
|
+
max-width: 100%;
|
|
703
|
+
border: 0;
|
|
704
|
+
background: transparent;
|
|
705
|
+
color: inherit;
|
|
706
|
+
font: inherit;
|
|
707
|
+
letter-spacing: inherit;
|
|
708
|
+
text-transform: inherit;
|
|
709
|
+
cursor: pointer;
|
|
710
|
+
padding: 2px 6px;
|
|
711
|
+
border-radius: 5px;
|
|
712
|
+
}
|
|
713
|
+
.sv-grid-group-toggle:hover {
|
|
714
|
+
background: color-mix(in srgb, var(--sg-accent, #2563eb) 12%, transparent);
|
|
715
|
+
color: var(--sg-accent, #2563eb);
|
|
716
|
+
}
|
|
717
|
+
.sv-grid-group-caret {
|
|
718
|
+
flex-shrink: 0;
|
|
719
|
+
transition: transform 140ms ease;
|
|
720
|
+
}
|
|
721
|
+
/* Collapsed: point the caret right (like a closed disclosure). */
|
|
722
|
+
.sv-grid-group-toggle.is-collapsed .sv-grid-group-caret {
|
|
723
|
+
transform: rotate(-90deg);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.sv-grid-foot {
|
|
727
|
+
position: sticky;
|
|
728
|
+
bottom: 0;
|
|
729
|
+
z-index: 6;
|
|
730
|
+
background: var(--sg-header-bg, #f5f7fb);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/* Responsive (narrow) mode: smooth momentum touch panning, and let touch
|
|
734
|
+
gestures pan the grid freely (the wheel-trap contain stays for pointer). */
|
|
735
|
+
.sv-grid-container.sv-grid-narrow {
|
|
736
|
+
-webkit-overflow-scrolling: touch;
|
|
737
|
+
scroll-behavior: auto;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
.sv-grid-container {
|
|
741
|
+
scrollbar-width: auto;
|
|
742
|
+
-ms-overflow-style: auto;
|
|
743
|
+
/* Trap wheel scroll inside the grid. Without this, hitting the top
|
|
744
|
+
* or bottom edge of the grid lets the wheel event chain to the
|
|
745
|
+
* outer page (or any scrollable ancestor), so what feels like one
|
|
746
|
+
* wheel notch keeps scrolling something else - the "starts and
|
|
747
|
+
* doesn't stop" symptom. `contain` cancels the chained scroll
|
|
748
|
+
* but still bounces normally inside the grid. */
|
|
749
|
+
overscroll-behavior: contain;
|
|
750
|
+
/* Disable scroll anchoring. The virtualizer changes the top-spacer
|
|
751
|
+
* height every time the visible row window slides - modern browsers
|
|
752
|
+
* (default `overflow-anchor: auto`) react by quietly adjusting
|
|
753
|
+
* `scrollTop` to keep the visible content stable. That adjustment
|
|
754
|
+
* fires another `scroll` event, which fires `onBodyScroll`, which
|
|
755
|
+
* re-runs the virtualizer, which shifts the spacer again, which
|
|
756
|
+
* adjusts `scrollTop` again - a self-sustaining loop that
|
|
757
|
+
* presents as "the grid keeps scrolling after the wheel stops."
|
|
758
|
+
* Turning anchoring off lets the virtualizer own the scroll
|
|
759
|
+
* position outright. */
|
|
760
|
+
overflow-anchor: none;
|
|
761
|
+
/* Tell the browser that the sticky thead occupies the top
|
|
762
|
+
`--sg-thead-h` pixels of the scroll container. `scrollIntoView`
|
|
763
|
+
and friends use this to avoid scrolling a row underneath the
|
|
764
|
+
sticky header - critical for Excel-style keyboard nav. */
|
|
765
|
+
scroll-padding-top: var(--sg-thead-h, 36px);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
.sv-grid-container.sv-grid-container-custom-scrollbars {
|
|
769
|
+
scrollbar-width: none;
|
|
770
|
+
-ms-overflow-style: none;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
.sv-grid-container.sv-grid-container-custom-scrollbars::-webkit-scrollbar {
|
|
774
|
+
display: none;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.sv-grid-scrollbar {
|
|
778
|
+
position: absolute;
|
|
779
|
+
/* Above pinned columns (z-index 30 via cellPinStyle) so a frozen
|
|
780
|
+
left/right column never paints over the custom scrollbar - that
|
|
781
|
+
was the bug behind "vertical scrollbar hidden under the frozen
|
|
782
|
+
column" in grids with a pinned column at the right edge. */
|
|
783
|
+
z-index: 40;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/* Scrollbars anchor to the inline-end edge so RTL flips them to the
|
|
787
|
+
* left automatically. `inset-inline-end` resolves to `right` in LTR
|
|
788
|
+
* and `left` in RTL, no manual override needed. */
|
|
789
|
+
.sv-grid-scrollbar-vertical {
|
|
790
|
+
top: 0;
|
|
791
|
+
inset-inline-end: 0;
|
|
792
|
+
width: 16px;
|
|
793
|
+
height: calc(100% - 16px);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.sv-grid-scrollbar-corner {
|
|
797
|
+
position: absolute;
|
|
798
|
+
top: 0;
|
|
799
|
+
inset-inline-end: 0;
|
|
800
|
+
width: 16px;
|
|
801
|
+
background: var(--sg-header-bg, #f5f7fb);
|
|
802
|
+
border-bottom: 1px solid rgba(15, 23, 42, 0.08);
|
|
803
|
+
/* Above pinned header cells (z-index 30) so the gap above the
|
|
804
|
+
vertical scrollbar stays clean over a frozen column. */
|
|
805
|
+
z-index: 41;
|
|
806
|
+
pointer-events: none;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
.sv-grid-scrollbar-corner-br {
|
|
810
|
+
position: absolute;
|
|
811
|
+
inset-inline-end: 0;
|
|
812
|
+
bottom: 0;
|
|
813
|
+
width: 16px;
|
|
814
|
+
height: 16px;
|
|
815
|
+
background: var(--sg-header-bg, #eef2f8);
|
|
816
|
+
box-shadow: inset 0 0 0 1px rgba(15, 23, 42, 0.05);
|
|
817
|
+
z-index: 42;
|
|
818
|
+
pointer-events: none;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
.sv-grid-scrollbar-horizontal {
|
|
822
|
+
inset-inline-start: 0;
|
|
823
|
+
bottom: 0;
|
|
824
|
+
width: calc(100% - 16px);
|
|
825
|
+
height: 16px;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.sv-grid-selection-column,
|
|
829
|
+
.sv-grid-selection-cell {
|
|
830
|
+
text-align: center;
|
|
831
|
+
padding-inline: 6px;
|
|
832
|
+
position: sticky;
|
|
833
|
+
z-index: 4;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/* Row-number column: sticks to the left edge of the scroll viewport. */
|
|
837
|
+
.sv-grid-row-number-column,
|
|
838
|
+
.sv-grid-row-number-cell {
|
|
839
|
+
text-align: right;
|
|
840
|
+
padding-inline: 8px;
|
|
841
|
+
position: sticky;
|
|
842
|
+
left: 0;
|
|
843
|
+
z-index: 4;
|
|
844
|
+
/* Match the column-header band so the row-number gutter and the column
|
|
845
|
+
headers form one continuous header chrome. */
|
|
846
|
+
background: var(--sg-header-bg, #f5f7fb);
|
|
847
|
+
color: var(--sg-muted, #64748b);
|
|
848
|
+
font-variant-numeric: tabular-nums;
|
|
849
|
+
}
|
|
850
|
+
.sv-grid-row-number-head {
|
|
851
|
+
color: inherit;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.sv-grid-filter-row-control {
|
|
855
|
+
display: flex;
|
|
856
|
+
flex-direction: row;
|
|
857
|
+
align-items: center;
|
|
858
|
+
gap: 4px;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
.sv-grid-filter-operator {
|
|
862
|
+
flex: 2 1 0;
|
|
863
|
+
min-width: 0;
|
|
864
|
+
box-sizing: border-box;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
.sv-grid-filter-value {
|
|
868
|
+
flex: 3 1 0;
|
|
869
|
+
min-width: 0;
|
|
870
|
+
box-sizing: border-box;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/* Brief glow used when the user clicks the funnel icon while the grid
|
|
874
|
+
is in row-filter mode - draws attention to where the input is so
|
|
875
|
+
the click isn't a no-op. */
|
|
876
|
+
.sv-grid-filter-value.sv-grid-filter-value-pulse {
|
|
877
|
+
animation: sv-grid-filter-pulse 700ms ease-out;
|
|
878
|
+
}
|
|
879
|
+
@keyframes sv-grid-filter-pulse {
|
|
880
|
+
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--sg-accent, #0b63f3) 55%, transparent); }
|
|
881
|
+
70% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--sg-accent, #0b63f3) 0%, transparent); }
|
|
882
|
+
100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--sg-accent, #0b63f3) 0%, transparent); }
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
.sv-grid-checkbox {
|
|
886
|
+
position: relative;
|
|
887
|
+
display: inline-flex;
|
|
888
|
+
align-items: center;
|
|
889
|
+
justify-content: center;
|
|
890
|
+
width: 16px;
|
|
891
|
+
height: 16px;
|
|
892
|
+
margin: 0 auto;
|
|
893
|
+
border: 1px solid var(--sg-input-border, #8794a8);
|
|
894
|
+
border-radius: 4px;
|
|
895
|
+
background: var(--sg-input-bg, #fff);
|
|
896
|
+
color: var(--sg-accent, #0b63f3);
|
|
897
|
+
cursor: pointer;
|
|
898
|
+
transition:
|
|
899
|
+
border-color 120ms ease,
|
|
900
|
+
background-color 120ms ease;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/* Full-row editor: inline editors shown for every editable cell of a row
|
|
904
|
+
in full-row edit. Fill the cell so the row reads as a form line. */
|
|
905
|
+
.sv-grid-fr-editor {
|
|
906
|
+
width: 100%;
|
|
907
|
+
box-sizing: border-box;
|
|
908
|
+
}
|
|
909
|
+
select.sv-grid-fr-editor {
|
|
910
|
+
height: 100%;
|
|
911
|
+
min-height: 26px;
|
|
912
|
+
}
|
|
913
|
+
.sv-grid-fr-checkbox {
|
|
914
|
+
width: 16px;
|
|
915
|
+
height: 16px;
|
|
916
|
+
accent-color: var(--sg-accent, #3b82f6);
|
|
917
|
+
cursor: pointer;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/* Color editor - native HTML color input filling the cell so the
|
|
921
|
+
swatch is clickable across the whole cell area, not just the tiny
|
|
922
|
+
OS default size. */
|
|
923
|
+
.sv-grid-cell-editor-color {
|
|
924
|
+
width: 100%;
|
|
925
|
+
height: 100%;
|
|
926
|
+
padding: 2px;
|
|
927
|
+
border: 0;
|
|
928
|
+
background: transparent;
|
|
929
|
+
cursor: pointer;
|
|
930
|
+
}
|
|
931
|
+
.sv-grid-cell-editor-color::-webkit-color-swatch-wrapper { padding: 0; }
|
|
932
|
+
.sv-grid-cell-editor-color::-webkit-color-swatch { border: 0; border-radius: 4px; }
|
|
933
|
+
.sv-grid-cell-editor-color::-moz-color-swatch { border: 0; border-radius: 4px; }
|
|
934
|
+
|
|
935
|
+
/* Rating editor - 5 clickable stars + clear button. Renders inline
|
|
936
|
+
in the cell when editorType="rating"; the readonly view renders a
|
|
937
|
+
filled-star count via the default text path (or a snippet cell). */
|
|
938
|
+
.sv-grid-rating-editor {
|
|
939
|
+
display: inline-flex;
|
|
940
|
+
align-items: center;
|
|
941
|
+
gap: 1px;
|
|
942
|
+
height: 100%;
|
|
943
|
+
}
|
|
944
|
+
.sv-grid-rating-star {
|
|
945
|
+
border: 0; background: transparent; cursor: pointer;
|
|
946
|
+
color: var(--sg-rating-empty, #cbd5e1);
|
|
947
|
+
font-size: 18px; line-height: 1;
|
|
948
|
+
padding: 0 1px;
|
|
949
|
+
transition: color 80ms ease, transform 80ms ease;
|
|
950
|
+
}
|
|
951
|
+
.sv-grid-rating-star-on { color: var(--sg-rating-on, #f59e0b); }
|
|
952
|
+
.sv-grid-rating-star:hover { color: var(--sg-rating-hover, #fbbf24); transform: scale(1.12); }
|
|
953
|
+
.sv-grid-rating-clear {
|
|
954
|
+
margin-left: 4px;
|
|
955
|
+
border: 0; background: transparent;
|
|
956
|
+
color: var(--sg-muted, #94a3b8);
|
|
957
|
+
cursor: pointer; font-size: 14px;
|
|
958
|
+
}
|
|
959
|
+
.sv-grid-rating-clear:hover { color: var(--sg-fg, #0f172a); }
|
|
960
|
+
|
|
961
|
+
.sv-grid-checkbox[aria-checked="true"],
|
|
962
|
+
.sv-grid-checkbox[aria-checked="mixed"] {
|
|
963
|
+
border-color: var(--sg-accent, #2563eb);
|
|
964
|
+
background: var(--sg-accent, #2563eb);
|
|
965
|
+
color: var(--sg-on-accent, #fff);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
.sv-grid-checkbox[aria-checked="true"]::after {
|
|
969
|
+
content: "";
|
|
970
|
+
width: 5px;
|
|
971
|
+
height: 9px;
|
|
972
|
+
border-right: 2px solid currentColor;
|
|
973
|
+
border-bottom: 2px solid currentColor;
|
|
974
|
+
transform: rotate(40deg) translate(-1px, -1px);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
.sv-grid-checkbox[aria-checked="mixed"]::after {
|
|
978
|
+
content: "";
|
|
979
|
+
width: 8px;
|
|
980
|
+
height: 2px;
|
|
981
|
+
border-radius: 1px;
|
|
982
|
+
background: currentColor;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.sv-grid-checkbox:focus-visible {
|
|
986
|
+
outline: 2px solid var(--sg-accent, #0b63f3);
|
|
987
|
+
outline-offset: 1px;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
.sv-grid-checkbox-readonly {
|
|
991
|
+
cursor: default;
|
|
992
|
+
pointer-events: none;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/* Built-in cell flash (ColumnDef `cellFlash`). Theme-tinted fade; consumers
|
|
996
|
+
can pass their own class (e.g. up/down colouring) via `cellFlash.className`. */
|
|
997
|
+
@keyframes sv-grid-cell-flash-kf {
|
|
998
|
+
0% { background-color: var(--sg-cell-flash, color-mix(in oklab, var(--sg-accent, #3b82f6) 42%, transparent)); }
|
|
999
|
+
100% { background-color: transparent; }
|
|
1000
|
+
}
|
|
1001
|
+
.sv-grid-cell-flash {
|
|
1002
|
+
animation: sv-grid-cell-flash-kf 0.7s ease-out;
|
|
1003
|
+
}
|
|
1004
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1005
|
+
.sv-grid-cell-flash { animation: none; }
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
.sv-grid-cell-active,
|
|
1009
|
+
.sv-grid-cell-editing {
|
|
1010
|
+
position: relative;
|
|
1011
|
+
/* High enough to sit above pinned-column stacking contexts (z=4) so the
|
|
1012
|
+
list/chips popover overlays adjacent cells instead of being clipped. */
|
|
1013
|
+
z-index: 20;
|
|
1014
|
+
/* `box-shadow: inset` is bound to the cell's own box, so the ring
|
|
1015
|
+
clips cleanly with the cell when it's partially scrolled out of
|
|
1016
|
+
view (and shows up consistently at the right edge of a virtualized
|
|
1017
|
+
window). The previous `outline + outline-offset: -2px` rendered at
|
|
1018
|
+
the wrong position on clipped cells in some browsers - it's drawn
|
|
1019
|
+
outside the layout box, so the negative offset's effect was
|
|
1020
|
+
inconsistent at scroll boundaries. */
|
|
1021
|
+
box-shadow: inset 0 0 0 2px var(--sg-accent, #0b63f3);
|
|
1022
|
+
outline: none;
|
|
1023
|
+
/* Let list/chips popouts extend past the cell box (default cells have
|
|
1024
|
+
overflow: hidden so the chips picker would otherwise be clipped). */
|
|
1025
|
+
overflow: visible;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/* Excel-style fill handle: a 7×7 blue square anchored to the bottom-
|
|
1029
|
+
right corner of the active cell or selection range. The parent
|
|
1030
|
+
`.sv-grid-cell` is `position: relative` (sticky cells override
|
|
1031
|
+
that with sticky - which is also a positioning context), so the
|
|
1032
|
+
handle anchors to the cell box. */
|
|
1033
|
+
/* Excel-style per-cell note indicator. A small triangle in the top-
|
|
1034
|
+
* right corner that, when hovered, shows the note tooltip. The
|
|
1035
|
+
* triangle IS the hot-zone (12×12 square clipped to a triangle via
|
|
1036
|
+
* border tricks); a transparent invisible square on top widens the
|
|
1037
|
+
* hit target a little so it's not painful to hit. */
|
|
1038
|
+
.sv-grid-cell-note-corner {
|
|
1039
|
+
position: absolute;
|
|
1040
|
+
top: 0; right: 0;
|
|
1041
|
+
width: 12px; height: 12px;
|
|
1042
|
+
/* Triangle: 0,0 → 12,0 → 12,12 */
|
|
1043
|
+
background:
|
|
1044
|
+
linear-gradient(45deg, transparent 50%, var(--sg-note-corner, #f59e0b) 50%);
|
|
1045
|
+
cursor: help;
|
|
1046
|
+
z-index: 6;
|
|
1047
|
+
}
|
|
1048
|
+
/* Slightly larger invisible hit area so users don't have to land on
|
|
1049
|
+
a 6×6 pixel triangle. */
|
|
1050
|
+
.sv-grid-cell-note-corner::before {
|
|
1051
|
+
content: '';
|
|
1052
|
+
position: absolute;
|
|
1053
|
+
inset: -3px -3px 0 0;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
.sv-grid-fill-handle {
|
|
1057
|
+
position: absolute;
|
|
1058
|
+
/* Sit fully INSIDE the cell box so `overflow: hidden` doesn't clip
|
|
1059
|
+
most of the handle. Previously the handle stuck 3px outside the
|
|
1060
|
+
cell and the visible 4×4 corner was nearly unclickable - especially
|
|
1061
|
+
on chip cells where pills filled the bottom edge. */
|
|
1062
|
+
bottom: 0;
|
|
1063
|
+
right: 0;
|
|
1064
|
+
width: 9px;
|
|
1065
|
+
height: 9px;
|
|
1066
|
+
background: var(--sg-accent, #0b63f3);
|
|
1067
|
+
border: 1px solid var(--sg-bg, #ffffff);
|
|
1068
|
+
box-sizing: border-box;
|
|
1069
|
+
cursor: crosshair;
|
|
1070
|
+
z-index: 15;
|
|
1071
|
+
touch-action: none;
|
|
1072
|
+
}
|
|
1073
|
+
.sv-grid-fill-handle:hover {
|
|
1074
|
+
transform: scale(1.3);
|
|
1075
|
+
transform-origin: bottom right;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/* Excel-style fill-handle drag marquee: ONE thin dashed rectangle around the
|
|
1079
|
+
whole target range (source + fill-into area), not a thick per-cell border or
|
|
1080
|
+
fill tint. Drawn as four background-image layers on the boundary cells - no
|
|
1081
|
+
`border`, so it paints on the compositor with zero reflow while dragging.
|
|
1082
|
+
`--sg-fill-marquee` overrides the colour (defaults to a theme-aware grey). */
|
|
1083
|
+
/* Step 1 (LOW specificity): default every edge layer to `none`, then light up
|
|
1084
|
+
the edges this cell sits on. These are custom properties, which the
|
|
1085
|
+
`background:` shorthands on selected / zebra cells do NOT reset - so they can
|
|
1086
|
+
safely live at low specificity and just compete with each other by source
|
|
1087
|
+
order (gradient rules come after the defaults, so they win). */
|
|
1088
|
+
.sv-grid-cell[data-fill-top="true"],
|
|
1089
|
+
.sv-grid-cell[data-fill-bottom="true"],
|
|
1090
|
+
.sv-grid-cell[data-fill-left="true"],
|
|
1091
|
+
.sv-grid-cell[data-fill-right="true"] {
|
|
1092
|
+
--sv-fill-top: none;
|
|
1093
|
+
--sv-fill-bottom: none;
|
|
1094
|
+
--sv-fill-left: none;
|
|
1095
|
+
--sv-fill-right: none;
|
|
1096
|
+
/* Match the selection colour (the grid accent) by default. */
|
|
1097
|
+
--sv-fill-color: var(--sg-fill-marquee, var(--sg-accent, #2563eb));
|
|
1098
|
+
}
|
|
1099
|
+
.sv-grid-cell[data-fill-top="true"] {
|
|
1100
|
+
--sv-fill-top: repeating-linear-gradient(to right, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1101
|
+
}
|
|
1102
|
+
.sv-grid-cell[data-fill-bottom="true"] {
|
|
1103
|
+
--sv-fill-bottom: repeating-linear-gradient(to right, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1104
|
+
}
|
|
1105
|
+
.sv-grid-cell[data-fill-left="true"] {
|
|
1106
|
+
--sv-fill-left: repeating-linear-gradient(to bottom, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1107
|
+
}
|
|
1108
|
+
.sv-grid-cell[data-fill-right="true"] {
|
|
1109
|
+
--sv-fill-right: repeating-linear-gradient(to bottom, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1110
|
+
}
|
|
1111
|
+
/* Step 2 (HIGH specificity, `.sv-grid-table` prefix): compose the resolved edge
|
|
1112
|
+
layers into `background-image` so it beats the `background:` shorthands on
|
|
1113
|
+
selected / zebra / hover cells (which would otherwise reset background-image).
|
|
1114
|
+
The cell keeps its background-COLOR; this only adds the dashed lines on top. */
|
|
1115
|
+
.sv-grid-table .sv-grid-cell[data-fill-top="true"],
|
|
1116
|
+
.sv-grid-table .sv-grid-cell[data-fill-bottom="true"],
|
|
1117
|
+
.sv-grid-table .sv-grid-cell[data-fill-left="true"],
|
|
1118
|
+
.sv-grid-table .sv-grid-cell[data-fill-right="true"] {
|
|
1119
|
+
background-image: var(--sv-fill-top), var(--sv-fill-bottom),
|
|
1120
|
+
var(--sv-fill-left), var(--sv-fill-right);
|
|
1121
|
+
background-repeat: no-repeat;
|
|
1122
|
+
background-size: 100% 2px, 100% 2px, 2px 100%, 2px 100%;
|
|
1123
|
+
background-position: top left, bottom left, top left, top right;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
.sv-grid-cell-editing {
|
|
1127
|
+
padding: 0;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/* The four CSS variables let us compose a single box-shadow that draws the
|
|
1131
|
+
selection rectangle outline only on the cells that sit on its edges. */
|
|
1132
|
+
.sv-grid-cell[data-selected-range="true"] {
|
|
1133
|
+
background: var(--sg-selection-bg, #eef4ff);
|
|
1134
|
+
--sv-range-top: 0 0 0 transparent;
|
|
1135
|
+
--sv-range-bottom: 0 0 0 transparent;
|
|
1136
|
+
--sv-range-left: 0 0 0 transparent;
|
|
1137
|
+
--sv-range-right: 0 0 0 transparent;
|
|
1138
|
+
box-shadow: var(--sv-range-top), var(--sv-range-bottom),
|
|
1139
|
+
var(--sv-range-left), var(--sv-range-right);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/* Selection-range edges as inset box-shadows. We tried `border-*: 2px
|
|
1143
|
+
solid` to dodge the 1px-notch artifact at cell boundaries, but
|
|
1144
|
+
border width changes force a full table reflow on every selection
|
|
1145
|
+
change - catastrophic during keyboard nav (every arrow key pressed
|
|
1146
|
+
re-flows ~30 visible rows + their neighbours under border-collapse).
|
|
1147
|
+
Box-shadow inset paints on the compositor only - no reflow. */
|
|
1148
|
+
.sv-grid-cell[data-range-top="true"] {
|
|
1149
|
+
--sv-range-top: inset 0 2px 0 var(--sg-accent, #0b63f3);
|
|
1150
|
+
}
|
|
1151
|
+
.sv-grid-cell[data-range-bottom="true"] {
|
|
1152
|
+
--sv-range-bottom: inset 0 -2px 0 var(--sg-accent, #0b63f3);
|
|
1153
|
+
}
|
|
1154
|
+
.sv-grid-cell[data-range-left="true"] {
|
|
1155
|
+
--sv-range-left: inset 2px 0 0 var(--sg-accent, #0b63f3);
|
|
1156
|
+
}
|
|
1157
|
+
.sv-grid-cell[data-range-right="true"] {
|
|
1158
|
+
--sv-range-right: inset -2px 0 0 var(--sg-accent, #0b63f3);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/* Lift edge cells above their neighbours so the inset shadow renders
|
|
1162
|
+
above adjacent cells' (collapsed) borders rather than under them. */
|
|
1163
|
+
.sv-grid-cell[data-range-top="true"],
|
|
1164
|
+
.sv-grid-cell[data-range-bottom="true"],
|
|
1165
|
+
.sv-grid-cell[data-range-left="true"],
|
|
1166
|
+
.sv-grid-cell[data-range-right="true"],
|
|
1167
|
+
.sv-grid-cell.sv-merge-edge-right,
|
|
1168
|
+
.sv-grid-cell.sv-merge-edge-bottom {
|
|
1169
|
+
position: relative;
|
|
1170
|
+
z-index: 3;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/* Inherited selection-range edges from `spreadsheetLayout` merges.
|
|
1174
|
+
Same inset-shadow technique - no layout impact when toggling. */
|
|
1175
|
+
.sv-grid-cell.sv-merge-edge-right {
|
|
1176
|
+
box-shadow: inset -2px 0 0 var(--sg-accent, #0b63f3);
|
|
1177
|
+
}
|
|
1178
|
+
.sv-grid-cell.sv-merge-edge-bottom {
|
|
1179
|
+
box-shadow: inset 0 -2px 0 var(--sg-accent, #0b63f3);
|
|
1180
|
+
}
|
|
1181
|
+
.sv-grid-cell.sv-merge-in-range {
|
|
1182
|
+
background: var(--sg-selection-bg, #eef4ff);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
.sv-grid-cell-editor {
|
|
1186
|
+
position: absolute;
|
|
1187
|
+
inset: 0;
|
|
1188
|
+
width: 100%;
|
|
1189
|
+
height: 100%;
|
|
1190
|
+
border: 0;
|
|
1191
|
+
outline: none;
|
|
1192
|
+
background: var(--sg-input-bg, #fff);
|
|
1193
|
+
box-sizing: border-box;
|
|
1194
|
+
font: inherit;
|
|
1195
|
+
color: inherit;
|
|
1196
|
+
padding: 0 8px;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/* Base look for the filter inputs (header filter row, per-column filter, the
|
|
1200
|
+
global search box, and the menu's search / condition inputs). The inline cell
|
|
1201
|
+
editor is styled separately - it's borderless and fills its cell. Shipping
|
|
1202
|
+
this with the grid means filters look like real inputs with no host
|
|
1203
|
+
stylesheet. */
|
|
1204
|
+
.sv-grid-column-filter,
|
|
1205
|
+
.sv-grid-filter-value,
|
|
1206
|
+
.sv-grid-global-filter input,
|
|
1207
|
+
.sv-grid-menu-search,
|
|
1208
|
+
.sv-grid-menu-condition-value {
|
|
1209
|
+
background: var(--sg-input-bg, #fff);
|
|
1210
|
+
color: var(--sg-fg, #0f172a);
|
|
1211
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1212
|
+
border-radius: 5px;
|
|
1213
|
+
padding: 0 8px;
|
|
1214
|
+
box-sizing: border-box;
|
|
1215
|
+
font: inherit;
|
|
1216
|
+
}
|
|
1217
|
+
.sv-grid-column-filter {
|
|
1218
|
+
height: 28px;
|
|
1219
|
+
width: 100%;
|
|
1220
|
+
font-size: 0.8125rem;
|
|
1221
|
+
}
|
|
1222
|
+
.sv-grid-column-filter::placeholder,
|
|
1223
|
+
.sv-grid-filter-value::placeholder,
|
|
1224
|
+
.sv-grid-menu-search::placeholder,
|
|
1225
|
+
.sv-grid-global-filter input::placeholder,
|
|
1226
|
+
.sv-grid-cell-editor::placeholder {
|
|
1227
|
+
color: var(--sg-muted, #64748b);
|
|
1228
|
+
opacity: 0.7;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/* Calm, theme-aware focus (a soft box-shadow rather than a hard outline). */
|
|
1232
|
+
.sv-grid-global-filter input:focus,
|
|
1233
|
+
.sv-grid-filter-value:focus,
|
|
1234
|
+
.sv-grid-column-filter:focus,
|
|
1235
|
+
.sv-grid-menu-search:focus,
|
|
1236
|
+
.sv-grid-menu-condition-value:focus,
|
|
1237
|
+
.sv-grid-menu-operator:focus,
|
|
1238
|
+
.sv-grid-cell-editor:focus {
|
|
1239
|
+
outline: none;
|
|
1240
|
+
border-color: var(--sg-accent, #2563eb);
|
|
1241
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 30%, transparent);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
.sv-grid-cell-editor-number,
|
|
1245
|
+
.sv-grid-cell-editor-date,
|
|
1246
|
+
.sv-grid-cell-editor-datetime {
|
|
1247
|
+
width: 100%;
|
|
1248
|
+
height: 100%;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/* List editor: native <select>. For multi-select it grows into a
|
|
1252
|
+
small popup-style listbox that hangs over the cell. */
|
|
1253
|
+
.sv-grid-cell-editor-list {
|
|
1254
|
+
width: 100%;
|
|
1255
|
+
height: 100%;
|
|
1256
|
+
appearance: auto;
|
|
1257
|
+
font: inherit;
|
|
1258
|
+
color: inherit;
|
|
1259
|
+
background: var(--sg-input-bg, #fff);
|
|
1260
|
+
border: 0;
|
|
1261
|
+
padding: 0 6px;
|
|
1262
|
+
box-sizing: border-box;
|
|
1263
|
+
}
|
|
1264
|
+
.sv-grid-cell-editor-list[multiple] {
|
|
1265
|
+
position: absolute;
|
|
1266
|
+
top: 0;
|
|
1267
|
+
left: 0;
|
|
1268
|
+
right: 0;
|
|
1269
|
+
height: auto;
|
|
1270
|
+
min-height: 100%;
|
|
1271
|
+
max-height: 220px;
|
|
1272
|
+
z-index: 5;
|
|
1273
|
+
padding: 4px;
|
|
1274
|
+
border: 1px solid var(--sg-accent, #0b63f3);
|
|
1275
|
+
background: var(--sg-input-bg, #fff);
|
|
1276
|
+
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/* Free-form chips editor (multi-value cell with removable tokens).
|
|
1280
|
+
Absolutely positioned so it can grow downward past the row without
|
|
1281
|
+
pushing other rows. Theme-aware background + accent border + drop
|
|
1282
|
+
shadow so it reads as a popout above the next row, not as part of
|
|
1283
|
+
it. The cell's outer outline is suppressed for this editor - this
|
|
1284
|
+
popout already provides its own border. */
|
|
1285
|
+
.sv-grid-cell-editor-chips {
|
|
1286
|
+
position: absolute;
|
|
1287
|
+
top: -1px;
|
|
1288
|
+
left: -1px;
|
|
1289
|
+
right: -1px;
|
|
1290
|
+
min-height: calc(100% + 2px);
|
|
1291
|
+
background: var(--sg-header-bg, var(--sg-bg, #ffffff));
|
|
1292
|
+
color: var(--sg-fg, #0f172a);
|
|
1293
|
+
padding: 4px 6px;
|
|
1294
|
+
box-sizing: border-box;
|
|
1295
|
+
z-index: 1000;
|
|
1296
|
+
overflow: visible;
|
|
1297
|
+
display: flex;
|
|
1298
|
+
align-items: flex-start;
|
|
1299
|
+
border: 1px solid var(--sg-accent, #2563eb);
|
|
1300
|
+
border-radius: var(--sg-radius, 6px);
|
|
1301
|
+
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45);
|
|
1302
|
+
}
|
|
1303
|
+
/* When the chips editor is mounted, the parent .sv-grid-cell-editing
|
|
1304
|
+
no longer needs its own outline - the popout already shows one. */
|
|
1305
|
+
.sv-grid-cell-editing:has(.sv-grid-cell-editor-chips) {
|
|
1306
|
+
outline: none;
|
|
1307
|
+
}
|
|
1308
|
+
.sv-grid-chips-row {
|
|
1309
|
+
display: flex;
|
|
1310
|
+
flex-wrap: wrap;
|
|
1311
|
+
align-items: center;
|
|
1312
|
+
gap: 4px;
|
|
1313
|
+
width: 100%;
|
|
1314
|
+
}
|
|
1315
|
+
.sv-grid-chip-input {
|
|
1316
|
+
flex: 1 1 80px;
|
|
1317
|
+
min-width: 60px;
|
|
1318
|
+
border: 0;
|
|
1319
|
+
outline: none;
|
|
1320
|
+
background: transparent;
|
|
1321
|
+
font: inherit;
|
|
1322
|
+
color: inherit;
|
|
1323
|
+
padding: 2px 4px;
|
|
1324
|
+
}
|
|
1325
|
+
.sv-grid-chip-picker {
|
|
1326
|
+
flex: 1 1 100px;
|
|
1327
|
+
min-width: 80px;
|
|
1328
|
+
font: inherit;
|
|
1329
|
+
color: inherit;
|
|
1330
|
+
background: var(--sg-bg, #fff);
|
|
1331
|
+
border: 1px solid rgba(15, 23, 42, 0.15);
|
|
1332
|
+
border-radius: 4px;
|
|
1333
|
+
padding: 2px 4px;
|
|
1334
|
+
}
|
|
1335
|
+
.sv-grid-chip-commit {
|
|
1336
|
+
font-size: 11px;
|
|
1337
|
+
text-transform: uppercase;
|
|
1338
|
+
letter-spacing: 0.04em;
|
|
1339
|
+
color: #fff;
|
|
1340
|
+
background: var(--sg-accent, #0b63f3);
|
|
1341
|
+
border: 0;
|
|
1342
|
+
border-radius: 4px;
|
|
1343
|
+
padding: 3px 8px;
|
|
1344
|
+
cursor: pointer;
|
|
1345
|
+
}
|
|
1346
|
+
.sv-grid-chip-commit:hover {
|
|
1347
|
+
filter: brightness(1.1);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/* Chip badge - used both in readonly cell display and inside the
|
|
1351
|
+
chips editor. Subtle neutral chrome, scales to the row's font size.
|
|
1352
|
+
Readonly chip rows stay on one line (the parent <td> has
|
|
1353
|
+
overflow: hidden, so overflowing chips get clipped - widen the
|
|
1354
|
+
column or open the editor to see all). */
|
|
1355
|
+
.sv-grid-chips-display {
|
|
1356
|
+
display: inline-flex;
|
|
1357
|
+
flex-wrap: nowrap;
|
|
1358
|
+
align-items: center;
|
|
1359
|
+
gap: 4px;
|
|
1360
|
+
max-width: 100%;
|
|
1361
|
+
vertical-align: middle;
|
|
1362
|
+
}
|
|
1363
|
+
.sv-grid-sparkline {
|
|
1364
|
+
display: inline-block;
|
|
1365
|
+
vertical-align: middle;
|
|
1366
|
+
overflow: visible;
|
|
1367
|
+
}
|
|
1368
|
+
/* Conditional formatting overlays - behind the cell text. */
|
|
1369
|
+
.sv-grid-cf-bg {
|
|
1370
|
+
position: absolute;
|
|
1371
|
+
inset: 0;
|
|
1372
|
+
z-index: 0;
|
|
1373
|
+
pointer-events: none;
|
|
1374
|
+
}
|
|
1375
|
+
.sv-grid-cf-bar {
|
|
1376
|
+
position: absolute;
|
|
1377
|
+
left: 0;
|
|
1378
|
+
top: 0;
|
|
1379
|
+
bottom: 0;
|
|
1380
|
+
z-index: 0;
|
|
1381
|
+
opacity: 0.85;
|
|
1382
|
+
border-radius: 0 2px 2px 0;
|
|
1383
|
+
pointer-events: none;
|
|
1384
|
+
}
|
|
1385
|
+
.sv-grid-cell-cf {
|
|
1386
|
+
position: relative;
|
|
1387
|
+
}
|
|
1388
|
+
/* Declarative cell validation (column `validate` hook). Invalid cells get a
|
|
1389
|
+
* soft red wash and a red inset ring - Handsontable's `htInvalid` look. The
|
|
1390
|
+
* value keeps rendering as-is; the message (if any) shows as the tooltip. */
|
|
1391
|
+
.sv-grid-cell-invalid {
|
|
1392
|
+
background-color: var(--sg-invalid-bg, rgba(239, 68, 68, 0.14)) !important;
|
|
1393
|
+
box-shadow: inset 0 0 0 1px var(--sg-invalid-border, rgba(239, 68, 68, 0.45));
|
|
1394
|
+
color: var(--sg-invalid-fg, #b91c1c);
|
|
1395
|
+
}
|
|
1396
|
+
.sv-grid-cell-invalid.sv-grid-cell-active {
|
|
1397
|
+
box-shadow: inset 0 0 0 2px var(--sg-invalid-border, rgba(239, 68, 68, 0.7));
|
|
1398
|
+
}
|
|
1399
|
+
.sv-grid-status-bar {
|
|
1400
|
+
display: flex;
|
|
1401
|
+
flex-wrap: wrap;
|
|
1402
|
+
align-items: center;
|
|
1403
|
+
gap: 18px;
|
|
1404
|
+
padding: 6px 14px;
|
|
1405
|
+
font-size: 12px;
|
|
1406
|
+
color: var(--sg-fg);
|
|
1407
|
+
background: var(--sg-header-bg, #f1f5f9);
|
|
1408
|
+
border: 1px solid var(--sg-border, #e2e8f0);
|
|
1409
|
+
border-top: 0;
|
|
1410
|
+
flex-shrink: 0;
|
|
1411
|
+
}
|
|
1412
|
+
.sv-grid-status-item {
|
|
1413
|
+
font-variant-numeric: tabular-nums;
|
|
1414
|
+
font-weight: 600;
|
|
1415
|
+
}
|
|
1416
|
+
.sv-grid-status-label {
|
|
1417
|
+
color: var(--sg-muted, #64748b);
|
|
1418
|
+
font-weight: 400;
|
|
1419
|
+
margin-right: 5px;
|
|
1420
|
+
}
|
|
1421
|
+
.sv-grid-cf-content {
|
|
1422
|
+
position: relative;
|
|
1423
|
+
z-index: 1;
|
|
1424
|
+
}
|
|
1425
|
+
.sv-grid-cf-icon {
|
|
1426
|
+
margin-right: 5px;
|
|
1427
|
+
font-size: 0.95em;
|
|
1428
|
+
}
|
|
1429
|
+
.sv-grid-chip {
|
|
1430
|
+
display: inline-flex;
|
|
1431
|
+
align-items: center;
|
|
1432
|
+
gap: 4px;
|
|
1433
|
+
padding: 2px 8px;
|
|
1434
|
+
font-size: 0.85em;
|
|
1435
|
+
line-height: 1.4;
|
|
1436
|
+
background: color-mix(in srgb, var(--sg-accent, #2563eb) 18%, transparent);
|
|
1437
|
+
color: var(--sg-fg, inherit);
|
|
1438
|
+
border-radius: 999px;
|
|
1439
|
+
border: 1px solid
|
|
1440
|
+
color-mix(in srgb, var(--sg-accent, #2563eb) 35%, transparent);
|
|
1441
|
+
white-space: nowrap;
|
|
1442
|
+
}
|
|
1443
|
+
.sv-grid-chip-removable {
|
|
1444
|
+
padding-right: 2px;
|
|
1445
|
+
}
|
|
1446
|
+
.sv-grid-chip-remove {
|
|
1447
|
+
display: inline-flex;
|
|
1448
|
+
align-items: center;
|
|
1449
|
+
justify-content: center;
|
|
1450
|
+
width: 16px;
|
|
1451
|
+
height: 16px;
|
|
1452
|
+
border: 0;
|
|
1453
|
+
border-radius: 999px;
|
|
1454
|
+
background: color-mix(in srgb, var(--sg-fg, #0f172a) 18%, transparent);
|
|
1455
|
+
color: inherit;
|
|
1456
|
+
font-size: 12px;
|
|
1457
|
+
line-height: 1;
|
|
1458
|
+
cursor: pointer;
|
|
1459
|
+
padding: 0;
|
|
1460
|
+
}
|
|
1461
|
+
.sv-grid-chip-remove:hover {
|
|
1462
|
+
background: rgba(220, 38, 38, 0.7);
|
|
1463
|
+
color: #fff;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
.sv-grid-icon {
|
|
1467
|
+
width: 1em;
|
|
1468
|
+
height: 1em;
|
|
1469
|
+
display: block;
|
|
1470
|
+
flex: none;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
.sv-grid-header-cell {
|
|
1474
|
+
display: flex;
|
|
1475
|
+
align-items: center;
|
|
1476
|
+
gap: 2px;
|
|
1477
|
+
width: 100%;
|
|
1478
|
+
min-width: 0;
|
|
1479
|
+
/* Themeable header height. Defaults to auto (content-driven); presets
|
|
1480
|
+
can set a taller header (e.g. Material's roomy data-table header).
|
|
1481
|
+
The component measures the rendered header height for sticky offsets,
|
|
1482
|
+
so this propagates to --sg-thead-h automatically. */
|
|
1483
|
+
min-height: var(--sg-header-min-height, auto);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
.sv-grid-header-sort {
|
|
1487
|
+
display: flex;
|
|
1488
|
+
align-items: center;
|
|
1489
|
+
gap: 4px;
|
|
1490
|
+
flex: 1 1 auto;
|
|
1491
|
+
min-width: 0;
|
|
1492
|
+
padding: 2px;
|
|
1493
|
+
border: 0;
|
|
1494
|
+
background: transparent;
|
|
1495
|
+
font: inherit;
|
|
1496
|
+
color: inherit;
|
|
1497
|
+
text-align: left;
|
|
1498
|
+
cursor: pointer;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
/* Custom (function-valued) header rendering. Behaves like the sort
|
|
1502
|
+
button but is a div so the consumer's snippet can contain its own
|
|
1503
|
+
interactive elements (menu buttons, dropdowns, etc) - button-in-
|
|
1504
|
+
button DOM is invalid. */
|
|
1505
|
+
.sv-grid-header-custom {
|
|
1506
|
+
display: flex;
|
|
1507
|
+
align-items: center;
|
|
1508
|
+
gap: 4px;
|
|
1509
|
+
flex: 1 1 auto;
|
|
1510
|
+
min-width: 0;
|
|
1511
|
+
padding: 2px;
|
|
1512
|
+
text-align: left;
|
|
1513
|
+
cursor: pointer;
|
|
1514
|
+
outline: none;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
.sv-grid-header-label {
|
|
1518
|
+
/* min-width:0 lets this flex item shrink below its text width so a long
|
|
1519
|
+
label truncates to a single line with an ellipsis instead of overflowing
|
|
1520
|
+
or forcing the header taller. flex:0 1 auto keeps the sort icon snug to
|
|
1521
|
+
the label rather than pushed to the far edge. */
|
|
1522
|
+
flex: 0 1 auto;
|
|
1523
|
+
min-width: 0;
|
|
1524
|
+
overflow: hidden;
|
|
1525
|
+
text-overflow: ellipsis;
|
|
1526
|
+
white-space: nowrap;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
.sv-grid-header-icon {
|
|
1530
|
+
display: inline-flex;
|
|
1531
|
+
flex: none;
|
|
1532
|
+
font-size: 13px;
|
|
1533
|
+
color: var(--sg-muted, #5b6b85);
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
.sv-grid-header-icon-hint {
|
|
1537
|
+
opacity: 0;
|
|
1538
|
+
transition: opacity 120ms ease;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
.sv-grid-column:hover .sv-grid-header-icon-hint {
|
|
1542
|
+
opacity: 0.4;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
.sv-grid-header-icon-flag {
|
|
1546
|
+
color: var(--sg-accent, #0b63f3);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
.sv-grid-col-menu-btn {
|
|
1550
|
+
display: inline-flex;
|
|
1551
|
+
align-items: center;
|
|
1552
|
+
justify-content: center;
|
|
1553
|
+
flex: none;
|
|
1554
|
+
/* Collapse to zero width when invisible so a non-hovered column
|
|
1555
|
+
has no dead space at the right; the funnel (when an active
|
|
1556
|
+
filter is on) can then sit flush against the right edge.
|
|
1557
|
+
Hover / focus / open expand it back to 22px. */
|
|
1558
|
+
width: 0;
|
|
1559
|
+
height: 22px;
|
|
1560
|
+
padding: 0;
|
|
1561
|
+
border: 0;
|
|
1562
|
+
border-radius: 4px;
|
|
1563
|
+
background: transparent;
|
|
1564
|
+
color: var(--sg-muted, #5b6b85);
|
|
1565
|
+
font-size: 15px;
|
|
1566
|
+
cursor: pointer;
|
|
1567
|
+
opacity: 0;
|
|
1568
|
+
pointer-events: none;
|
|
1569
|
+
overflow: hidden;
|
|
1570
|
+
transition:
|
|
1571
|
+
width 140ms ease,
|
|
1572
|
+
opacity 140ms ease,
|
|
1573
|
+
background-color 120ms ease;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
.sv-grid-column:hover .sv-grid-col-menu-btn,
|
|
1577
|
+
.sv-grid-col-menu-btn.is-open,
|
|
1578
|
+
.sv-grid-col-menu-btn:focus-visible {
|
|
1579
|
+
width: 22px;
|
|
1580
|
+
opacity: 1;
|
|
1581
|
+
pointer-events: auto;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
/* The funnel filter button is a sibling .sv-grid-col-menu-btn that
|
|
1585
|
+
ALSO carries .sv-grid-col-filter-btn. When the column has an
|
|
1586
|
+
active filter (.is-active) the funnel stays visible at full size
|
|
1587
|
+
even when the column isn't hovered - so users can see at a glance
|
|
1588
|
+
which columns are filtered. Because the 3-dot menu still collapses
|
|
1589
|
+
to width: 0 in that state, the funnel ends up flush against the
|
|
1590
|
+
right edge. On hover, the menu expands back to 22px and pushes the
|
|
1591
|
+
funnel left naturally. */
|
|
1592
|
+
.sv-grid-col-filter-btn.is-active {
|
|
1593
|
+
width: 22px;
|
|
1594
|
+
opacity: 1;
|
|
1595
|
+
pointer-events: auto;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
.sv-grid-col-menu-btn:hover,
|
|
1599
|
+
.sv-grid-col-menu-btn.is-open {
|
|
1600
|
+
background: var(--sg-row-hover-bg, #e2e8f3);
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
.sv-grid-filter-operator-btn {
|
|
1604
|
+
display: inline-flex;
|
|
1605
|
+
align-items: center;
|
|
1606
|
+
gap: 2px;
|
|
1607
|
+
flex: none;
|
|
1608
|
+
height: 24px;
|
|
1609
|
+
padding: 0 4px;
|
|
1610
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1611
|
+
border-radius: 4px;
|
|
1612
|
+
background: var(--sg-input-bg, #fff);
|
|
1613
|
+
color: var(--sg-fg, #334155);
|
|
1614
|
+
font-size: 13px;
|
|
1615
|
+
cursor: pointer;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
.sv-grid-filter-operator-btn.is-open {
|
|
1619
|
+
border-color: var(--sg-accent, #0b63f3);
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
.sv-grid-caret {
|
|
1623
|
+
display: inline-flex;
|
|
1624
|
+
font-size: 9px;
|
|
1625
|
+
color: var(--sg-muted, #94a3b8);
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
.sv-grid-menu-backdrop {
|
|
1629
|
+
position: fixed;
|
|
1630
|
+
inset: 0;
|
|
1631
|
+
z-index: 900;
|
|
1632
|
+
background: transparent;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
.sv-grid-menu {
|
|
1636
|
+
position: fixed;
|
|
1637
|
+
z-index: 901;
|
|
1638
|
+
max-height: calc(100vh - 24px);
|
|
1639
|
+
overflow: auto;
|
|
1640
|
+
padding: 4px;
|
|
1641
|
+
background: var(--sg-bg, #fff);
|
|
1642
|
+
border: 1px solid var(--sg-border, #d6dee9);
|
|
1643
|
+
border-radius: 8px;
|
|
1644
|
+
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);
|
|
1645
|
+
font-size: 13px;
|
|
1646
|
+
color: var(--sg-fg, #1f2937);
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
.sv-grid-column-menu {
|
|
1650
|
+
width: 260px;
|
|
1651
|
+
}
|
|
1652
|
+
/* Column-menu tab bar (General / Filter / Columns). */
|
|
1653
|
+
.sv-grid-menu-tabs {
|
|
1654
|
+
display: flex;
|
|
1655
|
+
gap: 2px;
|
|
1656
|
+
margin: -2px -2px 4px;
|
|
1657
|
+
padding: 2px 2px 0;
|
|
1658
|
+
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
1659
|
+
}
|
|
1660
|
+
.sv-grid-menu-tab {
|
|
1661
|
+
flex: 1;
|
|
1662
|
+
padding: 6px 6px;
|
|
1663
|
+
border: 0;
|
|
1664
|
+
border-bottom: 2px solid transparent;
|
|
1665
|
+
background: transparent;
|
|
1666
|
+
color: var(--sg-muted, #64748b);
|
|
1667
|
+
font-size: 12px;
|
|
1668
|
+
font-weight: 600;
|
|
1669
|
+
cursor: pointer;
|
|
1670
|
+
}
|
|
1671
|
+
.sv-grid-menu-tab:hover {
|
|
1672
|
+
color: var(--sg-fg, #0f172a);
|
|
1673
|
+
}
|
|
1674
|
+
.sv-grid-menu-tab.is-active {
|
|
1675
|
+
color: var(--sg-accent, #2563eb);
|
|
1676
|
+
border-bottom-color: var(--sg-accent, #2563eb);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
.sv-grid-operator-menu {
|
|
1680
|
+
width: 184px;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
.sv-grid-context-menu {
|
|
1684
|
+
min-width: 180px;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
.sv-grid-comment-editor {
|
|
1688
|
+
width: 240px;
|
|
1689
|
+
padding: 8px;
|
|
1690
|
+
}
|
|
1691
|
+
.sv-grid-comment-textarea {
|
|
1692
|
+
width: 100%;
|
|
1693
|
+
resize: vertical;
|
|
1694
|
+
min-height: 60px;
|
|
1695
|
+
padding: 6px 8px;
|
|
1696
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1697
|
+
border-radius: 4px;
|
|
1698
|
+
background: var(--sg-input-bg, #fff);
|
|
1699
|
+
color: var(--sg-fg, #0f172a);
|
|
1700
|
+
font: inherit;
|
|
1701
|
+
font-size: 13px;
|
|
1702
|
+
outline: none;
|
|
1703
|
+
}
|
|
1704
|
+
.sv-grid-comment-textarea:focus {
|
|
1705
|
+
border-color: var(--sg-accent, #6366f1);
|
|
1706
|
+
}
|
|
1707
|
+
.sv-grid-comment-actions {
|
|
1708
|
+
display: flex;
|
|
1709
|
+
align-items: center;
|
|
1710
|
+
gap: 6px;
|
|
1711
|
+
margin-top: 8px;
|
|
1712
|
+
}
|
|
1713
|
+
.sv-grid-comment-spacer {
|
|
1714
|
+
flex: 1 1 auto;
|
|
1715
|
+
}
|
|
1716
|
+
.sv-grid-comment-actions button {
|
|
1717
|
+
padding: 4px 10px;
|
|
1718
|
+
border-radius: 4px;
|
|
1719
|
+
border: 1px solid var(--sg-border, #cbd5e1);
|
|
1720
|
+
background: var(--sg-bg, #fff);
|
|
1721
|
+
color: var(--sg-fg, #1f2937);
|
|
1722
|
+
font: inherit;
|
|
1723
|
+
font-size: 12px;
|
|
1724
|
+
cursor: pointer;
|
|
1725
|
+
}
|
|
1726
|
+
.sv-grid-comment-save {
|
|
1727
|
+
border-color: var(--sg-accent, #6366f1) !important;
|
|
1728
|
+
background: var(--sg-accent, #6366f1) !important;
|
|
1729
|
+
color: #fff !important;
|
|
1730
|
+
}
|
|
1731
|
+
.sv-grid-comment-remove {
|
|
1732
|
+
color: var(--sg-danger, #dc2626) !important;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
.sv-grid-menu-item {
|
|
1736
|
+
display: flex;
|
|
1737
|
+
align-items: center;
|
|
1738
|
+
gap: 8px;
|
|
1739
|
+
width: 100%;
|
|
1740
|
+
padding: 7px 10px;
|
|
1741
|
+
border: 0;
|
|
1742
|
+
border-radius: 5px;
|
|
1743
|
+
background: transparent;
|
|
1744
|
+
color: inherit;
|
|
1745
|
+
font: inherit;
|
|
1746
|
+
text-align: left;
|
|
1747
|
+
cursor: pointer;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
.sv-grid-menu-item:hover {
|
|
1751
|
+
background: var(--sg-row-hover-bg, #eef2f8);
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
.sv-grid-menu-item[aria-checked="true"] {
|
|
1755
|
+
background: var(--sg-selection-bg, #eaf2ff);
|
|
1756
|
+
color: var(--sg-accent, #0b63f3);
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
.sv-grid-menu-sep {
|
|
1760
|
+
height: 1px;
|
|
1761
|
+
margin: 4px 6px;
|
|
1762
|
+
background: var(--sg-border, #e6ebf2);
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
.sv-grid-menu-filter {
|
|
1766
|
+
padding: 4px 6px 6px;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
.sv-grid-menu-filter-head {
|
|
1770
|
+
display: flex;
|
|
1771
|
+
align-items: center;
|
|
1772
|
+
gap: 6px;
|
|
1773
|
+
padding: 2px 0 6px;
|
|
1774
|
+
font-weight: 600;
|
|
1775
|
+
color: var(--sg-muted, #475569);
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
.sv-grid-menu-search {
|
|
1779
|
+
width: 100%;
|
|
1780
|
+
height: 28px;
|
|
1781
|
+
box-sizing: border-box;
|
|
1782
|
+
margin-bottom: 6px;
|
|
1783
|
+
padding: 0 8px;
|
|
1784
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1785
|
+
border-radius: 5px;
|
|
1786
|
+
font: inherit;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
.sv-grid-menu-filter-row {
|
|
1790
|
+
display: flex;
|
|
1791
|
+
gap: 4px;
|
|
1792
|
+
margin-bottom: 6px;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
.sv-grid-menu-operator-group {
|
|
1796
|
+
display: flex;
|
|
1797
|
+
gap: 2px;
|
|
1798
|
+
margin-bottom: 6px;
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
.sv-grid-menu-operator-btn {
|
|
1802
|
+
flex: 1 1 0;
|
|
1803
|
+
display: inline-flex;
|
|
1804
|
+
align-items: center;
|
|
1805
|
+
justify-content: center;
|
|
1806
|
+
height: 28px;
|
|
1807
|
+
padding: 0;
|
|
1808
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1809
|
+
border-radius: 4px;
|
|
1810
|
+
background: var(--sg-input-bg, #fff);
|
|
1811
|
+
color: var(--sg-fg, #475569);
|
|
1812
|
+
cursor: pointer;
|
|
1813
|
+
transition:
|
|
1814
|
+
background-color 100ms ease,
|
|
1815
|
+
color 100ms ease,
|
|
1816
|
+
border-color 100ms ease;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
.sv-grid-menu-operator-btn:hover {
|
|
1820
|
+
background: var(--sg-row-hover-bg, #eef2f8);
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
.sv-grid-menu-operator-btn.is-active {
|
|
1824
|
+
background: var(--sg-accent, #0b63f3);
|
|
1825
|
+
border-color: var(--sg-accent, #0b63f3);
|
|
1826
|
+
color: #fff;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
.sv-grid-menu-condition-value {
|
|
1830
|
+
flex: 1 1 0;
|
|
1831
|
+
min-width: 0;
|
|
1832
|
+
height: 28px;
|
|
1833
|
+
box-sizing: border-box;
|
|
1834
|
+
padding: 0 8px;
|
|
1835
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1836
|
+
border-radius: 5px;
|
|
1837
|
+
font: inherit;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
/* Multi-condition filter: AND/OR join toggle + add-condition button. */
|
|
1841
|
+
.sv-grid-menu-add-cond {
|
|
1842
|
+
align-self: flex-start;
|
|
1843
|
+
margin-top: 2px;
|
|
1844
|
+
padding: 3px 8px;
|
|
1845
|
+
font-size: 11.5px;
|
|
1846
|
+
font-weight: 600;
|
|
1847
|
+
color: var(--sg-accent, #3b82f6);
|
|
1848
|
+
background: transparent;
|
|
1849
|
+
border: 1px dashed var(--sg-border, #cbd5e1);
|
|
1850
|
+
border-radius: 5px;
|
|
1851
|
+
cursor: pointer;
|
|
1852
|
+
}
|
|
1853
|
+
.sv-grid-menu-add-cond:hover {
|
|
1854
|
+
background: color-mix(in oklab, var(--sg-accent, #3b82f6) 8%, transparent);
|
|
1855
|
+
border-style: solid;
|
|
1856
|
+
}
|
|
1857
|
+
.sv-grid-menu-join {
|
|
1858
|
+
display: inline-flex;
|
|
1859
|
+
align-items: center;
|
|
1860
|
+
gap: 2px;
|
|
1861
|
+
margin: 2px 0;
|
|
1862
|
+
}
|
|
1863
|
+
.sv-grid-menu-join button {
|
|
1864
|
+
padding: 2px 10px;
|
|
1865
|
+
font-size: 11px;
|
|
1866
|
+
font-weight: 700;
|
|
1867
|
+
color: var(--sg-muted, #64748b);
|
|
1868
|
+
background: transparent;
|
|
1869
|
+
border: 1px solid var(--sg-border, #cbd5e1);
|
|
1870
|
+
cursor: pointer;
|
|
1871
|
+
}
|
|
1872
|
+
.sv-grid-menu-join button:first-child { border-radius: 5px 0 0 5px; }
|
|
1873
|
+
.sv-grid-menu-join button:nth-child(2) { border-radius: 0; border-left: 0; }
|
|
1874
|
+
.sv-grid-menu-join button.is-on {
|
|
1875
|
+
background: var(--sg-accent, #3b82f6);
|
|
1876
|
+
color: #fff;
|
|
1877
|
+
border-color: var(--sg-accent, #3b82f6);
|
|
1878
|
+
}
|
|
1879
|
+
.sv-grid-menu-join-x {
|
|
1880
|
+
margin-left: auto;
|
|
1881
|
+
border-radius: 5px !important;
|
|
1882
|
+
font-size: 14px;
|
|
1883
|
+
line-height: 1;
|
|
1884
|
+
padding: 2px 7px !important;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
.sv-grid-facet-list {
|
|
1888
|
+
max-height: 200px;
|
|
1889
|
+
overflow: auto;
|
|
1890
|
+
border: 1px solid var(--sg-border, #eaeef4);
|
|
1891
|
+
border-radius: 5px;
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
.sv-grid-facet {
|
|
1895
|
+
display: flex;
|
|
1896
|
+
align-items: center;
|
|
1897
|
+
gap: 8px;
|
|
1898
|
+
padding: 4px 8px;
|
|
1899
|
+
cursor: pointer;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
.sv-grid-facet:hover {
|
|
1903
|
+
background: var(--sg-row-hover-bg, #f4f6fa);
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
.sv-grid-facet-all {
|
|
1907
|
+
border-bottom: 1px solid var(--sg-border, #eaeef4);
|
|
1908
|
+
font-weight: 600;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
.sv-grid-facet input {
|
|
1912
|
+
flex: none;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
.sv-grid-facet-label {
|
|
1916
|
+
overflow: hidden;
|
|
1917
|
+
text-overflow: ellipsis;
|
|
1918
|
+
white-space: nowrap;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
.sv-grid-facet-empty,
|
|
1922
|
+
.sv-grid-facet-note {
|
|
1923
|
+
padding: 6px 8px;
|
|
1924
|
+
color: var(--sg-muted, #94a3b8);
|
|
1925
|
+
font-size: 12px;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
.sv-grid-menu-actions {
|
|
1929
|
+
display: flex;
|
|
1930
|
+
justify-content: flex-end;
|
|
1931
|
+
gap: 6px;
|
|
1932
|
+
padding-top: 8px;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
.sv-grid-menu-btn {
|
|
1936
|
+
padding: 5px 10px;
|
|
1937
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1938
|
+
border-radius: 5px;
|
|
1939
|
+
background: var(--sg-input-bg, #fff);
|
|
1940
|
+
color: var(--sg-fg, #334155);
|
|
1941
|
+
font: inherit;
|
|
1942
|
+
cursor: pointer;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
.sv-grid-menu-btn-primary {
|
|
1946
|
+
background: var(--sg-accent, #0b63f3);
|
|
1947
|
+
border-color: var(--sg-accent, #0b63f3);
|
|
1948
|
+
color: #fff;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
.sv-grid-menu-item:disabled,
|
|
1952
|
+
.sv-grid-menu-btn:disabled {
|
|
1953
|
+
opacity: 0.4;
|
|
1954
|
+
cursor: default;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
.sv-grid-menu-item:disabled:hover {
|
|
1958
|
+
background: transparent;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
.sv-grid-group-row > .sv-grid-cell {
|
|
1962
|
+
background: var(--sg-header-bg, #eef2f8);
|
|
1963
|
+
font-weight: 600;
|
|
1964
|
+
cursor: pointer;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
.sv-grid-row-selected > .sv-grid-cell {
|
|
1968
|
+
background: var(--sg-selection-bg, #eaf2ff);
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
.sv-grid-group-cell {
|
|
1972
|
+
padding: 0 12px;
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
.sv-grid-group-content {
|
|
1976
|
+
display: inline-flex;
|
|
1977
|
+
align-items: center;
|
|
1978
|
+
gap: 6px;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
.sv-grid-group-toggle {
|
|
1982
|
+
border: 0;
|
|
1983
|
+
background: transparent;
|
|
1984
|
+
padding: 0 4px 0 0;
|
|
1985
|
+
font-size: 11px;
|
|
1986
|
+
line-height: 1;
|
|
1987
|
+
color: var(--sg-fg, #334155);
|
|
1988
|
+
cursor: pointer;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
.sv-grid-group-count {
|
|
1992
|
+
margin-left: 6px;
|
|
1993
|
+
font-weight: 400;
|
|
1994
|
+
font-size: 12px;
|
|
1995
|
+
color: var(--sg-muted, #64748b);
|
|
1996
|
+
}
|
|
1997
|
+
.sv-grid-group-agg {
|
|
1998
|
+
margin-left: 10px;
|
|
1999
|
+
font-size: 12px;
|
|
2000
|
+
font-weight: 600;
|
|
2001
|
+
color: var(--sg-fg);
|
|
2002
|
+
white-space: nowrap;
|
|
2003
|
+
}
|
|
2004
|
+
.sv-grid-group-agg-label {
|
|
2005
|
+
font-weight: 400;
|
|
2006
|
+
color: var(--sg-muted, #64748b);
|
|
2007
|
+
margin-right: 4px;
|
|
2008
|
+
}
|
|
2009
|
+
.sv-grid-group-agg-label::after {
|
|
2010
|
+
content: ":";
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
.sv-grid-group-child-indent {
|
|
2014
|
+
display: inline-block;
|
|
2015
|
+
width: 18px;
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
/* ---- Custom hover tooltip (column .tooltip + cell notes) ---- */
|
|
2019
|
+
.sv-grid-tooltip {
|
|
2020
|
+
position: fixed;
|
|
2021
|
+
z-index: 10000;
|
|
2022
|
+
max-width: 280px;
|
|
2023
|
+
padding: 8px 10px;
|
|
2024
|
+
background: #0f172a;
|
|
2025
|
+
color: #f1f5f9;
|
|
2026
|
+
border-radius: var(--sg-radius, 6px);
|
|
2027
|
+
font-size: 12px;
|
|
2028
|
+
line-height: 1.45;
|
|
2029
|
+
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.28);
|
|
2030
|
+
pointer-events: none;
|
|
2031
|
+
white-space: pre-wrap;
|
|
2032
|
+
word-wrap: break-word;
|
|
2033
|
+
}
|
|
2034
|
+
[data-theme="dark"] .sv-grid-tooltip { background: #f1f5f9; color: #0f172a; }
|
|
2035
|
+
|
|
2036
|
+
/* ---- Textarea editor ---- */
|
|
2037
|
+
.sv-grid-cell-editor-textarea {
|
|
2038
|
+
width: 100%;
|
|
2039
|
+
min-height: 80px;
|
|
2040
|
+
padding: 6px 8px;
|
|
2041
|
+
border: 1px solid var(--sg-accent, #6366f1);
|
|
2042
|
+
background: var(--sg-bg, #fff);
|
|
2043
|
+
color: var(--sg-fg, #0f172a);
|
|
2044
|
+
border-radius: 4px;
|
|
2045
|
+
font-family: inherit;
|
|
2046
|
+
font-size: 13px;
|
|
2047
|
+
line-height: 1.4;
|
|
2048
|
+
resize: vertical;
|
|
2049
|
+
outline: none;
|
|
2050
|
+
box-shadow: var(--sg-focus-ring, 0 0 0 2px rgba(99, 102, 241, 0.40));
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
.sv-grid-autocomplete {
|
|
2054
|
+
position: relative;
|
|
2055
|
+
width: 100%;
|
|
2056
|
+
}
|
|
2057
|
+
.sv-grid-autocomplete-list {
|
|
2058
|
+
position: absolute;
|
|
2059
|
+
left: 0;
|
|
2060
|
+
right: 0;
|
|
2061
|
+
top: 100%;
|
|
2062
|
+
z-index: 30;
|
|
2063
|
+
max-height: 220px;
|
|
2064
|
+
overflow: auto;
|
|
2065
|
+
margin-top: 2px;
|
|
2066
|
+
padding: 4px;
|
|
2067
|
+
background: var(--sg-bg, #fff);
|
|
2068
|
+
border: 1px solid var(--sg-border, #d6dee9);
|
|
2069
|
+
border-radius: var(--sg-radius, 6px);
|
|
2070
|
+
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);
|
|
2071
|
+
}
|
|
2072
|
+
.sv-grid-autocomplete-option {
|
|
2073
|
+
display: block;
|
|
2074
|
+
width: 100%;
|
|
2075
|
+
text-align: left;
|
|
2076
|
+
padding: 5px 8px;
|
|
2077
|
+
border: 0;
|
|
2078
|
+
border-radius: 4px;
|
|
2079
|
+
background: transparent;
|
|
2080
|
+
color: var(--sg-fg, #1f2937);
|
|
2081
|
+
font: inherit;
|
|
2082
|
+
font-size: 13px;
|
|
2083
|
+
cursor: pointer;
|
|
2084
|
+
white-space: nowrap;
|
|
2085
|
+
overflow: hidden;
|
|
2086
|
+
text-overflow: ellipsis;
|
|
2087
|
+
}
|
|
2088
|
+
.sv-grid-autocomplete-option:hover,
|
|
2089
|
+
.sv-grid-autocomplete-option[aria-selected="true"] {
|
|
2090
|
+
background: var(--sg-row-hover-bg, rgba(99, 102, 241, 0.10));
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
/* ---- Find-in-grid overlay ---- */
|
|
2094
|
+
.sv-grid-find {
|
|
2095
|
+
position: absolute;
|
|
2096
|
+
top: 8px; right: 8px;
|
|
2097
|
+
z-index: 60;
|
|
2098
|
+
display: inline-flex; align-items: center; gap: 4px;
|
|
2099
|
+
padding: 6px 8px;
|
|
2100
|
+
background: var(--sg-bg, #fff);
|
|
2101
|
+
color: var(--sg-fg, #0f172a);
|
|
2102
|
+
border: 1px solid var(--sg-border, #cbd5e1);
|
|
2103
|
+
border-radius: 8px;
|
|
2104
|
+
box-shadow: 0 10px 25px rgba(15, 23, 42, 0.18);
|
|
2105
|
+
min-width: 280px;
|
|
2106
|
+
}
|
|
2107
|
+
.sv-grid-find-icon {
|
|
2108
|
+
width: 14px; height: 14px;
|
|
2109
|
+
color: var(--sg-muted, #94a3b8);
|
|
2110
|
+
flex: none;
|
|
2111
|
+
margin-left: 2px;
|
|
2112
|
+
}
|
|
2113
|
+
.sv-grid-find-input {
|
|
2114
|
+
flex: 1; min-width: 0;
|
|
2115
|
+
border: 0; outline: none;
|
|
2116
|
+
background: transparent;
|
|
2117
|
+
color: inherit;
|
|
2118
|
+
font-size: 13px;
|
|
2119
|
+
padding: 2px 6px;
|
|
2120
|
+
}
|
|
2121
|
+
.sv-grid-find-input::placeholder { color: var(--sg-muted, #94a3b8); }
|
|
2122
|
+
.sv-grid-find-count {
|
|
2123
|
+
font-size: 11px;
|
|
2124
|
+
color: var(--sg-muted, #64748b);
|
|
2125
|
+
font-variant-numeric: tabular-nums;
|
|
2126
|
+
padding: 0 4px;
|
|
2127
|
+
white-space: nowrap;
|
|
2128
|
+
}
|
|
2129
|
+
.sv-grid-find-step, .sv-grid-find-close {
|
|
2130
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
2131
|
+
width: 22px; height: 22px;
|
|
2132
|
+
background: transparent;
|
|
2133
|
+
border: 0;
|
|
2134
|
+
color: var(--sg-muted, #64748b);
|
|
2135
|
+
border-radius: 4px;
|
|
2136
|
+
cursor: pointer;
|
|
2137
|
+
font-size: 12px;
|
|
2138
|
+
}
|
|
2139
|
+
.sv-grid-find-step:hover:not(:disabled),
|
|
2140
|
+
.sv-grid-find-close:hover {
|
|
2141
|
+
background: var(--sg-row-hover-bg, rgba(148, 163, 184, 0.12));
|
|
2142
|
+
color: var(--sg-fg, #0f172a);
|
|
2143
|
+
}
|
|
2144
|
+
.sv-grid-find-step:disabled { opacity: 0.30; cursor: default; }
|
|
2145
|
+
|
|
2146
|
+
/* ---- Pagination footer (GridFooter.svelte) ------------------------------
|
|
2147
|
+
Ships with the grid so the pager is styled out of the box - previously this
|
|
2148
|
+
lived only in the examples' host stylesheet, leaving bare consumers with an
|
|
2149
|
+
unstyled pager. Uses --sg-* tokens with fallbacks like the rest of the theme. */
|
|
2150
|
+
.sv-grid-pagination {
|
|
2151
|
+
display: flex;
|
|
2152
|
+
align-items: center;
|
|
2153
|
+
justify-content: flex-end;
|
|
2154
|
+
gap: 24px;
|
|
2155
|
+
padding: 12px 16px;
|
|
2156
|
+
margin-top: 0;
|
|
2157
|
+
border: 1px solid var(--sg-border, #e2e8f0);
|
|
2158
|
+
border-top: 0;
|
|
2159
|
+
border-radius: 0 0 var(--sg-radius, 6px) var(--sg-radius, 6px);
|
|
2160
|
+
background: var(--sg-header-bg, #f1f5f9);
|
|
2161
|
+
color: var(--sg-fg, #0f172a);
|
|
2162
|
+
font-size: 13px;
|
|
2163
|
+
}
|
|
2164
|
+
/* Top-positioned pager: rounded + bordered on the top edge instead. */
|
|
2165
|
+
.sv-grid-pagination-top {
|
|
2166
|
+
border-top: 1px solid var(--sg-border, #e2e8f0);
|
|
2167
|
+
border-bottom: 0;
|
|
2168
|
+
border-radius: var(--sg-radius, 6px) var(--sg-radius, 6px) 0 0;
|
|
2169
|
+
}
|
|
2170
|
+
.sv-grid-pagination-pagesize {
|
|
2171
|
+
display: inline-flex;
|
|
2172
|
+
align-items: center;
|
|
2173
|
+
gap: 8px;
|
|
2174
|
+
color: var(--sg-muted, #64748b);
|
|
2175
|
+
}
|
|
2176
|
+
.sv-grid-pagination-pagesize-dd {
|
|
2177
|
+
position: relative;
|
|
2178
|
+
height: 28px;
|
|
2179
|
+
min-width: 62px;
|
|
2180
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
2181
|
+
border-radius: 5px;
|
|
2182
|
+
background: var(--sg-input-bg, #fff);
|
|
2183
|
+
}
|
|
2184
|
+
.sv-grid-pagination-pagesize-dd .sv-grid-dropdown,
|
|
2185
|
+
.sv-grid-pagination-pagesize-dd .sv-grid-dropdown-trigger {
|
|
2186
|
+
background: transparent;
|
|
2187
|
+
border-radius: 5px;
|
|
2188
|
+
font-size: 13px;
|
|
2189
|
+
color: var(--sg-fg, #0f172a);
|
|
2190
|
+
}
|
|
2191
|
+
.sv-grid-pagination-pagesize-dd:focus-within {
|
|
2192
|
+
border-color: var(--sg-accent, #2563eb);
|
|
2193
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 30%, transparent);
|
|
2194
|
+
}
|
|
2195
|
+
.sv-grid-pagination-range {
|
|
2196
|
+
color: var(--sg-fg, #0f172a);
|
|
2197
|
+
}
|
|
2198
|
+
.sv-grid-pagination-nav {
|
|
2199
|
+
display: inline-flex;
|
|
2200
|
+
align-items: center;
|
|
2201
|
+
gap: 4px;
|
|
2202
|
+
}
|
|
2203
|
+
.sv-grid-pagination-btn {
|
|
2204
|
+
display: inline-flex;
|
|
2205
|
+
align-items: center;
|
|
2206
|
+
justify-content: center;
|
|
2207
|
+
width: 28px;
|
|
2208
|
+
height: 28px;
|
|
2209
|
+
padding: 0;
|
|
2210
|
+
border: 0;
|
|
2211
|
+
border-radius: 5px;
|
|
2212
|
+
background: transparent;
|
|
2213
|
+
color: var(--sg-fg, #0f172a);
|
|
2214
|
+
font: inherit;
|
|
2215
|
+
font-size: 16px;
|
|
2216
|
+
cursor: pointer;
|
|
2217
|
+
transition: background-color 100ms ease, color 100ms ease;
|
|
2218
|
+
}
|
|
2219
|
+
.sv-grid-pagination-btn:hover:not(:disabled) {
|
|
2220
|
+
background: var(--sg-input-bg, #fff);
|
|
2221
|
+
color: var(--sg-accent, #2563eb);
|
|
2222
|
+
}
|
|
2223
|
+
.sv-grid-pagination-btn:disabled {
|
|
2224
|
+
color: var(--sg-muted, #64748b);
|
|
2225
|
+
opacity: 0.4;
|
|
2226
|
+
cursor: default;
|
|
2227
|
+
}
|
|
2228
|
+
.sv-grid-pagination-label {
|
|
2229
|
+
margin: 0 8px;
|
|
2230
|
+
color: var(--sg-fg, #0f172a);
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
/* ---- Menu / filter chrome completeness -----------------------------------
|
|
2234
|
+
These styles previously lived only in the examples' host stylesheet. Shipping
|
|
2235
|
+
them here means the column menu, filter/choose-columns popovers, facet
|
|
2236
|
+
checklist, operator select, filter buttons, and the scrollbar corner are
|
|
2237
|
+
fully themed out of the box for any consumer - all via --sg-* tokens. */
|
|
2238
|
+
|
|
2239
|
+
/* Hide the scrollbar corner when its scrollbar is absent. */
|
|
2240
|
+
.sv-grid-no-scroll .sv-grid-scrollbar-corner,
|
|
2241
|
+
.sv-grid-no-scroll .sv-grid-scrollbar {
|
|
2242
|
+
display: none;
|
|
2243
|
+
}
|
|
2244
|
+
/* Scrollbar corner divider uses the theme border token. */
|
|
2245
|
+
.sv-grid-scrollbar-corner,
|
|
2246
|
+
.sv-grid-scrollbar-corner-br {
|
|
2247
|
+
border-bottom-color: var(--sg-border, #e2e8f0);
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
/* Filter operator <select> (the dropdown that replaced the radio group). */
|
|
2251
|
+
.sv-grid-menu-operator-select {
|
|
2252
|
+
width: 100%;
|
|
2253
|
+
height: 28px;
|
|
2254
|
+
padding: 0 22px 0 8px;
|
|
2255
|
+
margin-bottom: 6px;
|
|
2256
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
2257
|
+
border-radius: 5px;
|
|
2258
|
+
background: var(--sg-input-bg, #fff);
|
|
2259
|
+
color: var(--sg-fg, #0f172a);
|
|
2260
|
+
font: inherit;
|
|
2261
|
+
font-size: 13px;
|
|
2262
|
+
cursor: pointer;
|
|
2263
|
+
appearance: none;
|
|
2264
|
+
-webkit-appearance: none;
|
|
2265
|
+
background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
|
|
2266
|
+
linear-gradient(135deg, currentColor 50%, transparent 50%);
|
|
2267
|
+
background-position: calc(100% - 12px) 12px, calc(100% - 8px) 12px;
|
|
2268
|
+
background-size: 4px 4px, 4px 4px;
|
|
2269
|
+
background-repeat: no-repeat;
|
|
2270
|
+
}
|
|
2271
|
+
.sv-grid-menu-operator-select:focus {
|
|
2272
|
+
outline: none;
|
|
2273
|
+
border-color: var(--sg-accent, #2563eb);
|
|
2274
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 40%, transparent);
|
|
2275
|
+
}
|
|
2276
|
+
/* Operator toggle button (active) uses the on-accent token. */
|
|
2277
|
+
.sv-grid-menu-operator-btn.is-active {
|
|
2278
|
+
color: var(--sg-on-accent, #fff);
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
/* Filter-only + choose-columns popover widths. */
|
|
2282
|
+
.sv-grid-filter-menu {
|
|
2283
|
+
width: 260px;
|
|
2284
|
+
}
|
|
2285
|
+
.sv-grid-choose-columns-menu {
|
|
2286
|
+
width: 240px;
|
|
2287
|
+
}
|
|
2288
|
+
.sv-grid-choose-columns-menu .sv-grid-facet-list {
|
|
2289
|
+
max-height: 280px;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
/* Native checkboxes in the facet list pick up the theme accent. */
|
|
2293
|
+
.sv-grid-facet input[type="checkbox"] {
|
|
2294
|
+
accent-color: var(--sg-accent, #2563eb);
|
|
2295
|
+
width: 14px;
|
|
2296
|
+
height: 14px;
|
|
2297
|
+
cursor: pointer;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
/* Submenu right-chevron on the "Choose columns" item. */
|
|
2301
|
+
.sv-grid-menu-item-chevron {
|
|
2302
|
+
margin-left: auto;
|
|
2303
|
+
transform: rotate(-90deg);
|
|
2304
|
+
opacity: 0.55;
|
|
2305
|
+
}
|
|
2306
|
+
.sv-grid-menu-item.is-open .sv-grid-menu-item-chevron {
|
|
2307
|
+
opacity: 1;
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
/* Magnifier prefix on the menu's search input. */
|
|
2311
|
+
.sv-grid-menu-search {
|
|
2312
|
+
padding-left: 28px;
|
|
2313
|
+
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='6'/%3E%3Cpath d='M20 20l-4.5-4.5'/%3E%3C/svg%3E");
|
|
2314
|
+
background-position: 8px center;
|
|
2315
|
+
background-repeat: no-repeat;
|
|
2316
|
+
background-size: 14px 14px;
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
/* Filter menu vertical rhythm. */
|
|
2320
|
+
.sv-grid-menu-filter > * + * {
|
|
2321
|
+
margin-top: 6px;
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
/* Column-header filter (funnel) button tints accent when a filter is active. */
|
|
2325
|
+
.sv-grid-col-filter-btn.is-active {
|
|
2326
|
+
color: var(--sg-accent, #2563eb);
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
/* Row-number gutter: neutralize the right border so it fuses with the header. */
|
|
2330
|
+
.sv-grid-row-number-column,
|
|
2331
|
+
.sv-grid-row-number-cell {
|
|
2332
|
+
border-right-color: var(--sg-header-bg, #f1f5f9);
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
/* Group row label colour. */
|
|
2336
|
+
.sv-grid-group-row > .sv-grid-cell {
|
|
2337
|
+
color: var(--sg-header-fg, #0f172a);
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
/* Filter-row operator button: soften when idle, accent border on hover/open. */
|
|
2341
|
+
.sv-grid-filter-operator-btn {
|
|
2342
|
+
opacity: 0.85;
|
|
2343
|
+
}
|
|
2344
|
+
.sv-grid-filter-operator-btn:hover {
|
|
2345
|
+
opacity: 1;
|
|
2346
|
+
border-color: var(--sg-accent, #2563eb);
|
|
2347
|
+
}
|
|
2348
|
+
.sv-grid-filter-operator-btn.is-open {
|
|
2349
|
+
opacity: 1;
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
/* Kanban board mode search box (board.searchable) */
|
|
2353
|
+
.sv-grid-board-search {
|
|
2354
|
+
display: flex;
|
|
2355
|
+
align-items: center;
|
|
2356
|
+
gap: 6px;
|
|
2357
|
+
margin-bottom: 8px;
|
|
2358
|
+
padding: 5px 9px;
|
|
2359
|
+
border: 1px solid var(--sg-border, #e4e7ec);
|
|
2360
|
+
border-radius: var(--sg-radius, 8px);
|
|
2361
|
+
background: var(--sg-bg, #fff);
|
|
2362
|
+
color: color-mix(in srgb, var(--sg-fg, #101828) 55%, transparent);
|
|
2363
|
+
max-width: 280px;
|
|
2364
|
+
}
|
|
2365
|
+
.sv-grid-board-search:focus-within {
|
|
2366
|
+
border-color: var(--sg-accent, #3b82f6);
|
|
2367
|
+
}
|
|
2368
|
+
.sv-grid-board-search input {
|
|
2369
|
+
flex: 1 1 auto;
|
|
2370
|
+
border: 0;
|
|
2371
|
+
outline: none;
|
|
2372
|
+
background: transparent;
|
|
2373
|
+
font: inherit;
|
|
2374
|
+
font-size: 0.82rem;
|
|
2375
|
+
color: var(--sg-fg, #101828);
|
|
2376
|
+
}
|