@lavalogic/scoria 0.37.16 → 0.37.18

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.
@@ -727,6 +727,8 @@ div.input-cell {
727
727
  overflow: visible;
728
728
  padding-left: 0;
729
729
  padding-right: 0;
730
+ border-right: solid 1px #cbd4da !important;
731
+ box-sizing: border-box;
730
732
  }
731
733
  div.input-cell div.stick-right {
732
734
  position: absolute;
@@ -368,56 +368,71 @@
368
368
  ondragleave={disableDragVisual}
369
369
  ondrop={disableDragVisual}
370
370
  >
371
- <div class="visibility-wrapper">
372
- <Checkbox
373
- icon={{ name: 'assessment', size: Size.Medium }}
374
- size={Size.FillWidth}
375
- id={`is-visible-${tableContext.tableName}-${columnDef.id}`}
376
- value={tableContext.columnVisibility[columnDef.id] !== false}
377
- onchange={onVisibilityChange}
378
- />
379
- </div>
380
-
381
- {#if tableContext.allowColumnReordering}
382
- <!--
383
- Tri-state pin selector rendered via `ThreeStateRadio`,
384
- matching the visual segmented-control used elsewhere in
385
- the table (filter Active state, etc.). The three
386
- `Ternary` slots map to:
387
- False → pinned LEFT (pin-left icon, leftmost)
388
- Neutral → unpinned (circle-cross icon, middle)
389
- True → pinned RIGHT (pin-right icon, rightmost)
390
- Expand columns can never be pinned, so the group goes
391
- fully disabled for them.
392
- -->
393
- <div
394
- class="pin-selector"
395
- class:disabled={isExpandColumn}
371
+ <!--
372
+ `.row-main` stacks the column label on top and the
373
+ visibility / pin controls underneath, so the label gets
374
+ the full card width to wrap into (instead of competing
375
+ horizontally with the controls). `.reorder-buttons` stays
376
+ as a sibling outside `.row-main` so the up / down arrows
377
+ remain vertically centred on the right edge of the card.
378
+ -->
379
+ <div class="row-main">
380
+ <span
381
+ class="column-label"
382
+ title={typeof columnDef.header === 'string' ? columnDef.header : columnDef.id}
396
383
  >
397
- <ThreeStateRadio
398
- value={pinSide}
399
- disabled={isExpandColumn}
400
- noLabel="Pin to left"
401
- neutralLabel="Unpinned"
402
- yesLabel="Pin to right"
403
- noIcon={{ name: 'pin-left', size: Size.Medium }}
404
- neutralIcon={{ name: 'circle-cross', size: Size.Medium }}
405
- yesIcon={{ name: 'pin-right', size: Size.Medium }}
406
- onchange={(next: Ternary) => {
407
- setPin(next);
408
- }}
409
- />
384
+ {#if typeof columnDef.header === 'function'}
385
+ <!-- FIXME this needs testing -->
386
+ {@render columnDef.header()}
387
+ {:else}
388
+ {columnDef.header}
389
+ {/if}
390
+ </span>
391
+
392
+ <div class="row-controls">
393
+ <div class="visibility-wrapper">
394
+ <Checkbox
395
+ icon={{ name: 'assessment', size: Size.MediumSmall }}
396
+ size={Size.FillWidth}
397
+ id={`is-visible-${tableContext.tableName}-${columnDef.id}`}
398
+ value={tableContext.columnVisibility[columnDef.id] !== false}
399
+ onchange={onVisibilityChange}
400
+ />
401
+ </div>
402
+
403
+ {#if tableContext.allowColumnReordering}
404
+ <!--
405
+ Tri-state pin selector rendered via `ThreeStateRadio`,
406
+ matching the visual segmented-control used elsewhere in
407
+ the table (filter Active state, etc.). The three
408
+ `Ternary` slots map to:
409
+ False → pinned LEFT (pin-left icon, leftmost)
410
+ Neutral → unpinned (circle-cross icon, middle)
411
+ True → pinned RIGHT (pin-right icon, rightmost)
412
+ Expand columns can never be pinned, so the group goes
413
+ fully disabled for them.
414
+ -->
415
+ <div
416
+ class="pin-selector"
417
+ class:disabled={isExpandColumn}
418
+ >
419
+ <ThreeStateRadio
420
+ value={pinSide}
421
+ disabled={isExpandColumn}
422
+ noLabel="Pin to left"
423
+ neutralLabel="Unpinned"
424
+ yesLabel="Pin to right"
425
+ noIcon={{ name: 'pin-left', size: Size.MediumSmall }}
426
+ neutralIcon={{ name: 'circle-cross', size: Size.MediumSmall }}
427
+ yesIcon={{ name: 'pin-right', size: Size.MediumSmall }}
428
+ onchange={(next: Ternary) => {
429
+ setPin(next);
430
+ }}
431
+ />
432
+ </div>
433
+ {/if}
410
434
  </div>
411
- {/if}
412
-
413
- <span class="column-label">
414
- {#if typeof columnDef.header === 'function'}
415
- <!-- FIXME this needs testing -->
416
- {@render columnDef.header()}
417
- {:else}
418
- {columnDef.header}
419
- {/if}
420
- </span>
435
+ </div>
421
436
 
422
437
  {#if tableContext.allowColumnReordering}
423
438
  <!--
@@ -478,10 +493,11 @@ li.targeted {
478
493
 
479
494
  li.single-column:not(.anchor) {
480
495
  display: flex;
481
- gap: 0.75rem;
482
- padding: 0.5rem 0.75rem;
483
- margin-bottom: 0.5rem;
496
+ flex-flow: row nowrap;
484
497
  align-items: center;
498
+ gap: 0.5rem;
499
+ padding: 0.5rem 0.625rem;
500
+ margin-bottom: 0.375rem;
485
501
  background-color: #ffffff;
486
502
  border: solid 1px #cbd4da;
487
503
  border-radius: 4px;
@@ -499,15 +515,30 @@ li.single-column:not(.anchor):last-of-type {
499
515
  margin-bottom: 0;
500
516
  }
501
517
 
518
+ .row-main {
519
+ flex: 1 1 auto;
520
+ min-width: 0;
521
+ display: flex;
522
+ flex-flow: column nowrap;
523
+ gap: 0.375rem;
524
+ }
525
+
526
+ .row-controls {
527
+ display: flex;
528
+ flex-flow: row nowrap;
529
+ align-items: center;
530
+ gap: 0.5rem;
531
+ }
532
+
502
533
  .visibility-wrapper {
503
- width: 3rem;
504
- height: 3rem;
534
+ width: 2.25rem;
535
+ height: 2.25rem;
505
536
  display: flex;
506
537
  flex-shrink: 0;
507
538
  }
508
539
 
509
540
  .pin-selector {
510
- --input-height: 3rem;
541
+ --input-height: 2.25rem;
511
542
  --input-width: auto;
512
543
  flex-shrink: 0;
513
544
  display: flex;
@@ -519,11 +550,19 @@ li.single-column:not(.anchor):last-of-type {
519
550
  }
520
551
 
521
552
  .column-label {
522
- flex: 1 1 auto;
553
+ width: 100%;
523
554
  min-width: 0;
555
+ font-size: 1.125rem;
556
+ font-weight: 500;
557
+ line-height: 1.25;
558
+ color: #3a4952;
559
+ display: -webkit-box;
560
+ -webkit-line-clamp: 2;
561
+ -webkit-box-orient: vertical;
524
562
  overflow: hidden;
525
563
  text-overflow: ellipsis;
526
- white-space: nowrap;
564
+ word-break: normal;
565
+ overflow-wrap: anywhere;
527
566
  }
528
567
 
529
568
  .reorder-buttons {
@@ -188,6 +188,7 @@ div.bubble-parent {
188
188
  display: flex;
189
189
  flex-flow: row wrap;
190
190
  max-width: 100%;
191
+ height: 100%;
191
192
  overflow-x: clip;
192
193
  justify-content: flex-start;
193
194
  align-items: center;
@@ -127,12 +127,26 @@ function commonProps(opts, kind) {
127
127
  filterGroup: opts.filterGroup,
128
128
  };
129
129
  }
130
- /** Generate a synthetic id for a column kind that does not take an `id`
131
- * from the caller. Mirrors the existing pattern used by `ActionsDef` and
132
- * `RowSelectionDef`. The returned value is the `SyntheticColumnId` brand. */
130
+ /** Generate a column id for a kind whose option type does not require
131
+ * an `id` from the caller.
132
+ *
133
+ * - When `hint` is provided, use it as the id verbatim. This is the
134
+ * server-side-filtering contract: callers who pass an `idHint` are
135
+ * indicating that the column maps to a real backend field (so the
136
+ * filter row's `id` flows through `createRemoteRowSource` as the
137
+ * query-string key the server expects). Adding a synthetic prefix
138
+ * here would mean every `col.display({ idHint: 'outboundType_id' })`
139
+ * serialised as `display-outboundType_id` and the server rejected
140
+ * the request with "field does not exist".
141
+ * - When no `hint` is provided, fall back to `${prefix}-${UUID}` so
142
+ * the id is still unique within the table. Such columns cannot
143
+ * participate in server-driven filtering anyway (the server would
144
+ * not know the random suffix). */
133
145
  function mintSyntheticId(prefix, hint) {
134
- const suffix = hint ?? generateUUID();
135
- return syntheticColumnId(`${prefix}-${suffix}`);
146
+ if (hint != null) {
147
+ return syntheticColumnId(hint);
148
+ }
149
+ return syntheticColumnId(`${prefix}-${generateUUID()}`);
136
150
  }
137
151
  /** Resolve an `options` field that may be a static array or an async
138
152
  * loader into the always-loader form the Def expects. Mutable arrays are
@@ -160,4 +160,6 @@
160
160
  display: flex;
161
161
  flex-flow: column nowrap;
162
162
  background-color: #ffffff;
163
+ padding: 0 1.25rem;
164
+ box-sizing: border-box;
163
165
  }</style>
@@ -156,10 +156,25 @@
156
156
  `#${tableContext.tableName}-cell-${row}-${def.id}`
157
157
  ) as HTMLElement | undefined;
158
158
 
159
+ // Skip horizontal scroll-into-view for pinned (sticky)
160
+ // cells. Sticky positioning makes the cell visually
161
+ // "always on screen", but its true layout position is at
162
+ // its real column track - which for a right-pinned column
163
+ // in a wider-than-viewport table is offscreen. Asking the
164
+ // browser to `scrollIntoView({inline: 'nearest'})` then
165
+ // scrolls the table horizontally to bring the cell's
166
+ // underlying position into view, which is the user-facing
167
+ // "table jumps right when I click the Actions button"
168
+ // bug. The vertical block axis still scrolls because the
169
+ // row's vertical position is the user's actual scroll
170
+ // target.
171
+ const isPinned =
172
+ cell?.classList.contains('pinned-left') ||
173
+ cell?.classList.contains('pinned-right');
159
174
  cell?.scrollIntoView({
160
175
  behavior: 'instant',
161
176
  block: 'nearest',
162
- inline: 'nearest',
177
+ inline: isPinned ? undefined : 'nearest',
163
178
  });
164
179
  }
165
180
  });
@@ -1869,6 +1869,17 @@
1869
1869
  overflow: visible;
1870
1870
  padding-left: 0;
1871
1871
  padding-right: 0;
1872
+ // Reassert the column-separator border-right here so it survives
1873
+ // even when a narrow select-filter cell's internal widget (the
1874
+ // SingleSelect / MultiSelect dropdown trigger) paints flush to
1875
+ // the right edge. Without this rule, two adjacent select filters
1876
+ // visually merge because the inner widget's background covers
1877
+ // the cell's table-cell-basic border. `!important` is here to
1878
+ // override the more-specific .focusable rule from
1879
+ // table-cell-basic that occasionally re-paints the right edge
1880
+ // transparent during focus.
1881
+ border-right: solid 1px colours.$ui-blue-4 !important;
1882
+ box-sizing: border-box;
1872
1883
 
1873
1884
  div.stick-right {
1874
1885
  position: absolute;
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.16",
4
+ "version": "0.37.18",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },