@lavalogic/scoria 0.37.23 → 0.37.25

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.
@@ -24,6 +24,37 @@
24
24
  cell.column.columnDef.getDisplayValue?.(cell.row.original, cell.row.originalIndex) ?? value
25
25
  );
26
26
 
27
+ /** For Date-typed display columns, expose both the local-time and UTC
28
+ * representations of the raw accessor value as a multi-line tooltip.
29
+ * The cell's body shows the locale-formatted value (via
30
+ * `getDisplayValue`); the title attribute below lets users hover to
31
+ * see Local + UTC side-by-side without expanding the column. */
32
+ const dateTooltip: string | undefined = $derived.by(() => {
33
+ if (cell.column.columnDef.filterValueType !== 'Date') {
34
+ return undefined;
35
+ }
36
+ if (value == null || value === '' || value instanceof Promise) {
37
+ return undefined;
38
+ }
39
+ let d: Date | undefined;
40
+ if (value instanceof Date) {
41
+ d = value;
42
+ } else if (typeof value === 'string' || typeof value === 'number') {
43
+ const parsed = new Date(value);
44
+ if (!Number.isNaN(parsed.getTime())) {
45
+ d = parsed;
46
+ }
47
+ }
48
+ if (d == null) {
49
+ return undefined;
50
+ }
51
+ // Use `toLocaleString` for both lines so the visual format matches
52
+ // (only the time zone differs). The `timeZone: 'UTC'` override on
53
+ // the second call renders the same date / time but reinterpreted
54
+ // in UTC, which is what the user expects to see on hover.
55
+ return `Local: ${d.toLocaleString()}\nUTC: ${d.toLocaleString(undefined, { timeZone: 'UTC' })}`;
56
+ });
57
+
27
58
  const tableContext = getContext<TableContext<T, RowIdType>>(TableContext.identifier);
28
59
 
29
60
  const options: Array<SelectOption<unknown>> = $derived(
@@ -106,6 +137,7 @@
106
137
  class:has-modal={!!modalComponent}
107
138
  tabIndex={hasValue ? 0 : undefined}
108
139
  class="focusable"
140
+ title={dateTooltip}
109
141
  style={focusDetails.outline}
110
142
  class:multifocus={hasValue && focusDetails.isMultiFocused}
111
143
  class:multifocus-start={hasValue && focusDetails.isMultiFocusStart}
@@ -325,26 +325,33 @@ export function createColumnFactory() {
325
325
  : opts.filterValueType === 'String'
326
326
  ? 'text'
327
327
  : 'display';
328
- // When the consumer flagged the column as a Date filter, auto-
329
- // format the displayed value via `toLocaleString` so timestamps
330
- // surface in the user's locale rather than the raw ISO 8601
331
- // payload returned by the server. Promise / scalar / null
332
- // inputs are all handled. Filter / sort logic still operates
333
- // on the raw accessor value below.
328
+ // For Date-typed display columns, leave the accessor returning
329
+ // the raw Date / ISO string (filter / sort logic still operates
330
+ // on the raw value) and surface the formatted local-time
331
+ // string via `getDisplayValue`. The cell renders
332
+ // `getDisplayValue` for visible text and reads the raw value
333
+ // from `accessorFn` to compose the hover tooltip showing both
334
+ // the local-time and UTC representations.
334
335
  const rawValue = opts.value;
335
- const valueAccessor = opts.filterValueType === 'Date'
336
+ const valueAccessor = rawValue;
337
+ const dateDisplayValue = opts.filterValueType === 'Date'
336
338
  ? (row, index) => {
337
339
  const v = rawValue(row, index);
338
340
  if (v instanceof Promise) {
339
- return v.then(formatLocaleDate);
341
+ return v.then((resolved) => {
342
+ const formatted = formatLocaleDate(resolved);
343
+ return formatted == null ? null : String(formatted);
344
+ });
340
345
  }
341
- return formatLocaleDate(v);
346
+ const formatted = formatLocaleDate(v);
347
+ return formatted == null ? null : String(formatted);
342
348
  }
343
- : rawValue;
349
+ : undefined;
344
350
  const def = new DisplayDef({
345
351
  id,
346
352
  ...commonProps(opts, inferredKind),
347
353
  accessorFn: valueAccessor,
354
+ getDisplayValue: dateDisplayValue,
348
355
  modal: opts.modal,
349
356
  // Display columns have no implicit filter type (unlike the
350
357
  // typed kinds where the factory pins one based on the
@@ -359,7 +359,8 @@
359
359
  --button-padding-vertical: 0.42rem;
360
360
  }
361
361
  .spread_row .block.records-per-page {
362
- --input-width: 2.75rem;
362
+ --input-min-width: 3.5rem;
363
+ --input-width: 3.5rem;
363
364
  }
364
365
  .spread_row span,
365
366
  .spread_row label {
@@ -146,6 +146,10 @@ export function attachDropdownPortal(options) {
146
146
  }
147
147
  originalParent = dropdown.parentNode;
148
148
  originalNextSibling = dropdown.nextSibling;
149
+ // Clear any `display: none` left over from a previous portalIn
150
+ // (see the defensive hide there) so the dropdown is visible
151
+ // once teleported into the new host.
152
+ dropdown.style.display = '';
149
153
  // Append the dropdown to the nearest open native `<dialog>` ancestor
150
154
  // when one exists, otherwise to `document.body`. A `<dialog>` opened
151
155
  // via `showModal()` renders in the browser's top layer, which sits
@@ -200,6 +204,18 @@ export function attachDropdownPortal(options) {
200
204
  dropdown.style.maxHeight = '';
201
205
  dropdown.style.zIndex = '';
202
206
  dropdown.removeAttribute('data-scoria-portal-dropdown');
207
+ // Defensive: if Svelecte left the dropdown without its own
208
+ // `display: none` (e.g. on certain transition-cancel race paths
209
+ // the open/close class flip and the inline style update arrive
210
+ // out of order), the dropdown would now be `position: static;
211
+ // display: block` in `document.body` and contribute its option
212
+ // list's height to the body scroll size - which is what surfaces
213
+ // as a stray page-level vertical scrollbar after toggling a
214
+ // select. Force `display: none` here; Svelecte will restore the
215
+ // inline style on next open via its own class-driven render.
216
+ if (!dropdown.classList.contains('is-open')) {
217
+ dropdown.style.display = 'none';
218
+ }
203
219
  if (originalParent != null) {
204
220
  if (originalNextSibling != null && originalNextSibling.parentNode === originalParent) {
205
221
  originalParent.insertBefore(dropdown, originalNextSibling);
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.23",
4
+ "version": "0.37.25",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },