@proyecto-viviana/solidaria-components 0.2.9 → 0.3.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/README.md +39 -272
- package/dist/ActionBar.d.ts +21 -13
- package/dist/ActionBar.d.ts.map +1 -1
- package/dist/ActionGroup.d.ts +8 -8
- package/dist/ActionGroup.d.ts.map +1 -1
- package/dist/Alert.d.ts +5 -5
- package/dist/Alert.d.ts.map +1 -1
- package/dist/Autocomplete.d.ts +5 -5
- package/dist/Autocomplete.d.ts.map +1 -1
- package/dist/Breadcrumbs.d.ts +18 -7
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Button.d.ts +24 -5
- package/dist/Button.d.ts.map +1 -1
- package/dist/Calendar.d.ts +38 -7
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +32 -7
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/Collection.d.ts +19 -14
- package/dist/Collection.d.ts.map +1 -1
- package/dist/Color.d.ts +103 -14
- package/dist/Color.d.ts.map +1 -1
- package/dist/ColorEditor.d.ts +6 -6
- package/dist/ColorEditor.d.ts.map +1 -1
- package/dist/ComboBox.d.ts +85 -19
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ContextualHelpTrigger.d.ts +2 -2
- package/dist/ContextualHelpTrigger.d.ts.map +1 -1
- package/dist/DateField.d.ts +8 -6
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DatePicker.d.ts +53 -22
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/DateRangePickerContext.d.ts +30 -0
- package/dist/DateRangePickerContext.d.ts.map +1 -0
- package/dist/Dialog.d.ts +5 -5
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Disclosure.d.ts +23 -5
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/DragAndDrop.d.ts +6 -6
- package/dist/DragAndDrop.d.ts.map +1 -1
- package/dist/DragPreview.d.ts +2 -2
- package/dist/DragPreview.d.ts.map +1 -1
- package/dist/DropZone.d.ts +4 -4
- package/dist/DropZone.d.ts.map +1 -1
- package/dist/FieldError.d.ts +9 -5
- package/dist/FieldError.d.ts.map +1 -1
- package/dist/FileTrigger.d.ts +3 -3
- package/dist/FileTrigger.d.ts.map +1 -1
- package/dist/Focusable.d.ts +2 -2
- package/dist/Focusable.d.ts.map +1 -1
- package/dist/Form.d.ts +18 -4
- package/dist/Form.d.ts.map +1 -1
- package/dist/GridList.d.ts +32 -12
- package/dist/GridList.d.ts.map +1 -1
- package/dist/HiddenDateInput.d.ts +26 -0
- package/dist/HiddenDateInput.d.ts.map +1 -0
- package/dist/HiddenTimeInput.d.ts +25 -0
- package/dist/HiddenTimeInput.d.ts.map +1 -0
- package/dist/Icon.d.ts +5 -5
- package/dist/Icon.d.ts.map +1 -1
- package/dist/Keyboard.d.ts +1 -1
- package/dist/Landmark.d.ts +3 -3
- package/dist/Landmark.d.ts.map +1 -1
- package/dist/Link.d.ts +10 -4
- package/dist/Link.d.ts.map +1 -1
- package/dist/ListBox.d.ts +32 -12
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListDropTargetDelegate.d.ts +6 -6
- package/dist/ListDropTargetDelegate.d.ts.map +1 -1
- package/dist/Menu.d.ts +65 -14
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Meter.d.ts +3 -3
- package/dist/Meter.d.ts.map +1 -1
- package/dist/Modal.d.ts +5 -5
- package/dist/Modal.d.ts.map +1 -1
- package/dist/NumberField.d.ts +8 -12
- package/dist/NumberField.d.ts.map +1 -1
- package/dist/Popover.d.ts +28 -5
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Pressable.d.ts +2 -2
- package/dist/Pressable.d.ts.map +1 -1
- package/dist/ProgressBar.d.ts +5 -3
- package/dist/ProgressBar.d.ts.map +1 -1
- package/dist/RadioGroup.d.ts +43 -9
- package/dist/RadioGroup.d.ts.map +1 -1
- package/dist/RangeCalendar.d.ts +34 -7
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RouterProvider.d.ts +2 -2
- package/dist/RouterProvider.d.ts.map +1 -1
- package/dist/SearchField.d.ts +23 -20
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/Select.d.ts +41 -11
- package/dist/Select.d.ts.map +1 -1
- package/dist/SelectionIndicator.d.ts +3 -3
- package/dist/SelectionIndicator.d.ts.map +1 -1
- package/dist/Separator.d.ts +9 -3
- package/dist/Separator.d.ts.map +1 -1
- package/dist/SharedElementTransition.d.ts +6 -4
- package/dist/SharedElementTransition.d.ts.map +1 -1
- package/dist/Slider.d.ts +12 -8
- package/dist/Slider.d.ts.map +1 -1
- package/dist/StepList.d.ts +90 -0
- package/dist/StepList.d.ts.map +1 -0
- package/dist/Switch.d.ts +11 -5
- package/dist/Switch.d.ts.map +1 -1
- package/dist/Table.d.ts +187 -23
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +45 -9
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/TagGroup.d.ts +12 -10
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/Text.d.ts +2 -2
- package/dist/TextField.d.ts +15 -11
- package/dist/TextField.d.ts.map +1 -1
- package/dist/TimeField.d.ts +6 -6
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/Toast.d.ts +29 -14
- package/dist/Toast.d.ts.map +1 -1
- package/dist/ToggleButton.d.ts +11 -5
- package/dist/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButtonGroup.d.ts +7 -7
- package/dist/ToggleButtonGroup.d.ts.map +1 -1
- package/dist/Toolbar.d.ts +7 -3
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Tooltip.d.ts +50 -8
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/Tree.d.ts +66 -17
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Virtualizer.d.ts +12 -12
- package/dist/Virtualizer.d.ts.map +1 -1
- package/dist/VirtualizerLayouts.d.ts +2 -2
- package/dist/VirtualizerLayouts.d.ts.map +1 -1
- package/dist/VisuallyHidden.d.ts +1 -1
- package/dist/VisuallyHidden.d.ts.map +1 -1
- package/dist/contexts.d.ts +5 -1
- package/dist/contexts.d.ts.map +1 -1
- package/dist/index.d.ts +73 -71
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23247 -18564
- package/dist/index.js.map +1 -1
- package/dist/index.jsx +18110 -0
- package/dist/index.jsx.map +1 -0
- package/dist/useDragAndDrop.d.ts +13 -13
- package/dist/useDragAndDrop.d.ts.map +1 -1
- package/dist/utils.d.ts +2 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/virtualizer/Layout.d.ts +1 -1
- package/dist/virtualizer/Layout.d.ts.map +1 -1
- package/package.json +31 -32
- package/src/ActionBar.tsx +75 -72
- package/src/ActionGroup.tsx +53 -61
- package/src/Alert.tsx +17 -42
- package/src/Autocomplete.tsx +39 -44
- package/src/Breadcrumbs.tsx +149 -80
- package/src/Button.tsx +267 -70
- package/src/Calendar.tsx +218 -138
- package/src/Checkbox.tsx +413 -121
- package/src/Collection.tsx +67 -58
- package/src/Color.tsx +803 -380
- package/src/ColorEditor.tsx +131 -149
- package/src/ComboBox.tsx +414 -249
- package/src/ContextualHelpTrigger.tsx +86 -74
- package/src/DateField.tsx +185 -91
- package/src/DatePicker.tsx +524 -213
- package/src/DateRangePickerContext.tsx +44 -0
- package/src/Dialog.tsx +156 -118
- package/src/Disclosure.tsx +127 -80
- package/src/DragAndDrop.tsx +60 -54
- package/src/DragPreview.tsx +13 -11
- package/src/DropZone.tsx +42 -22
- package/src/FieldError.tsx +45 -23
- package/src/FileTrigger.tsx +19 -19
- package/src/Focusable.tsx +21 -24
- package/src/Form.tsx +71 -16
- package/src/GridList.tsx +273 -197
- package/src/HiddenDateInput.tsx +153 -0
- package/src/HiddenTimeInput.tsx +133 -0
- package/src/Icon.tsx +22 -43
- package/src/Keyboard.tsx +3 -3
- package/src/Landmark.tsx +37 -63
- package/src/Link.tsx +125 -75
- package/src/ListBox.tsx +332 -233
- package/src/ListDropTargetDelegate.ts +81 -80
- package/src/Menu.tsx +1023 -274
- package/src/Meter.tsx +38 -56
- package/src/Modal.tsx +243 -175
- package/src/NumberField.tsx +139 -143
- package/src/Popover.tsx +386 -233
- package/src/Pressable.tsx +21 -21
- package/src/ProgressBar.tsx +48 -57
- package/src/RadioGroup.tsx +524 -122
- package/src/RangeCalendar.tsx +157 -90
- package/src/RouterProvider.tsx +30 -47
- package/src/SearchField.tsx +362 -143
- package/src/Select.tsx +656 -233
- package/src/SelectionIndicator.tsx +18 -15
- package/src/Separator.tsx +47 -49
- package/src/SharedElementTransition.tsx +103 -97
- package/src/Slider.tsx +138 -98
- package/src/StepList.tsx +272 -0
- package/src/Switch.tsx +93 -46
- package/src/Table.tsx +1308 -342
- package/src/Tabs.tsx +324 -103
- package/src/TagGroup.tsx +139 -126
- package/src/Text.tsx +3 -3
- package/src/TextField.tsx +389 -79
- package/src/TimeField.tsx +136 -76
- package/src/Toast.tsx +209 -157
- package/src/ToggleButton.tsx +47 -37
- package/src/ToggleButtonGroup.tsx +39 -34
- package/src/Toolbar.tsx +54 -69
- package/src/Tooltip.tsx +387 -119
- package/src/Tree.tsx +651 -368
- package/src/Virtualizer.tsx +208 -180
- package/src/VirtualizerLayouts.ts +45 -30
- package/src/VisuallyHidden.tsx +19 -19
- package/src/contexts.ts +29 -37
- package/src/index.ts +110 -195
- package/src/useDragAndDrop.ts +87 -71
- package/src/utils.tsx +40 -55
- package/src/virtualizer/Layout.ts +14 -22
- package/dist/index.ssr.js +0 -16996
- package/dist/index.ssr.js.map +0 -1
package/src/Toast.tsx
CHANGED
|
@@ -7,46 +7,60 @@
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
type JSX,
|
|
10
|
+
type Accessor,
|
|
10
11
|
createContext,
|
|
11
12
|
createMemo,
|
|
12
13
|
createEffect,
|
|
14
|
+
createSignal,
|
|
13
15
|
onCleanup,
|
|
14
16
|
splitProps,
|
|
15
17
|
Show,
|
|
16
18
|
useContext,
|
|
17
|
-
} from
|
|
18
|
-
import { Portal
|
|
19
|
+
} from "solid-js";
|
|
20
|
+
import { Portal } from "solid-js/web";
|
|
19
21
|
import {
|
|
20
22
|
type ToastState,
|
|
21
23
|
type QueuedToast,
|
|
22
24
|
type ToastQueueOptions,
|
|
25
|
+
type ToastOptions,
|
|
23
26
|
ToastQueue,
|
|
24
27
|
createToastState,
|
|
25
|
-
} from
|
|
28
|
+
} from "@proyecto-viviana/solid-stately";
|
|
26
29
|
import {
|
|
27
30
|
createToast,
|
|
28
31
|
createToastRegion,
|
|
29
32
|
useUNSAFE_PortalContext,
|
|
30
|
-
} from
|
|
33
|
+
} from "@proyecto-viviana/solidaria";
|
|
31
34
|
import {
|
|
32
35
|
type RenderChildren,
|
|
33
36
|
type ClassNameOrFunction,
|
|
34
37
|
type StyleOrFunction,
|
|
35
38
|
useRenderProps,
|
|
36
39
|
filterDOMProps,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// ============================================
|
|
40
|
-
// TYPES
|
|
41
|
-
// ============================================
|
|
40
|
+
useIsHydrated,
|
|
41
|
+
} from "./utils";
|
|
42
42
|
|
|
43
43
|
export interface ToastContent {
|
|
44
|
+
/** DOM id for the toast root when content is queued through Spectrum ToastQueue. */
|
|
45
|
+
id?: string;
|
|
46
|
+
/** Data attributes for the toast root when content is queued through Spectrum ToastQueue. */
|
|
47
|
+
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
48
|
+
/** Spectrum Toast message content. */
|
|
49
|
+
children?: string;
|
|
44
50
|
/** The title of the toast. */
|
|
45
51
|
title?: string;
|
|
46
52
|
/** The description/body of the toast. */
|
|
47
53
|
description?: string;
|
|
48
|
-
/**
|
|
49
|
-
|
|
54
|
+
/** Spectrum variant. */
|
|
55
|
+
variant?: "info" | "positive" | "negative" | "neutral";
|
|
56
|
+
/** Backward-compatible type/variant of the toast. */
|
|
57
|
+
type?: "info" | "success" | "warning" | "error" | "positive" | "negative" | "neutral";
|
|
58
|
+
/** Spectrum action button label. */
|
|
59
|
+
actionLabel?: string;
|
|
60
|
+
/** Handler called when the Spectrum action button is pressed. */
|
|
61
|
+
onAction?: () => void;
|
|
62
|
+
/** Whether the toast should close when the Spectrum action is performed. */
|
|
63
|
+
shouldCloseOnAction?: boolean;
|
|
50
64
|
/** Custom action button. */
|
|
51
65
|
action?: {
|
|
52
66
|
label: string;
|
|
@@ -60,14 +74,14 @@ export interface ToastRenderProps {
|
|
|
60
74
|
/** Whether the toast is currently animating out. */
|
|
61
75
|
isExiting: boolean;
|
|
62
76
|
/** The animation state (entering, exiting, queued). */
|
|
63
|
-
animation:
|
|
77
|
+
animation: "entering" | "exiting" | "queued" | undefined;
|
|
64
78
|
/** The toast data. */
|
|
65
79
|
toast: QueuedToast<ToastContent>;
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
export interface ToastRegionRenderProps {
|
|
69
83
|
/** The visible toasts. */
|
|
70
|
-
visibleToasts: QueuedToast<ToastContent>[]
|
|
84
|
+
visibleToasts: Accessor<QueuedToast<ToastContent>[]>;
|
|
71
85
|
}
|
|
72
86
|
|
|
73
87
|
export interface ToastRegionProps {
|
|
@@ -80,14 +94,28 @@ export interface ToastRegionProps {
|
|
|
80
94
|
/** The toast state to display. If not provided, uses ToastContext. */
|
|
81
95
|
state?: ToastState<ToastContent>;
|
|
82
96
|
/** Accessible label for the region. */
|
|
83
|
-
|
|
97
|
+
"aria-label"?: string;
|
|
84
98
|
/** Whether to render in a portal. */
|
|
85
99
|
portal?: boolean;
|
|
86
100
|
/** Placement of the toast region. */
|
|
87
|
-
placement?:
|
|
101
|
+
placement?:
|
|
102
|
+
| "top"
|
|
103
|
+
| "top start"
|
|
104
|
+
| "top end"
|
|
105
|
+
| "top-start"
|
|
106
|
+
| "top-end"
|
|
107
|
+
| "bottom"
|
|
108
|
+
| "bottom start"
|
|
109
|
+
| "bottom end"
|
|
110
|
+
| "bottom-start"
|
|
111
|
+
| "bottom-end";
|
|
88
112
|
}
|
|
89
113
|
|
|
90
114
|
export interface ToastProps {
|
|
115
|
+
/** DOM id for the toast root. */
|
|
116
|
+
id?: string;
|
|
117
|
+
/** Data attributes for the toast root. */
|
|
118
|
+
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
91
119
|
/** The toast data. */
|
|
92
120
|
toast: QueuedToast<ToastContent>;
|
|
93
121
|
/** The children of the component - can be JSX or render function. */
|
|
@@ -98,10 +126,6 @@ export interface ToastProps {
|
|
|
98
126
|
style?: StyleOrFunction<ToastRenderProps>;
|
|
99
127
|
}
|
|
100
128
|
|
|
101
|
-
// ============================================
|
|
102
|
-
// CONTEXT
|
|
103
|
-
// ============================================
|
|
104
|
-
|
|
105
129
|
export const ToastContext = createContext<ToastState<ToastContent> | null>(null);
|
|
106
130
|
|
|
107
131
|
interface ToastAriaContextValue {
|
|
@@ -110,22 +134,19 @@ interface ToastAriaContextValue {
|
|
|
110
134
|
}
|
|
111
135
|
|
|
112
136
|
const ToastAriaContext = createContext<ToastAriaContextValue | null>(null);
|
|
137
|
+
const toastStateByKey = new Map<string, ToastState<ToastContent>>();
|
|
113
138
|
|
|
114
139
|
export function useToastContext(): ToastState<ToastContent> {
|
|
115
140
|
const context = useContext(ToastContext);
|
|
116
141
|
if (!context) {
|
|
117
|
-
throw new Error(
|
|
142
|
+
throw new Error("Toast components must be used within a ToastProvider");
|
|
118
143
|
}
|
|
119
144
|
return context;
|
|
120
145
|
}
|
|
121
146
|
|
|
122
|
-
// ============================================
|
|
123
|
-
// GLOBAL TOAST QUEUE
|
|
124
|
-
// ============================================
|
|
125
|
-
|
|
126
147
|
/** Default global toast queue that can be used for app-wide toasts. */
|
|
127
148
|
export const globalToastQueue = new ToastQueue<ToastContent>({
|
|
128
|
-
maxVisibleToasts:
|
|
149
|
+
maxVisibleToasts: Infinity,
|
|
129
150
|
hasExitAnimation: true,
|
|
130
151
|
});
|
|
131
152
|
|
|
@@ -133,16 +154,13 @@ export const globalToastQueue = new ToastQueue<ToastContent>({
|
|
|
133
154
|
* Add a toast to the global queue.
|
|
134
155
|
* Convenience function for adding toasts from anywhere in the app.
|
|
135
156
|
*/
|
|
136
|
-
export function addToast(
|
|
137
|
-
content: ToastContent,
|
|
138
|
-
options?: { timeout?: number; priority?: number }
|
|
139
|
-
): string {
|
|
157
|
+
export function addToast(content: ToastContent, options?: ToastOptions): string {
|
|
140
158
|
return globalToastQueue.add(content, options);
|
|
141
159
|
}
|
|
142
160
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
161
|
+
function normalizeToastPlacement(placement?: ToastRegionProps["placement"]) {
|
|
162
|
+
return (placement ?? "bottom").replace("-", " ") as NonNullable<ToastRegionProps["placement"]>;
|
|
163
|
+
}
|
|
146
164
|
|
|
147
165
|
export interface ToastProviderProps {
|
|
148
166
|
/** The children of the provider. */
|
|
@@ -172,17 +190,9 @@ export function ToastProvider(props: ToastProviderProps): JSX.Element {
|
|
|
172
190
|
|
|
173
191
|
const state = createToastState({ queue });
|
|
174
192
|
|
|
175
|
-
return
|
|
176
|
-
<ToastContext.Provider value={state}>
|
|
177
|
-
{props.children}
|
|
178
|
-
</ToastContext.Provider>
|
|
179
|
-
);
|
|
193
|
+
return <ToastContext.Provider value={state}>{props.children}</ToastContext.Provider>;
|
|
180
194
|
}
|
|
181
195
|
|
|
182
|
-
// ============================================
|
|
183
|
-
// TOAST REGION COMPONENT
|
|
184
|
-
// ============================================
|
|
185
|
-
|
|
186
196
|
/**
|
|
187
197
|
* ToastRegion is a container that displays all visible toasts.
|
|
188
198
|
* It handles pause on hover/focus and provides the landmark region.
|
|
@@ -199,91 +209,111 @@ export function ToastProvider(props: ToastProviderProps): JSX.Element {
|
|
|
199
209
|
* ```
|
|
200
210
|
*/
|
|
201
211
|
export function ToastRegion(props: ToastRegionProps): JSX.Element {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
212
|
+
// Do NOT early-return on the server: returning null on the server and a <Show>
|
|
213
|
+
// on the client desyncs hydration when the region is in the SSR tree. Render the
|
|
214
|
+
// same structure on both and gate the Portal on useIsHydrated() (see Popover).
|
|
215
|
+
const isHydrated = useIsHydrated();
|
|
205
216
|
|
|
206
217
|
const [local, rest] = splitProps(props, [
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
218
|
+
"children",
|
|
219
|
+
"class",
|
|
220
|
+
"style",
|
|
221
|
+
"state",
|
|
222
|
+
"aria-label",
|
|
223
|
+
"portal",
|
|
224
|
+
"placement",
|
|
214
225
|
]);
|
|
215
226
|
const portalContext = useUNSAFE_PortalContext();
|
|
216
227
|
const portalContainer = () => portalContext.getContainer?.() ?? undefined;
|
|
228
|
+
const [regionElement, setRegionElement] = createSignal<HTMLElement>();
|
|
217
229
|
|
|
218
|
-
// Get state from context if not provided
|
|
219
230
|
const contextState = useContext(ToastContext);
|
|
220
231
|
const state = () => local.state ?? contextState;
|
|
221
232
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
233
|
+
const regionAria = createToastRegion<ToastContent>({
|
|
234
|
+
state: {
|
|
235
|
+
visibleToasts: () => state()?.visibleToasts() ?? [],
|
|
236
|
+
add: (content, options) => state()?.add(content, options) ?? "",
|
|
237
|
+
close: (key) => state()?.close(key),
|
|
238
|
+
remove: (key) => state()?.remove(key),
|
|
239
|
+
clear: () => state()?.clear(),
|
|
240
|
+
pauseAll: () => state()?.pauseAll(),
|
|
241
|
+
resumeAll: () => state()?.resumeAll(),
|
|
242
|
+
},
|
|
243
|
+
ref: regionElement,
|
|
244
|
+
get "aria-label"() {
|
|
245
|
+
return local["aria-label"];
|
|
246
|
+
},
|
|
247
|
+
});
|
|
231
248
|
|
|
232
|
-
// Render props values
|
|
233
249
|
const renderValues = createMemo<ToastRegionRenderProps>(() => ({
|
|
234
|
-
visibleToasts: state()?.visibleToasts() ?? [],
|
|
250
|
+
visibleToasts: () => state()?.visibleToasts() ?? [],
|
|
235
251
|
}));
|
|
236
252
|
|
|
237
|
-
// Resolve render props
|
|
238
253
|
const renderProps = useRenderProps(
|
|
239
254
|
{
|
|
240
255
|
children: props.children,
|
|
241
256
|
class: local.class,
|
|
242
257
|
style: local.style,
|
|
243
|
-
defaultClassName:
|
|
258
|
+
defaultClassName: "solidaria-ToastRegion",
|
|
244
259
|
},
|
|
245
|
-
renderValues
|
|
260
|
+
renderValues,
|
|
246
261
|
);
|
|
262
|
+
const renderedChildren = createMemo(() => renderProps.renderChildren());
|
|
247
263
|
|
|
248
|
-
|
|
249
|
-
|
|
264
|
+
const domProps = createMemo(() =>
|
|
265
|
+
filterDOMProps(rest as Record<string, unknown>, { global: true }),
|
|
266
|
+
);
|
|
250
267
|
|
|
251
|
-
// Placement styles
|
|
252
268
|
const placementStyles = createMemo<JSX.CSSProperties>(() => {
|
|
253
|
-
const placement = local.placement
|
|
269
|
+
const placement = normalizeToastPlacement(local.placement);
|
|
270
|
+
const [edge, align = "center"] = placement.split(" ");
|
|
254
271
|
const base: JSX.CSSProperties = {
|
|
255
|
-
position:
|
|
256
|
-
|
|
257
|
-
display:
|
|
258
|
-
|
|
259
|
-
gap:
|
|
260
|
-
padding:
|
|
261
|
-
|
|
272
|
+
position: "fixed",
|
|
273
|
+
"z-index": 100001,
|
|
274
|
+
display: "flex",
|
|
275
|
+
"flex-direction": edge === "top" ? "column" : "column-reverse",
|
|
276
|
+
gap: "8px",
|
|
277
|
+
padding: "16px",
|
|
278
|
+
"pointer-events": "none",
|
|
262
279
|
};
|
|
263
280
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
return { ...base, top:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
281
|
+
if (edge === "top") {
|
|
282
|
+
if (align === "end") {
|
|
283
|
+
return { ...base, top: "16px", right: "16px" } as JSX.CSSProperties;
|
|
284
|
+
}
|
|
285
|
+
if (align === "start") {
|
|
286
|
+
return { ...base, top: "16px", left: "16px" } as JSX.CSSProperties;
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
...base,
|
|
290
|
+
top: "16px",
|
|
291
|
+
left: "50%",
|
|
292
|
+
transform: "translateX(-50%)",
|
|
293
|
+
} as JSX.CSSProperties;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (align === "end") {
|
|
297
|
+
return { ...base, bottom: "16px", right: "16px" } as JSX.CSSProperties;
|
|
298
|
+
}
|
|
299
|
+
if (align === "start") {
|
|
300
|
+
return { ...base, bottom: "16px", left: "16px" } as JSX.CSSProperties;
|
|
278
301
|
}
|
|
302
|
+
return {
|
|
303
|
+
...base,
|
|
304
|
+
bottom: "16px",
|
|
305
|
+
left: "50%",
|
|
306
|
+
transform: "translateX(-50%)",
|
|
307
|
+
} as JSX.CSSProperties;
|
|
279
308
|
});
|
|
280
309
|
|
|
310
|
+
const normalizedPlacement = () => normalizeToastPlacement(local.placement);
|
|
311
|
+
|
|
281
312
|
const visibleToasts = () => state()?.visibleToasts() ?? [];
|
|
282
313
|
const hasToasts = () => visibleToasts().length > 0;
|
|
283
314
|
|
|
284
315
|
const regionContent = () => {
|
|
285
|
-
|
|
286
|
-
if (!regionAria || !state()) return null;
|
|
316
|
+
if (!state()) return null;
|
|
287
317
|
|
|
288
318
|
// Merge styles - placement styles are base, renderProps.style() overrides
|
|
289
319
|
const mergedStyle = () => {
|
|
@@ -293,25 +323,24 @@ export function ToastRegion(props: ToastRegionProps): JSX.Element {
|
|
|
293
323
|
return { ...placement, ...custom } as JSX.CSSProperties;
|
|
294
324
|
};
|
|
295
325
|
|
|
296
|
-
// Extract ref from regionProps to avoid type conflicts
|
|
297
326
|
const { ref: _ref, ...cleanRegionProps } = regionAria.regionProps as Record<string, unknown>;
|
|
298
327
|
|
|
299
328
|
return (
|
|
300
329
|
<div
|
|
330
|
+
ref={setRegionElement}
|
|
301
331
|
{...domProps()}
|
|
302
332
|
{...cleanRegionProps}
|
|
303
333
|
class={renderProps.class()}
|
|
304
334
|
style={mergedStyle()}
|
|
305
|
-
data-placement={
|
|
335
|
+
data-placement={normalizedPlacement()}
|
|
306
336
|
>
|
|
307
|
-
{
|
|
337
|
+
{renderedChildren()}
|
|
308
338
|
</div>
|
|
309
339
|
);
|
|
310
340
|
};
|
|
311
341
|
|
|
312
|
-
// Only render when there are toasts
|
|
313
342
|
return (
|
|
314
|
-
<Show when={hasToasts()}>
|
|
343
|
+
<Show when={isHydrated() && hasToasts()}>
|
|
315
344
|
<Show when={local.portal !== false} fallback={regionContent()}>
|
|
316
345
|
<Portal mount={portalContainer()}>{regionContent()}</Portal>
|
|
317
346
|
</Show>
|
|
@@ -319,10 +348,6 @@ export function ToastRegion(props: ToastRegionProps): JSX.Element {
|
|
|
319
348
|
);
|
|
320
349
|
}
|
|
321
350
|
|
|
322
|
-
// ============================================
|
|
323
|
-
// TOAST COMPONENT
|
|
324
|
-
// ============================================
|
|
325
|
-
|
|
326
351
|
/**
|
|
327
352
|
* Toast is an individual notification component.
|
|
328
353
|
*
|
|
@@ -339,53 +364,64 @@ export function ToastRegion(props: ToastRegionProps): JSX.Element {
|
|
|
339
364
|
* ```
|
|
340
365
|
*/
|
|
341
366
|
export function Toast(props: ToastProps): JSX.Element {
|
|
342
|
-
const [local, rest] = splitProps(props, [
|
|
343
|
-
'toast',
|
|
344
|
-
'children',
|
|
345
|
-
'class',
|
|
346
|
-
'style',
|
|
347
|
-
]);
|
|
367
|
+
const [local, rest] = splitProps(props, ["toast", "children", "class", "style"]);
|
|
348
368
|
|
|
349
369
|
let toastRef!: HTMLDivElement;
|
|
350
370
|
|
|
351
|
-
// Get state from context
|
|
352
371
|
const state = useToastContext();
|
|
353
372
|
|
|
354
|
-
|
|
373
|
+
createEffect(() => {
|
|
374
|
+
const key = local.toast.key;
|
|
375
|
+
toastStateByKey.set(key, state);
|
|
376
|
+
onCleanup(() => {
|
|
377
|
+
if (toastStateByKey.get(key) === state) {
|
|
378
|
+
toastStateByKey.delete(key);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const hasTitle = () => !!(local.toast.content.children ?? local.toast.content.title);
|
|
355
384
|
const toastAria = createToast({
|
|
356
385
|
toast: local.toast,
|
|
357
386
|
state,
|
|
358
|
-
hasTitle:
|
|
387
|
+
hasTitle: hasTitle(),
|
|
359
388
|
hasDescription: !!local.toast.content.description,
|
|
360
389
|
});
|
|
361
390
|
|
|
362
|
-
// Render props values
|
|
363
391
|
const renderValues = createMemo<ToastRenderProps>(() => ({
|
|
364
|
-
isEntering: local.toast.animation ===
|
|
365
|
-
isExiting: local.toast.animation ===
|
|
392
|
+
isEntering: local.toast.animation === "entering",
|
|
393
|
+
isExiting: local.toast.animation === "exiting",
|
|
366
394
|
animation: local.toast.animation,
|
|
367
395
|
toast: local.toast,
|
|
368
396
|
}));
|
|
369
397
|
|
|
370
|
-
// Resolve render props
|
|
371
398
|
const renderProps = useRenderProps(
|
|
372
399
|
{
|
|
373
400
|
children: props.children,
|
|
374
401
|
class: local.class,
|
|
375
402
|
style: local.style,
|
|
376
|
-
defaultClassName:
|
|
403
|
+
defaultClassName: "solidaria-Toast",
|
|
377
404
|
},
|
|
378
|
-
renderValues
|
|
405
|
+
renderValues,
|
|
379
406
|
);
|
|
380
407
|
|
|
381
|
-
|
|
382
|
-
|
|
408
|
+
const domProps = createMemo(() =>
|
|
409
|
+
filterDOMProps(rest as Record<string, unknown>, { global: true }),
|
|
410
|
+
);
|
|
383
411
|
|
|
384
|
-
// Merge styles
|
|
385
412
|
const mergedStyle = () => {
|
|
386
413
|
const custom = renderProps.style();
|
|
387
|
-
if (!custom) return {
|
|
388
|
-
return {
|
|
414
|
+
if (!custom) return { "pointer-events": "auto" as const };
|
|
415
|
+
return { "pointer-events": "auto" as const, ...custom } as JSX.CSSProperties;
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const handleRootClick = (event: MouseEvent) => {
|
|
419
|
+
const target = event.target;
|
|
420
|
+
if (!(target instanceof Element)) return;
|
|
421
|
+
if (target.closest("[data-solidaria-toast-close-button]")) {
|
|
422
|
+
state.close(local.toast.key);
|
|
423
|
+
state.remove(local.toast.key);
|
|
424
|
+
}
|
|
389
425
|
};
|
|
390
426
|
|
|
391
427
|
// Exit animation lifecycle:
|
|
@@ -395,14 +431,14 @@ export function Toast(props: ToastProps): JSX.Element {
|
|
|
395
431
|
// Reduced-motion is handled by CSS (shorter/no animations), so the lifecycle
|
|
396
432
|
// naturally completes faster when the user prefers reduced motion.
|
|
397
433
|
createEffect(() => {
|
|
398
|
-
if (local.toast.animation !==
|
|
434
|
+
if (local.toast.animation !== "exiting") return;
|
|
399
435
|
if (!toastRef) {
|
|
400
436
|
state.remove(local.toast.key);
|
|
401
437
|
return;
|
|
402
438
|
}
|
|
403
439
|
|
|
404
440
|
// Check if the element supports the Web Animations API
|
|
405
|
-
if (!(
|
|
441
|
+
if (!("getAnimations" in toastRef)) {
|
|
406
442
|
state.remove(local.toast.key);
|
|
407
443
|
return;
|
|
408
444
|
}
|
|
@@ -434,7 +470,6 @@ export function Toast(props: ToastProps): JSX.Element {
|
|
|
434
470
|
});
|
|
435
471
|
});
|
|
436
472
|
|
|
437
|
-
// Extract ref from toastProps to avoid type conflicts
|
|
438
473
|
const { ref: _ref, ...cleanToastProps } = toastAria.toastProps as Record<string, unknown>;
|
|
439
474
|
|
|
440
475
|
// Ensure ARIA title/description IDs are present on rendered sub-components,
|
|
@@ -443,17 +478,19 @@ export function Toast(props: ToastProps): JSX.Element {
|
|
|
443
478
|
if (!toastRef) return;
|
|
444
479
|
|
|
445
480
|
const titleId = (toastAria.titleProps as Record<string, unknown>).id as string | undefined;
|
|
446
|
-
const descriptionId = (toastAria.descriptionProps as Record<string, unknown>).id as
|
|
481
|
+
const descriptionId = (toastAria.descriptionProps as Record<string, unknown>).id as
|
|
482
|
+
| string
|
|
483
|
+
| undefined;
|
|
447
484
|
|
|
448
485
|
if (titleId) {
|
|
449
|
-
const titleEl = toastRef.querySelector(
|
|
486
|
+
const titleEl = toastRef.querySelector("[data-solidaria-toast-title]");
|
|
450
487
|
if (titleEl instanceof HTMLElement) {
|
|
451
488
|
titleEl.id = titleId;
|
|
452
489
|
}
|
|
453
490
|
}
|
|
454
491
|
|
|
455
492
|
if (descriptionId) {
|
|
456
|
-
const descriptionEl = toastRef.querySelector(
|
|
493
|
+
const descriptionEl = toastRef.querySelector("[data-solidaria-toast-description]");
|
|
457
494
|
if (descriptionEl instanceof HTMLElement) {
|
|
458
495
|
descriptionEl.id = descriptionId;
|
|
459
496
|
}
|
|
@@ -461,7 +498,9 @@ export function Toast(props: ToastProps): JSX.Element {
|
|
|
461
498
|
});
|
|
462
499
|
|
|
463
500
|
return (
|
|
464
|
-
<ToastAriaContext.Provider
|
|
501
|
+
<ToastAriaContext.Provider
|
|
502
|
+
value={{ titleProps: toastAria.titleProps, descriptionProps: toastAria.descriptionProps }}
|
|
503
|
+
>
|
|
465
504
|
<div
|
|
466
505
|
ref={toastRef}
|
|
467
506
|
{...domProps()}
|
|
@@ -469,7 +508,9 @@ export function Toast(props: ToastProps): JSX.Element {
|
|
|
469
508
|
class={renderProps.class()}
|
|
470
509
|
style={mergedStyle()}
|
|
471
510
|
data-animation={local.toast.animation}
|
|
472
|
-
data-type={local.toast.content.type}
|
|
511
|
+
data-type={local.toast.content.type ?? local.toast.content.variant}
|
|
512
|
+
data-variant={local.toast.content.variant}
|
|
513
|
+
on:click={handleRootClick}
|
|
473
514
|
>
|
|
474
515
|
{renderProps.renderChildren()}
|
|
475
516
|
</div>
|
|
@@ -477,10 +518,6 @@ export function Toast(props: ToastProps): JSX.Element {
|
|
|
477
518
|
);
|
|
478
519
|
}
|
|
479
520
|
|
|
480
|
-
// ============================================
|
|
481
|
-
// TOAST SUB-COMPONENTS
|
|
482
|
-
// ============================================
|
|
483
|
-
|
|
484
521
|
export interface ToastTitleProps {
|
|
485
522
|
children: JSX.Element;
|
|
486
523
|
class?: string;
|
|
@@ -512,10 +549,18 @@ export interface ToastDescriptionProps {
|
|
|
512
549
|
*/
|
|
513
550
|
export function ToastDescription(props: ToastDescriptionProps): JSX.Element {
|
|
514
551
|
const context = useContext(ToastAriaContext);
|
|
515
|
-
const { ref: _ref, ...ariaDescriptionProps } = (context?.descriptionProps ?? {}) as Record<
|
|
552
|
+
const { ref: _ref, ...ariaDescriptionProps } = (context?.descriptionProps ?? {}) as Record<
|
|
553
|
+
string,
|
|
554
|
+
unknown
|
|
555
|
+
>;
|
|
516
556
|
|
|
517
557
|
return (
|
|
518
|
-
<div
|
|
558
|
+
<div
|
|
559
|
+
data-solidaria-toast-description=""
|
|
560
|
+
{...ariaDescriptionProps}
|
|
561
|
+
class={props.class}
|
|
562
|
+
style={props.style}
|
|
563
|
+
>
|
|
519
564
|
{props.children}
|
|
520
565
|
</div>
|
|
521
566
|
);
|
|
@@ -527,17 +572,19 @@ export interface ToastCloseButtonProps {
|
|
|
527
572
|
children?: JSX.Element;
|
|
528
573
|
class?: string;
|
|
529
574
|
style?: JSX.CSSProperties;
|
|
530
|
-
|
|
575
|
+
"aria-label"?: string;
|
|
531
576
|
}
|
|
532
577
|
|
|
533
578
|
/**
|
|
534
579
|
* ToastCloseButton is a button that closes the toast.
|
|
535
580
|
*/
|
|
536
581
|
export function ToastCloseButton(props: ToastCloseButtonProps): JSX.Element {
|
|
537
|
-
const
|
|
538
|
-
|
|
582
|
+
const contextState = useContext(ToastContext);
|
|
539
583
|
const handleClose = () => {
|
|
540
|
-
|
|
584
|
+
const key = props.toast.key;
|
|
585
|
+
const state = contextState ?? toastStateByKey.get(key);
|
|
586
|
+
state?.close(key);
|
|
587
|
+
state?.remove(key);
|
|
541
588
|
};
|
|
542
589
|
|
|
543
590
|
return (
|
|
@@ -545,18 +592,16 @@ export function ToastCloseButton(props: ToastCloseButtonProps): JSX.Element {
|
|
|
545
592
|
type="button"
|
|
546
593
|
class={props.class}
|
|
547
594
|
style={props.style}
|
|
548
|
-
aria-label={props[
|
|
595
|
+
aria-label={props["aria-label"] ?? "Close"}
|
|
596
|
+
data-solidaria-toast-close-button=""
|
|
597
|
+
on:click={handleClose}
|
|
549
598
|
onClick={handleClose}
|
|
550
599
|
>
|
|
551
|
-
{props.children ??
|
|
600
|
+
{props.children ?? "×"}
|
|
552
601
|
</button>
|
|
553
602
|
);
|
|
554
603
|
}
|
|
555
604
|
|
|
556
|
-
// ============================================
|
|
557
|
-
// DEFAULT TOAST RENDERING
|
|
558
|
-
// ============================================
|
|
559
|
-
|
|
560
605
|
export interface DefaultToastProps {
|
|
561
606
|
toast: QueuedToast<ToastContent>;
|
|
562
607
|
}
|
|
@@ -567,26 +612,33 @@ export interface DefaultToastProps {
|
|
|
567
612
|
*/
|
|
568
613
|
export function DefaultToast(props: DefaultToastProps): JSX.Element {
|
|
569
614
|
const content = () => props.toast.content;
|
|
615
|
+
const title = () => content().children ?? content().title;
|
|
616
|
+
const actionLabel = () => content().actionLabel ?? content().action?.label;
|
|
617
|
+
const actionHandler = () => content().onAction ?? content().action?.onAction;
|
|
618
|
+
const state = useToastContext();
|
|
619
|
+
const handleAction = () => {
|
|
620
|
+
actionHandler()?.();
|
|
621
|
+
if (content().shouldCloseOnAction) {
|
|
622
|
+
state.close(props.toast.key);
|
|
623
|
+
state.remove(props.toast.key);
|
|
624
|
+
}
|
|
625
|
+
};
|
|
570
626
|
|
|
571
627
|
return (
|
|
572
628
|
<Toast toast={props.toast}>
|
|
573
|
-
<div style={{ display:
|
|
629
|
+
<div style={{ display: "flex", "align-items": "flex-start", gap: "12px" }}>
|
|
574
630
|
<div style={{ flex: 1 }}>
|
|
575
|
-
<Show when={
|
|
576
|
-
<ToastTitle style={{
|
|
577
|
-
{
|
|
631
|
+
<Show when={title()}>
|
|
632
|
+
<ToastTitle style={{ "font-weight": "bold", "margin-bottom": "4px" }}>
|
|
633
|
+
{title()}
|
|
578
634
|
</ToastTitle>
|
|
579
635
|
</Show>
|
|
580
636
|
<Show when={content().description}>
|
|
581
637
|
<ToastDescription>{content().description}</ToastDescription>
|
|
582
638
|
</Show>
|
|
583
|
-
<Show when={
|
|
584
|
-
<button
|
|
585
|
-
|
|
586
|
-
style={{ 'margin-top': '8px' }}
|
|
587
|
-
onClick={content().action?.onAction}
|
|
588
|
-
>
|
|
589
|
-
{content().action?.label}
|
|
639
|
+
<Show when={actionLabel()}>
|
|
640
|
+
<button type="button" style={{ "margin-top": "8px" }} onClick={handleAction}>
|
|
641
|
+
{actionLabel()}
|
|
590
642
|
</button>
|
|
591
643
|
</Show>
|
|
592
644
|
</div>
|