@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
- function commonProps(opts) {
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: opts.minWidth,
77
- width: opts.width,
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}-${tableContext.columnDefs[index].id}`
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}-${tableContext.columnDefs[index].id}`
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
- this._overscan = options.overscan ?? 10;
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;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lavalogic/scoria",
3
3
  "description": "Svelte components used for the FloWMS Web Frontend",
4
- "version": "0.37.9",
4
+ "version": "0.37.11",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },