@lavalogic/scoria 0.37.9 → 0.37.11
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.
|
@@ -205,6 +205,7 @@
|
|
|
205
205
|
.virtual-spacer {
|
|
206
206
|
display: block;
|
|
207
207
|
border: none;
|
|
208
|
-
background: transparent;
|
|
209
208
|
pointer-events: none;
|
|
209
|
+
--row-stripe-height: var(--height, 3rem);
|
|
210
|
+
background-image: repeating-linear-gradient(to bottom, #ffffff 0, #ffffff var(--row-stripe-height), #f4f7f9 var(--row-stripe-height), #f4f7f9 calc(2 * var(--row-stripe-height)));
|
|
210
211
|
}</style>
|
|
@@ -67,14 +67,48 @@ function makeSpec(kind, def, internals = {}) {
|
|
|
67
67
|
});
|
|
68
68
|
return Object.freeze(spec);
|
|
69
69
|
}
|
|
70
|
+
/** Sensible per-kind default widths so a freshly-built table without explicit
|
|
71
|
+
* per-column `width` overrides ends up visually differentiated (rather than
|
|
72
|
+
* every column collapsing to the shared `minSize` fallback of 48px and then
|
|
73
|
+
* taking equal `1fr` shares of any remaining track space). Numbers chosen
|
|
74
|
+
* to roughly match what the legacy `<table>` element produced via the
|
|
75
|
+
* browser auto-layout algorithm for typical FloWMS column content. */
|
|
76
|
+
const KIND_DEFAULT_WIDTHS = {
|
|
77
|
+
text: 200,
|
|
78
|
+
number: 100,
|
|
79
|
+
checkbox: 80,
|
|
80
|
+
date: 140,
|
|
81
|
+
select: 160,
|
|
82
|
+
query: 160,
|
|
83
|
+
display: 150,
|
|
84
|
+
bubble: 150,
|
|
85
|
+
progress: 140,
|
|
86
|
+
validity: 60,
|
|
87
|
+
actions: 120,
|
|
88
|
+
rowSelection: 40,
|
|
89
|
+
expand: 40,
|
|
90
|
+
filterOnly: 0,
|
|
91
|
+
};
|
|
70
92
|
/** Returns the common Def props for an option object, excluding `id`.
|
|
71
93
|
* Callers pass `id` directly at the construction site so its strict
|
|
72
|
-
* literal-key narrowing survives the object spread.
|
|
73
|
-
|
|
94
|
+
* literal-key narrowing survives the object spread.
|
|
95
|
+
*
|
|
96
|
+
* `kind` selects the per-kind default width from `KIND_DEFAULT_WIDTHS`. The
|
|
97
|
+
* caller's `minWidth` / `width` values still win - the default is only
|
|
98
|
+
* applied if the consumer left the field undefined. */
|
|
99
|
+
function commonProps(opts, kind) {
|
|
100
|
+
const defaultWidth = KIND_DEFAULT_WIDTHS[kind];
|
|
74
101
|
return {
|
|
75
102
|
header: opts.header,
|
|
76
|
-
minSize
|
|
77
|
-
|
|
103
|
+
// Set minSize from the per-kind default too so
|
|
104
|
+
// `TableContext.registerDefs` does NOT run the canvas-based
|
|
105
|
+
// `measureHeaderWidth` fallback. That fallback floors every
|
|
106
|
+
// accessor column to ~130px when quick-filtering is enabled
|
|
107
|
+
// (it returns max(headerWidth, "Lorem Ipsum Dolor" width)),
|
|
108
|
+
// which equalises every accessor column width and erases the
|
|
109
|
+
// per-kind differentiation the factory just put in place.
|
|
110
|
+
minSize: opts.minWidth ?? defaultWidth,
|
|
111
|
+
width: opts.width ?? defaultWidth,
|
|
78
112
|
allowSorting: opts.allowSorting,
|
|
79
113
|
allowFiltering: opts.allowFiltering,
|
|
80
114
|
defaultHidden: opts.defaultHidden,
|
|
@@ -123,7 +157,7 @@ export function createColumnFactory() {
|
|
|
123
157
|
text(opts) {
|
|
124
158
|
const def = new TextInputDef({
|
|
125
159
|
id: opts.id,
|
|
126
|
-
...commonProps(opts),
|
|
160
|
+
...commonProps(opts, 'text'),
|
|
127
161
|
accessorFn: opts.accessor,
|
|
128
162
|
getDisplayValue: opts.getDisplayValue,
|
|
129
163
|
filterValueType: FilterValueType.String,
|
|
@@ -136,7 +170,7 @@ export function createColumnFactory() {
|
|
|
136
170
|
number(opts) {
|
|
137
171
|
const def = new NumberInputDef({
|
|
138
172
|
id: opts.id,
|
|
139
|
-
...commonProps(opts),
|
|
173
|
+
...commonProps(opts, 'number'),
|
|
140
174
|
accessorFn: opts.accessor,
|
|
141
175
|
getDisplayValue: opts.getDisplayValue,
|
|
142
176
|
min: opts.min ?? Number.MIN_SAFE_INTEGER,
|
|
@@ -151,7 +185,7 @@ export function createColumnFactory() {
|
|
|
151
185
|
checkbox(opts) {
|
|
152
186
|
const def = new CheckboxDef({
|
|
153
187
|
id: opts.id,
|
|
154
|
-
...commonProps(opts),
|
|
188
|
+
...commonProps(opts, 'checkbox'),
|
|
155
189
|
accessorFn: opts.accessor,
|
|
156
190
|
getDisplayValue: opts.getDisplayValue,
|
|
157
191
|
filterValueType: FilterValueType.Bool,
|
|
@@ -164,7 +198,7 @@ export function createColumnFactory() {
|
|
|
164
198
|
date(opts) {
|
|
165
199
|
const def = new DateInputDef({
|
|
166
200
|
id: opts.id,
|
|
167
|
-
...commonProps(opts),
|
|
201
|
+
...commonProps(opts, 'date'),
|
|
168
202
|
accessorFn: opts.accessor,
|
|
169
203
|
getDisplayValue: opts.getDisplayValue,
|
|
170
204
|
showTime: opts.withTime ?? false,
|
|
@@ -178,7 +212,7 @@ export function createColumnFactory() {
|
|
|
178
212
|
select(opts) {
|
|
179
213
|
const def = new SelectDef({
|
|
180
214
|
id: opts.id,
|
|
181
|
-
...commonProps(opts),
|
|
215
|
+
...commonProps(opts, 'select'),
|
|
182
216
|
accessorFn: opts.accessor,
|
|
183
217
|
getOptions: normaliseOptionsLoader(opts.options),
|
|
184
218
|
getDisplayValue: opts.getDisplayValue ?? ((row) => String(row[opts.id] ?? '')),
|
|
@@ -199,7 +233,7 @@ export function createColumnFactory() {
|
|
|
199
233
|
query(opts) {
|
|
200
234
|
const def = new QueryDef({
|
|
201
235
|
id: opts.id,
|
|
202
|
-
...commonProps(opts),
|
|
236
|
+
...commonProps(opts, 'query'),
|
|
203
237
|
accessorFn: opts.accessor,
|
|
204
238
|
// Forward the row to the consumer's query callback so it
|
|
205
239
|
// may scope results by row-level context (e.g. a `pod`
|
|
@@ -224,7 +258,7 @@ export function createColumnFactory() {
|
|
|
224
258
|
: undefined;
|
|
225
259
|
const def = new DisplayDef({
|
|
226
260
|
id,
|
|
227
|
-
...commonProps(opts),
|
|
261
|
+
...commonProps(opts, 'display'),
|
|
228
262
|
accessorFn: opts.value,
|
|
229
263
|
modal: opts.modal,
|
|
230
264
|
// Display columns have no implicit filter type (unlike the
|
|
@@ -241,7 +275,7 @@ export function createColumnFactory() {
|
|
|
241
275
|
const id = mintSyntheticId('bubble', opts.idHint);
|
|
242
276
|
const def = new BubbleDef({
|
|
243
277
|
id,
|
|
244
|
-
...commonProps(opts),
|
|
278
|
+
...commonProps(opts, 'bubble'),
|
|
245
279
|
accessorFn: opts.value,
|
|
246
280
|
getColour: opts.colour,
|
|
247
281
|
filterValueType: opts.filterValueType,
|
|
@@ -267,7 +301,7 @@ export function createColumnFactory() {
|
|
|
267
301
|
: () => 'grey';
|
|
268
302
|
const def = new ProgressBarDef({
|
|
269
303
|
id,
|
|
270
|
-
...commonProps(opts),
|
|
304
|
+
...commonProps(opts, 'progress'),
|
|
271
305
|
accessorFn: tupleAccessor,
|
|
272
306
|
getColour,
|
|
273
307
|
filterValueType: opts.filterValueType,
|
|
@@ -280,8 +314,8 @@ export function createColumnFactory() {
|
|
|
280
314
|
const def = new ValidityDef({
|
|
281
315
|
id,
|
|
282
316
|
header: opts?.header ?? '',
|
|
283
|
-
minSize: opts?.minWidth,
|
|
284
|
-
width: opts?.width,
|
|
317
|
+
minSize: opts?.minWidth ?? KIND_DEFAULT_WIDTHS.validity,
|
|
318
|
+
width: opts?.width ?? KIND_DEFAULT_WIDTHS.validity,
|
|
285
319
|
defaultHidden: opts?.defaultHidden,
|
|
286
320
|
resizable: opts?.resizable ?? false,
|
|
287
321
|
debug: opts?.debug,
|
|
@@ -295,6 +329,11 @@ export function createColumnFactory() {
|
|
|
295
329
|
const def = new RowSelectionDef({
|
|
296
330
|
isSelectable: opts?.isSelectable ?? (() => true),
|
|
297
331
|
});
|
|
332
|
+
// RowSelection has no public width option - apply the per-kind
|
|
333
|
+
// default so the column does not collapse to the 48px ColumnDef
|
|
334
|
+
// fallback and share equal `1fr` track space with wider columns.
|
|
335
|
+
def.width = KIND_DEFAULT_WIDTHS.rowSelection;
|
|
336
|
+
def.minSize = KIND_DEFAULT_WIDTHS.rowSelection;
|
|
298
337
|
return makeSpec('rowSelection', def);
|
|
299
338
|
},
|
|
300
339
|
actions(opts) {
|
|
@@ -317,6 +356,8 @@ export function createColumnFactory() {
|
|
|
317
356
|
actions: wrapped,
|
|
318
357
|
isEnabled: isEnabledFn ? (item) => isEnabledFn(item) : undefined,
|
|
319
358
|
});
|
|
359
|
+
// Actions has no public width option - apply the per-kind default.
|
|
360
|
+
def.width = KIND_DEFAULT_WIDTHS.actions;
|
|
320
361
|
return makeSpec('actions', def);
|
|
321
362
|
},
|
|
322
363
|
expand(opts) {
|
|
@@ -359,6 +400,9 @@ export function createColumnFactory() {
|
|
|
359
400
|
allowCopy: opts.inner?.allowCopy,
|
|
360
401
|
},
|
|
361
402
|
});
|
|
403
|
+
// Expand has no public width option - apply the per-kind default.
|
|
404
|
+
def.width = KIND_DEFAULT_WIDTHS.expand;
|
|
405
|
+
def.minSize = KIND_DEFAULT_WIDTHS.expand;
|
|
362
406
|
return makeSpec('expand', def);
|
|
363
407
|
},
|
|
364
408
|
filterOnly(opts) {
|
|
@@ -105,8 +105,19 @@
|
|
|
105
105
|
untrack(() => {
|
|
106
106
|
if (tableContext.focusStart && ref) {
|
|
107
107
|
const { row, column: index } = tableContext.focusStart;
|
|
108
|
+
// `columnDefs[index]` can be undefined when the focus
|
|
109
|
+
// coordinate references a memory slot that no longer
|
|
110
|
+
// exists (e.g. inner expand tables remount with a
|
|
111
|
+
// different column count, or a preset hydration races
|
|
112
|
+
// the column-layout init). Guard before destructuring
|
|
113
|
+
// so an out-of-range index degrades to "no focus
|
|
114
|
+
// restored" instead of a hard crash.
|
|
115
|
+
const def = tableContext.columnDefs[index];
|
|
116
|
+
if (!def) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
108
119
|
let element = ref.querySelector(
|
|
109
|
-
`#${tableContext.tableName}-cell-${row}-${
|
|
120
|
+
`#${tableContext.tableName}-cell-${row}-${def.id}`
|
|
110
121
|
) as HTMLElement | undefined;
|
|
111
122
|
|
|
112
123
|
if (element?.tabIndex == -1) {
|
|
@@ -136,8 +147,13 @@
|
|
|
136
147
|
untrack(() => {
|
|
137
148
|
if (tableContext.focusEnd && ref) {
|
|
138
149
|
const { row, column: index } = tableContext.focusEnd;
|
|
150
|
+
// Same out-of-range guard as the focusStart effect above.
|
|
151
|
+
const def = tableContext.columnDefs[index];
|
|
152
|
+
if (!def) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
139
155
|
const cell = ref.querySelector(
|
|
140
|
-
`#${tableContext.tableName}-cell-${row}-${
|
|
156
|
+
`#${tableContext.tableName}-cell-${row}-${def.id}`
|
|
141
157
|
) as HTMLElement | undefined;
|
|
142
158
|
|
|
143
159
|
cell?.scrollIntoView({
|
|
@@ -50,7 +50,17 @@ export class VirtualisationState {
|
|
|
50
50
|
constructor(deps, options = {}) {
|
|
51
51
|
this._deps = deps;
|
|
52
52
|
this._virtualisationThreshold = options.threshold ?? 100;
|
|
53
|
-
|
|
53
|
+
// Overscan controls how many rows above/below the viewport are
|
|
54
|
+
// kept rendered so a fast wheel-scroll does not surface an empty
|
|
55
|
+
// grey band before the next paint. The previous default of 10
|
|
56
|
+
// was too tight on tables with hundreds of rows: a single wheel
|
|
57
|
+
// flick easily moves the viewport past the overscan band before
|
|
58
|
+
// the `$derived` chain reschedules and re-renders the visible
|
|
59
|
+
// slice. 30 rows is enough to absorb a typical wheel flick at
|
|
60
|
+
// 60Hz while still keeping the rendered cell count bounded
|
|
61
|
+
// (~52 rows for a 600px viewport at the default 28-48px row
|
|
62
|
+
// height: 12 visible + 30 below + 10 above = 52).
|
|
63
|
+
this._overscan = options.overscan ?? 30;
|
|
54
64
|
}
|
|
55
65
|
get virtualisationThreshold() {
|
|
56
66
|
return this._virtualisationThreshold;
|