@lavalogic/scoria 0.37.25 → 0.37.27
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.
|
@@ -46,7 +46,11 @@
|
|
|
46
46
|
onchange,
|
|
47
47
|
}: DateInputProps = $props();
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
// Null-safe: a fresh `<DateInput>` with no value passes `value=null`
|
|
50
|
+
// through the prop. The old `value.trim()` crashed with "Cannot read
|
|
51
|
+
// properties of null (reading 'trim')" the moment a required, empty
|
|
52
|
+
// DateInput rendered inside e.g. the Order Details collapsible card.
|
|
53
|
+
const isInvalid = $derived((required && !value?.trim()) || invalid);
|
|
50
54
|
|
|
51
55
|
function onkeydown(e: KeyboardEvent): void {
|
|
52
56
|
if (e.key === 'Enter') {
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
lang="ts"
|
|
5
5
|
generics="T extends object, FilterValueType, RowIdType extends Primitive"
|
|
6
6
|
>
|
|
7
|
-
import Portal from '../../../../Portal.svelte';
|
|
8
7
|
import type { AbstractDisplayDef } from '../../../Types/Columns/Definitions/Display/AbstractDisplayDef.svelte.js';
|
|
9
8
|
import { TableContext } from '../../../Types/Context/TableContext.svelte.js';
|
|
10
9
|
import { type Primitive } from '../../../Types/Context/TableInitOptions.js';
|
|
@@ -210,19 +209,22 @@
|
|
|
210
209
|
</div>
|
|
211
210
|
{#if showModal && modalComponent}
|
|
212
211
|
<!--
|
|
213
|
-
|
|
214
|
-
`
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
212
|
+
The component receives the public `DisplayModalProps<T>` shape
|
|
213
|
+
`{ row, close }`. The dialog is NOT wrapped in
|
|
214
|
+
`<Portal target={document.body}>` any more. `<dialog>` opened
|
|
215
|
+
via `showModal()` enters the browser's top layer, so it already
|
|
216
|
+
escapes overflow:hidden / sticky / transform ancestors without
|
|
217
|
+
needing to be re-parented. Moving the `<div>` ancestor mid-mount
|
|
218
|
+
(Portal's $effect runs during the same microtask round as
|
|
219
|
+
BaseModal's `tick().then` `dialog.showModal()`) was popping the
|
|
220
|
+
dialog out of the top layer and leaving it to render inline at
|
|
221
|
+
the bottom of the page.
|
|
218
222
|
-->
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
/>
|
|
225
|
-
</Portal>
|
|
223
|
+
{@const ModalComponent = modalComponent}
|
|
224
|
+
<ModalComponent
|
|
225
|
+
row={cell.row.original}
|
|
226
|
+
close={onclose}
|
|
227
|
+
/>
|
|
226
228
|
{/if}
|
|
227
229
|
|
|
228
230
|
<style>.focusable,
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
>
|
|
7
7
|
import Action from '../../../../Action.svelte';
|
|
8
8
|
import type { IconProps } from '../../../../IconProps.js';
|
|
9
|
-
import Portal from '../../../../Portal.svelte';
|
|
10
9
|
import type { AbstractDisplayDef } from '../../../Types/Columns/Definitions/Display/AbstractDisplayDef.svelte.js';
|
|
11
10
|
import { TableContext } from '../../../Types/Context/TableContext.svelte.js';
|
|
12
11
|
import type { TableFocusDetails } from '../../../Types/Focus/TableFocusDetails.svelte.js';
|
|
@@ -161,22 +160,30 @@
|
|
|
161
160
|
/>
|
|
162
161
|
</div>
|
|
163
162
|
{#if showModal}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
163
|
+
<!--
|
|
164
|
+
`ColumnDef.modal` is declared as
|
|
165
|
+
`Component<DisplayModalProps<TRow>>` on the public column
|
|
166
|
+
options, which exposes a stable `{ row, close }` prop
|
|
167
|
+
shape. Cell renderers used to hand a legacy
|
|
168
|
+
`{ cell, onclose, onsubmit }` triple here, which made the
|
|
169
|
+
public type lie about the runtime shape - migrated to
|
|
170
|
+
`{ row, close }` so consumer-authored modals can rely on
|
|
171
|
+
the documented type.
|
|
172
|
+
|
|
173
|
+
The dialog is NOT wrapped in `<Portal target={document.body}>`
|
|
174
|
+
any more. `<dialog>` opened via `showModal()` enters the
|
|
175
|
+
browser's top layer, so it already escapes overflow:hidden /
|
|
176
|
+
sticky / transform ancestors without needing to be re-parented.
|
|
177
|
+
Moving the `<div>` ancestor mid-mount (Portal's $effect runs
|
|
178
|
+
during the same microtask round as BaseModal's `tick().then`
|
|
179
|
+
`dialog.showModal()`) was popping the dialog out of the top
|
|
180
|
+
layer and leaving it to render inline at the bottom of the
|
|
181
|
+
page.
|
|
182
|
+
-->
|
|
183
|
+
<ColumnDef.modal
|
|
184
|
+
row={cell.row.original}
|
|
185
|
+
close={onclose}
|
|
186
|
+
/>
|
|
180
187
|
{/if}
|
|
181
188
|
</div>
|
|
182
189
|
|
|
@@ -25,6 +25,41 @@
|
|
|
25
25
|
import HighlightAllHeader from './HighlightAllHeader.svelte';
|
|
26
26
|
import SelectAllHeader from './SelectAllHeader.svelte';
|
|
27
27
|
import type { TableHeadProps } from './TableHeadProps.js';
|
|
28
|
+
|
|
29
|
+
/** Per-column toggle queue. The header onclick fires this so multiple
|
|
30
|
+
* clicks within a small window stack and apply on a single timer
|
|
31
|
+
* flush, which lets a fast double-click cycle straight to desc (or
|
|
32
|
+
* to clear-sort) without the user having to wait for the asc state
|
|
33
|
+
* to apply and reload data first. Single clicks pay a ~150 ms delay
|
|
34
|
+
* before the toggle runs, which is below most users' perceived
|
|
35
|
+
* latency threshold while still wide enough to batch dblclicks. */
|
|
36
|
+
const SORT_DEBOUNCE_MS = 150;
|
|
37
|
+
const sortDebounce = new WeakMap<
|
|
38
|
+
object,
|
|
39
|
+
{ count: number; timer: ReturnType<typeof setTimeout> | null }
|
|
40
|
+
>();
|
|
41
|
+
function debouncedSortToggle(columnRef: object, handler: ((e?: unknown) => void) | undefined): void {
|
|
42
|
+
if (!handler) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
let entry = sortDebounce.get(columnRef);
|
|
46
|
+
if (!entry) {
|
|
47
|
+
entry = { count: 0, timer: null };
|
|
48
|
+
sortDebounce.set(columnRef, entry);
|
|
49
|
+
}
|
|
50
|
+
entry.count += 1;
|
|
51
|
+
if (entry.timer != null) {
|
|
52
|
+
clearTimeout(entry.timer);
|
|
53
|
+
}
|
|
54
|
+
entry.timer = setTimeout(() => {
|
|
55
|
+
const toFire = entry!.count;
|
|
56
|
+
entry!.count = 0;
|
|
57
|
+
entry!.timer = null;
|
|
58
|
+
for (let i = 0; i < toFire; i++) {
|
|
59
|
+
handler();
|
|
60
|
+
}
|
|
61
|
+
}, SORT_DEBOUNCE_MS);
|
|
62
|
+
}
|
|
28
63
|
</script>
|
|
29
64
|
|
|
30
65
|
<script
|
|
@@ -290,7 +325,7 @@
|
|
|
290
325
|
hideTooltip(header.column.columnDef);
|
|
291
326
|
}}
|
|
292
327
|
class="contents"
|
|
293
|
-
onclick={header.column.getToggleSortingHandler()}
|
|
328
|
+
onclick={() => debouncedSortToggle(header.column, header.column.getToggleSortingHandler())}
|
|
294
329
|
>
|
|
295
330
|
<div class="th cursor-pointer select-none">
|
|
296
331
|
<div class="header-text-wrapper">
|