@ultimat3/ui 31.16.0 → 31.18.0
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/ui",
|
|
3
|
-
"version": "31.
|
|
3
|
+
"version": "31.18.0",
|
|
4
4
|
"description": "SolidJS design system: semantic design tokens, dark/RTL-ready SCSS modules, a11y primitives",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ultimate",
|
|
@@ -58,11 +58,11 @@
|
|
|
58
58
|
"icons": "bun run src/icons/build-icons.ts"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@ultimat3/core": "31.
|
|
62
|
-
"@ultimat3/i18n": "31.
|
|
63
|
-
"@ultimat3/money": "31.
|
|
64
|
-
"@ultimat3/schema": "31.
|
|
65
|
-
"@ultimat3/time": "31.
|
|
61
|
+
"@ultimat3/core": "31.18.0",
|
|
62
|
+
"@ultimat3/i18n": "31.18.0",
|
|
63
|
+
"@ultimat3/money": "31.18.0",
|
|
64
|
+
"@ultimat3/schema": "31.18.0",
|
|
65
|
+
"@ultimat3/time": "31.18.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"solid-js": "^1.9.0"
|
|
@@ -59,8 +59,11 @@ export function Combobox(props: ComboboxProps): JSX.Element {
|
|
|
59
59
|
rt.onCleanup(() => notify.cancel());
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
// One filter pass per change of query or options: the datalist, the status class and its count
|
|
63
|
+
// all read it, and a plain function ran the O(n) pass once per read.
|
|
64
|
+
const matches = rt.createMemo((): readonly ComboboxOption[] =>
|
|
65
|
+
filterOptions(props.options, props.value ?? '', props.limit),
|
|
66
|
+
);
|
|
64
67
|
|
|
65
68
|
const describedBy = (): string =>
|
|
66
69
|
[props['aria-describedby'], statusId].filter((id) => id !== undefined).join(' ');
|
|
@@ -64,7 +64,11 @@ export function Drawer(props: DrawerProps): JSX.Element {
|
|
|
64
64
|
rt.createEffect(() => {
|
|
65
65
|
const dialog = element;
|
|
66
66
|
if (dialog === undefined) return;
|
|
67
|
-
if (props.open && !dialog.open)
|
|
67
|
+
if (props.open && !dialog.open) {
|
|
68
|
+
// `snap` is where a sheet OPENS: last time's drag to `full` does not decide this time.
|
|
69
|
+
setSnap(props.snap ?? 'half');
|
|
70
|
+
dialog.showModal();
|
|
71
|
+
}
|
|
68
72
|
if (!props.open && dialog.open) dialog.close();
|
|
69
73
|
});
|
|
70
74
|
|
|
@@ -92,14 +92,16 @@ export function SearchableSelect(props: SearchableSelectProps): JSX.Element {
|
|
|
92
92
|
finiteOption('SearchableSelect', 'debounceMs', props.debounceMs ?? DEBOUNCE_DEFAULT_MS),
|
|
93
93
|
);
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
// One pass per keystroke: the rows, the status text and its class, and every key handler read
|
|
96
|
+
// the view, and a plain function re-ran the O(n) check and ranking once per read.
|
|
97
|
+
const view = rt.createMemo((): SearchableView => {
|
|
96
98
|
checkOptions(props.options, chosen());
|
|
97
99
|
const limit = finiteCount('SearchableSelect', 'limit', props.limit ?? COMBOBOX_LIMIT, 0);
|
|
98
100
|
// Under `onSearch` the caller already matched: ranking again could drop what it found.
|
|
99
101
|
return props.onSearch === undefined
|
|
100
102
|
? searchableView(props.options, query() ?? '', limit)
|
|
101
103
|
: searchableView(props.options, '', limit);
|
|
102
|
-
};
|
|
104
|
+
});
|
|
103
105
|
const picked = (): readonly SearchableOption[] => chosenOptions(props.options, chosen());
|
|
104
106
|
const text = (): string => query() ?? (many() ? '' : (picked()[0]?.label ?? ''));
|
|
105
107
|
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
|
|
9
9
|
import type { JSX } from 'solid-js';
|
|
10
10
|
import type { Politeness } from '../a11y';
|
|
11
|
+
import { solid } from '../theme/solid-adapter';
|
|
11
12
|
import { type ToastItem, visibleToasts } from '../toast/toast-state';
|
|
12
13
|
import type { ToastStore } from '../toast/toast-store';
|
|
13
14
|
import { useToasts } from '../toast/use-toasts';
|
|
14
15
|
import { Button } from './Button';
|
|
16
|
+
import { keyedRows } from './data-table-rows';
|
|
15
17
|
import { Toast, type ToastPlacement, ToastRegion } from './Toast';
|
|
18
|
+
import { thunk } from './thunk';
|
|
16
19
|
|
|
17
20
|
export interface ToasterProps {
|
|
18
21
|
/** The queue. One per app — `toastStore()` in the island that mounts this. */
|
|
@@ -27,6 +30,7 @@ export interface ToasterProps {
|
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export function Toaster(props: ToasterProps): JSX.Element {
|
|
33
|
+
const rt = solid();
|
|
30
34
|
const queue = useToasts(props.store);
|
|
31
35
|
|
|
32
36
|
/**
|
|
@@ -34,7 +38,7 @@ export function Toaster(props: ToasterProps): JSX.Element {
|
|
|
34
38
|
* it disappears on a timer, and a keyboard user who was typing when it arrived never reached it.
|
|
35
39
|
* Taking the undo also dismisses — the offer has been answered.
|
|
36
40
|
*/
|
|
37
|
-
const actionSlot = (item: ToastItem): JSX.Element => {
|
|
41
|
+
const actionSlot = (item: ToastItem, id: string): JSX.Element => {
|
|
38
42
|
const action = item.action;
|
|
39
43
|
if (action === undefined) return null;
|
|
40
44
|
return (
|
|
@@ -44,7 +48,7 @@ export function Toaster(props: ToasterProps): JSX.Element {
|
|
|
44
48
|
tone={item.tone}
|
|
45
49
|
onClick={() => {
|
|
46
50
|
action.onAction();
|
|
47
|
-
props.store.dismiss(
|
|
51
|
+
props.store.dismiss(id);
|
|
48
52
|
}}
|
|
49
53
|
>
|
|
50
54
|
{action.label}
|
|
@@ -52,6 +56,25 @@ export function Toaster(props: ToasterProps): JSX.Element {
|
|
|
52
56
|
);
|
|
53
57
|
};
|
|
54
58
|
|
|
59
|
+
// One node per toast id, built once in the body: a publish that adds toast B leaves toast A's
|
|
60
|
+
// <li> where it was, so the live region reads only B (`aria-atomic="false"`) and a keyboard user
|
|
61
|
+
// on A's Undo keeps their focus. A plain `.map` rebuilt every visible <li> on every arrival.
|
|
62
|
+
const toastNodes = keyedRows<ToastItem, JSX.Element>(
|
|
63
|
+
rt,
|
|
64
|
+
(item) => item.id,
|
|
65
|
+
(item, id) => (
|
|
66
|
+
<Toast
|
|
67
|
+
title={item().title}
|
|
68
|
+
tone={item().tone}
|
|
69
|
+
dismissLabel={props.dismissLabel}
|
|
70
|
+
action={actionSlot(item(), id)}
|
|
71
|
+
onDismiss={() => props.store.dismiss(id)}
|
|
72
|
+
>
|
|
73
|
+
{item().message}
|
|
74
|
+
</Toast>
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
|
|
55
78
|
return (
|
|
56
79
|
<ToastRegion
|
|
57
80
|
label={props.label}
|
|
@@ -61,17 +84,7 @@ export function Toaster(props: ToasterProps): JSX.Element {
|
|
|
61
84
|
onHold={(reason) => props.store.hold(reason)}
|
|
62
85
|
onRelease={(reason) => props.store.release(reason)}
|
|
63
86
|
>
|
|
64
|
-
{visibleToasts(queue())
|
|
65
|
-
<Toast
|
|
66
|
-
title={item.title}
|
|
67
|
-
tone={item.tone}
|
|
68
|
-
dismissLabel={props.dismissLabel}
|
|
69
|
-
action={actionSlot(item)}
|
|
70
|
-
onDismiss={() => props.store.dismiss(item.id)}
|
|
71
|
-
>
|
|
72
|
-
{item.message}
|
|
73
|
-
</Toast>
|
|
74
|
-
))}
|
|
87
|
+
{thunk(() => toastNodes(visibleToasts(queue())))}
|
|
75
88
|
</ToastRegion>
|
|
76
89
|
);
|
|
77
90
|
}
|