@zvk/ui 0.1.8 → 0.1.9
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/CHANGELOG.md +7 -0
- package/README.md +1 -1
- package/dist/components/alert-dialog/alert-dialog.d.ts +1 -1
- package/dist/components/alert-dialog/alert-dialog.js +18 -10
- package/dist/components/combobox/combobox.js +3 -1
- package/dist/components/command/command-filter.js +1 -1
- package/dist/components/command/command.d.ts +5 -0
- package/dist/components/command/command.js +9 -2
- package/dist/components/command/index.d.ts +1 -1
- package/dist/components/context-menu/context-menu.js +1 -1
- package/dist/components/date-range-picker/date-range-picker.d.ts +27 -0
- package/dist/components/date-range-picker/date-range-picker.js +193 -0
- package/dist/components/date-range-picker/index.d.ts +2 -0
- package/dist/components/date-range-picker/index.js +2 -0
- package/dist/components/dialog/dialog.d.ts +1 -1
- package/dist/components/dialog/dialog.js +19 -14
- package/dist/components/dropdown-menu/dropdown-menu.d.ts +2 -2
- package/dist/components/dropdown-menu/dropdown-menu.js +24 -19
- package/dist/components/file-dropzone/file-dropzone.d.ts +25 -0
- package/dist/components/file-dropzone/file-dropzone.js +171 -0
- package/dist/components/file-dropzone/index.d.ts +2 -0
- package/dist/components/file-dropzone/index.js +2 -0
- package/dist/components/form/form.d.ts +16 -1
- package/dist/components/form/form.js +13 -2
- package/dist/components/form/index.d.ts +1 -1
- package/dist/components/hover-card/hover-card.d.ts +1 -1
- package/dist/components/hover-card/hover-card.js +12 -3
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.js +3 -0
- package/dist/components/kbd/index.d.ts +2 -0
- package/dist/components/kbd/index.js +1 -0
- package/dist/components/kbd/kbd.d.ts +15 -0
- package/dist/components/kbd/kbd.js +10 -0
- package/dist/components/menubar/menubar.js +1 -1
- package/dist/components/popover/popover.d.ts +1 -1
- package/dist/components/popover/popover.js +14 -24
- package/dist/components/select/select.d.ts +1 -1
- package/dist/components/select/select.js +88 -8
- package/dist/components/sheet/sheet.d.ts +1 -1
- package/dist/components/sheet/sheet.js +17 -12
- package/dist/components/table/index.d.ts +1 -1
- package/dist/components/table/table.d.ts +37 -0
- package/dist/components/table/table.js +30 -2
- package/dist/components/toast/index.d.ts +1 -1
- package/dist/components/toast/toast.d.ts +18 -0
- package/dist/components/toast/toast.js +60 -0
- package/dist/components/tooltip/tooltip.d.ts +1 -1
- package/dist/components/tooltip/tooltip.js +12 -3
- package/dist/internal/floating/transform-origin.d.ts +2 -0
- package/dist/internal/floating/transform-origin.js +22 -0
- package/dist/internal/presence/index.d.ts +2 -0
- package/dist/internal/presence/index.js +2 -0
- package/dist/internal/presence/use-presence.d.ts +18 -0
- package/dist/internal/presence/use-presence.js +119 -0
- package/dist/styles.css +1710 -224
- package/dist/tokens/token-types.d.ts +4 -0
- package/dist/tokens/tokens.d.ts +41 -5
- package/dist/tokens/tokens.js +45 -9
- package/package.json +135 -61
|
@@ -7,7 +7,9 @@ import { cn } from "../../utils/cn.js";
|
|
|
7
7
|
import { Portal } from "../../internal/portal/index.js";
|
|
8
8
|
import { DismissableLayer } from "../../internal/dismissable-layer/index.js";
|
|
9
9
|
import { useFloatingPosition } from "../../internal/floating/index.js";
|
|
10
|
+
import { getFloatingTransformOrigin } from "../../internal/floating/transform-origin.js";
|
|
10
11
|
import { placementFromSideAlign } from "../../internal/floating/placement-aliases.js";
|
|
12
|
+
import { usePresence } from "../../internal/presence/index.js";
|
|
11
13
|
import { Slot } from "../../internal/slot/index.js";
|
|
12
14
|
const defaultPositioning = {
|
|
13
15
|
sideOffset: 0,
|
|
@@ -15,6 +17,7 @@ const defaultPositioning = {
|
|
|
15
17
|
collisionPadding: 0,
|
|
16
18
|
matchTriggerWidth: false
|
|
17
19
|
};
|
|
20
|
+
const popoverExitDurationMs = 120;
|
|
18
21
|
const PopoverContext = React.createContext(null);
|
|
19
22
|
function usePopoverContext(calledBy) {
|
|
20
23
|
const context = React.useContext(PopoverContext);
|
|
@@ -32,26 +35,6 @@ function mergeRef(ref, node) {
|
|
|
32
35
|
ref.current = node;
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
|
-
function getTransformOrigin(placement) {
|
|
36
|
-
const [side, align = "center"] = placement.split("-");
|
|
37
|
-
const anchor = side === "top" ? "bottom" : side === "bottom" ? "top" : side === "left" ? "right" : "left";
|
|
38
|
-
if (side === "top" || side === "bottom") {
|
|
39
|
-
if (align === "start") {
|
|
40
|
-
return `${anchor} left`;
|
|
41
|
-
}
|
|
42
|
-
if (align === "end") {
|
|
43
|
-
return `${anchor} right`;
|
|
44
|
-
}
|
|
45
|
-
return `${anchor} center`;
|
|
46
|
-
}
|
|
47
|
-
if (align === "start") {
|
|
48
|
-
return `${anchor} top`;
|
|
49
|
-
}
|
|
50
|
-
if (align === "end") {
|
|
51
|
-
return `${anchor} bottom`;
|
|
52
|
-
}
|
|
53
|
-
return `${anchor} center`;
|
|
54
|
-
}
|
|
55
38
|
function getPlacementParts(placement) {
|
|
56
39
|
const [side, align = "center"] = placement.split("-");
|
|
57
40
|
return { side, align };
|
|
@@ -132,10 +115,13 @@ function PopoverTrigger({ asChild = false, className, disabled, onClick, ref, ty
|
|
|
132
115
|
}
|
|
133
116
|
return (_jsx("button", { ...props, ref: handleRef, type: type, disabled: disabled, className: cn("zvk-ui-popover__trigger", className), "aria-expanded": context.open, "aria-controls": context.contentId, "aria-haspopup": "dialog", "data-state": context.open ? "open" : "closed", onClick: composeEventHandlers(onClick, handleClick) }));
|
|
134
117
|
}
|
|
135
|
-
function PopoverContent({ align, alignOffset = defaultPositioning.alignOffset, className, container, forceMount = false, id, disableEscapeKeyDown = false, disableOutsidePointerDown = false, ref, placement, side, sideOffset = 0, collisionPadding = 0, matchTriggerWidth = false, style, ...props }) {
|
|
118
|
+
function PopoverContent({ align, alignOffset = defaultPositioning.alignOffset, className, container, forceMount = false, id, disableEscapeKeyDown = false, disableOutsidePointerDown = false, onAnimationEnd, ref, placement, side, sideOffset = 0, collisionPadding = 0, matchTriggerWidth = false, style, ...props }) {
|
|
136
119
|
const context = usePopoverContext("Popover.Content");
|
|
137
120
|
const { defaultContentId, setContentId } = context;
|
|
138
|
-
const
|
|
121
|
+
const presence = usePresence({ open: context.open, forceMount, exitDurationMs: popoverExitDurationMs });
|
|
122
|
+
const wasOpenRef = React.useRef(context.open);
|
|
123
|
+
const isClosing = wasOpenRef.current && !context.open;
|
|
124
|
+
const transformOrigin = getFloatingTransformOrigin(context.floatingPlacement);
|
|
139
125
|
const placementParts = getPlacementParts(context.floatingPlacement);
|
|
140
126
|
const contentId = id ?? defaultContentId;
|
|
141
127
|
const handleRef = React.useCallback((node) => {
|
|
@@ -153,6 +139,9 @@ function PopoverContent({ align, alignOffset = defaultPositioning.alignOffset, c
|
|
|
153
139
|
setContentId(defaultContentId);
|
|
154
140
|
};
|
|
155
141
|
}, [contentId, defaultContentId, setContentId]);
|
|
142
|
+
React.useEffect(() => {
|
|
143
|
+
wasOpenRef.current = context.open;
|
|
144
|
+
}, [context.open]);
|
|
156
145
|
const close = () => {
|
|
157
146
|
context.setOpen(false);
|
|
158
147
|
};
|
|
@@ -176,10 +165,11 @@ function PopoverContent({ align, alignOffset = defaultPositioning.alignOffset, c
|
|
|
176
165
|
context.updateContentPositioning(defaultPositioning);
|
|
177
166
|
};
|
|
178
167
|
}, [align, alignOffset, context.updateContentPositioning, collisionPadding, matchTriggerWidth, placement, side, sideOffset]);
|
|
179
|
-
if (!
|
|
168
|
+
if (!presence.present) {
|
|
180
169
|
return null;
|
|
181
170
|
}
|
|
182
|
-
const
|
|
171
|
+
const hidden = presence.inert && !isClosing && presence.motionState !== "exiting" ? true : undefined;
|
|
172
|
+
const content = (_jsx("div", { ...props, ref: handleRef, "aria-hidden": presence.inert ? true : undefined, id: contentId, inert: presence.inert ? true : undefined, role: "dialog", className: cn("zvk-ui-popover__content", className), "data-align": placementParts.align, "data-side": placementParts.side, "data-state": presence.state, hidden: hidden, onAnimationEnd: composeEventHandlers(onAnimationEnd, presence.onExitComplete), style: contentStyle }));
|
|
183
173
|
if (context.open &&
|
|
184
174
|
props["aria-label"] === undefined &&
|
|
185
175
|
props["aria-labelledby"] === undefined &&
|
|
@@ -53,7 +53,7 @@ export interface SelectClearProps extends React.ButtonHTMLAttributes<HTMLButtonE
|
|
|
53
53
|
declare function SelectRoot({ children, className, defaultOpen, defaultValue, disabled, invalid, name, onOpenChange, onValueChange, open: openProp, placeholder, placement, required, size, value, ...props }: SelectProps): React.JSX.Element;
|
|
54
54
|
declare function SelectTrigger({ children, className, disabled, onClick, onKeyDown, ref, type, ...props }: SelectTriggerProps): React.JSX.Element;
|
|
55
55
|
declare function SelectValue({ className, placeholder, ref, ...props }: SelectValueProps): React.JSX.Element;
|
|
56
|
-
declare function SelectContent({ children, className, collisionPadding, container, forceMount, matchTriggerWidth, onKeyDown, ref, sideOffset, style, ...props }: SelectContentProps): React.JSX.Element | null;
|
|
56
|
+
declare function SelectContent({ children, className, collisionPadding, container, forceMount, matchTriggerWidth, onAnimationEnd, onKeyDown, ref, sideOffset, style, ...props }: SelectContentProps): React.JSX.Element | null;
|
|
57
57
|
declare function SelectItem({ children, className, disabled, onClick, onKeyDown, ref, value, ...props }: SelectItemProps): React.JSX.Element;
|
|
58
58
|
declare function SelectGroup({ children, className, ref, ...props }: SelectGroupProps): React.JSX.Element;
|
|
59
59
|
declare function SelectLabel({ className, ref, ...props }: SelectLabelProps): React.JSX.Element;
|
|
@@ -7,6 +7,8 @@ import { cn } from "../../utils/cn.js";
|
|
|
7
7
|
import { Portal } from "../../internal/portal/index.js";
|
|
8
8
|
import { DismissableLayer } from "../../internal/dismissable-layer/index.js";
|
|
9
9
|
import { useFloatingPosition } from "../../internal/floating/index.js";
|
|
10
|
+
import { placementParts } from "../../internal/floating/placement-aliases.js";
|
|
11
|
+
import { usePresence } from "../../internal/presence/index.js";
|
|
10
12
|
const nativeSelectStyle = {
|
|
11
13
|
position: "absolute",
|
|
12
14
|
width: 1,
|
|
@@ -20,6 +22,8 @@ const defaultContentPositioning = {
|
|
|
20
22
|
collisionPadding: 0,
|
|
21
23
|
matchTriggerWidth: true
|
|
22
24
|
};
|
|
25
|
+
const selectExitDurationMs = 120;
|
|
26
|
+
const selectTypeaheadTimeoutMs = 700;
|
|
23
27
|
const SelectContext = React.createContext(null);
|
|
24
28
|
function useSelectContext(calledBy) {
|
|
25
29
|
const context = React.useContext(SelectContext);
|
|
@@ -43,6 +47,18 @@ function composeRefs(...refs) {
|
|
|
43
47
|
function getTextLabel(node) {
|
|
44
48
|
return node;
|
|
45
49
|
}
|
|
50
|
+
function getTypeaheadLabel(node, fallback) {
|
|
51
|
+
if (typeof node === "string" || typeof node === "number") {
|
|
52
|
+
return String(node);
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(node)) {
|
|
55
|
+
return node.map((child) => getTypeaheadLabel(child, "")).join(" ").trim() || fallback;
|
|
56
|
+
}
|
|
57
|
+
if (React.isValidElement(node)) {
|
|
58
|
+
return getTypeaheadLabel(node.props.children, fallback);
|
|
59
|
+
}
|
|
60
|
+
return fallback;
|
|
61
|
+
}
|
|
46
62
|
function getNativeOptionLabel(label, value) {
|
|
47
63
|
if (typeof label === "string" || typeof label === "number") {
|
|
48
64
|
return String(label);
|
|
@@ -70,6 +86,30 @@ function collectNativeOptions(children) {
|
|
|
70
86
|
visit(children);
|
|
71
87
|
return options;
|
|
72
88
|
}
|
|
89
|
+
function isPrintableTypeaheadKey(event) {
|
|
90
|
+
return event.key.length === 1 && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
91
|
+
}
|
|
92
|
+
function getTypeaheadSearch(buffer) {
|
|
93
|
+
const normalized = buffer.toLocaleLowerCase();
|
|
94
|
+
if (normalized.length > 1 && normalized.split("").every((character) => character === normalized[0])) {
|
|
95
|
+
return normalized[0] ?? "";
|
|
96
|
+
}
|
|
97
|
+
return normalized;
|
|
98
|
+
}
|
|
99
|
+
function findTypeaheadMatch(items, search, activeElement) {
|
|
100
|
+
if (items.length === 0 || search.length === 0) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
const activeIndex = items.findIndex((item) => item.element === activeElement);
|
|
104
|
+
const startIndex = activeIndex >= 0 ? activeIndex + 1 : 0;
|
|
105
|
+
for (let offset = 0; offset < items.length; offset += 1) {
|
|
106
|
+
const item = items[(startIndex + offset) % items.length];
|
|
107
|
+
if (item?.labelText.toLocaleLowerCase().startsWith(search)) {
|
|
108
|
+
return item;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
73
113
|
function SelectRoot({ children, className, defaultOpen = false, defaultValue = null, disabled, invalid, name, onOpenChange, onValueChange, open: openProp, placeholder, placement = "bottom-start", required, size = "md", value, ...props }) {
|
|
74
114
|
const instanceId = React.useId();
|
|
75
115
|
const triggerId = `${instanceId}-trigger`;
|
|
@@ -77,6 +117,8 @@ function SelectRoot({ children, className, defaultOpen = false, defaultValue = n
|
|
|
77
117
|
const triggerRef = React.useRef(null);
|
|
78
118
|
const itemRegistry = React.useRef(new Map());
|
|
79
119
|
const itemOrder = React.useRef([]);
|
|
120
|
+
const typeaheadBuffer = React.useRef("");
|
|
121
|
+
const typeaheadTimer = React.useRef(null);
|
|
80
122
|
const wasOpenRef = React.useRef(false);
|
|
81
123
|
const [open, setOpen] = useControllableState({
|
|
82
124
|
...(openProp !== undefined ? { value: openProp } : {}),
|
|
@@ -122,6 +164,26 @@ function SelectRoot({ children, className, defaultOpen = false, defaultValue = n
|
|
|
122
164
|
setCurrentValue(nextValue);
|
|
123
165
|
setOpen(false);
|
|
124
166
|
}, [setCurrentValue, setOpen]);
|
|
167
|
+
const resetTypeahead = React.useCallback(() => {
|
|
168
|
+
typeaheadBuffer.current = "";
|
|
169
|
+
if (typeaheadTimer.current !== null) {
|
|
170
|
+
clearTimeout(typeaheadTimer.current);
|
|
171
|
+
typeaheadTimer.current = null;
|
|
172
|
+
}
|
|
173
|
+
}, []);
|
|
174
|
+
const focusTypeaheadMatch = React.useCallback((key) => {
|
|
175
|
+
typeaheadBuffer.current += key;
|
|
176
|
+
if (typeaheadTimer.current !== null) {
|
|
177
|
+
clearTimeout(typeaheadTimer.current);
|
|
178
|
+
}
|
|
179
|
+
typeaheadTimer.current = setTimeout(resetTypeahead, selectTypeaheadTimeoutMs);
|
|
180
|
+
const match = findTypeaheadMatch(getEnabledItems(), getTypeaheadSearch(typeaheadBuffer.current), document.activeElement);
|
|
181
|
+
if (!match?.element) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
match.element.focus();
|
|
185
|
+
return true;
|
|
186
|
+
}, [getEnabledItems, resetTypeahead]);
|
|
125
187
|
const orderedItems = React.useMemo(() => getOrderedItems(), [getOrderedItems, itemsVersion]);
|
|
126
188
|
const selectedLabel = React.useMemo(() => orderedItems.find((item) => item.value === currentValue)?.label ??
|
|
127
189
|
nativeOptions.find((item) => item.value === currentValue)?.label, [currentValue, nativeOptions, orderedItems]);
|
|
@@ -133,13 +195,17 @@ function SelectRoot({ children, className, defaultOpen = false, defaultValue = n
|
|
|
133
195
|
if (wasOpenRef.current && !open) {
|
|
134
196
|
triggerRef.current?.focus();
|
|
135
197
|
}
|
|
198
|
+
if (!open) {
|
|
199
|
+
resetTypeahead();
|
|
200
|
+
}
|
|
136
201
|
wasOpenRef.current = open;
|
|
137
|
-
}, [open]);
|
|
202
|
+
}, [open, resetTypeahead]);
|
|
138
203
|
React.useEffect(() => {
|
|
139
204
|
if (!required || currentValue !== null) {
|
|
140
205
|
setNativeInvalid(false);
|
|
141
206
|
}
|
|
142
207
|
}, [currentValue, required]);
|
|
208
|
+
React.useEffect(() => resetTypeahead, [resetTypeahead]);
|
|
143
209
|
const handleNativeInvalid = React.useCallback((event) => {
|
|
144
210
|
event.preventDefault();
|
|
145
211
|
setNativeInvalid(true);
|
|
@@ -147,6 +213,7 @@ function SelectRoot({ children, className, defaultOpen = false, defaultValue = n
|
|
|
147
213
|
}, []);
|
|
148
214
|
const context = React.useMemo(() => ({
|
|
149
215
|
contentId,
|
|
216
|
+
focusTypeaheadMatch,
|
|
150
217
|
...(disabled !== undefined ? { disabled } : {}),
|
|
151
218
|
getEnabledItems,
|
|
152
219
|
...(resolvedInvalid ? { invalid: true } : {}),
|
|
@@ -164,8 +231,9 @@ function SelectRoot({ children, className, defaultOpen = false, defaultValue = n
|
|
|
164
231
|
setOpen,
|
|
165
232
|
referenceRef: floating.referenceRef,
|
|
166
233
|
floatingRef: floating.floatingRef,
|
|
234
|
+
floatingPlacement: floating.placement,
|
|
167
235
|
floatingStyle: floating.floatingStyle
|
|
168
|
-
}), [contentId, currentValue, disabled, floating.floatingRef, floating.floatingStyle, floating.referenceRef, getEnabledItems, open, placeholder, registerItem, required, resolvedInvalid, selectValue, selectedLabel, setOpen, triggerId, unregisterItem]);
|
|
236
|
+
}), [contentId, currentValue, disabled, floating.floatingRef, floating.floatingStyle, floating.placement, floating.referenceRef, focusTypeaheadMatch, getEnabledItems, open, placeholder, registerItem, required, resolvedInvalid, selectValue, selectedLabel, setOpen, triggerId, unregisterItem]);
|
|
169
237
|
return (_jsx(SelectContext.Provider, { value: context, children: _jsxs("div", { ...props, className: cn("zvk-ui-select", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": resolvedInvalid ? "true" : undefined, "data-required": required ? "true" : undefined, "data-size": size, "data-state": open ? "open" : "closed", children: [name ? (_jsxs("select", { "aria-hidden": "true", disabled: disabled, name: name, onChange: () => undefined, onInvalid: handleNativeInvalid, required: required, style: nativeSelectStyle, tabIndex: -1, value: nativeValue, children: [_jsx("option", { value: "" }), nativeOptions.map((item) => (_jsx("option", { disabled: item.disabled, value: item.value, children: item.label }, item.value)))] })) : null, children] }) }));
|
|
170
238
|
}
|
|
171
239
|
function focusItem(items, index) {
|
|
@@ -190,7 +258,7 @@ function SelectTrigger({ children, className, disabled, onClick, onKeyDown, ref,
|
|
|
190
258
|
focusItem(items, selectedIndex >= 0 ? selectedIndex : 0);
|
|
191
259
|
});
|
|
192
260
|
}, [context]);
|
|
193
|
-
return (
|
|
261
|
+
return (_jsxs("button", { ...props, ref: composeRefs(ref, context.triggerRef, context.referenceRef), "aria-controls": context.contentId, "aria-expanded": context.open ? "true" : "false", "aria-haspopup": "listbox", "aria-invalid": context.invalid ? "true" : undefined, "aria-required": context.required ? "true" : undefined, className: cn("zvk-ui-select__trigger", className), "data-disabled": isDisabled ? "true" : undefined, "data-invalid": context.invalid ? "true" : undefined, "data-state": context.open ? "open" : "closed", disabled: isDisabled, id: context.triggerId, onClick: composeEventHandlers(onClick, () => {
|
|
194
262
|
if (!isDisabled) {
|
|
195
263
|
context.setOpen(!context.open);
|
|
196
264
|
}
|
|
@@ -206,15 +274,19 @@ function SelectTrigger({ children, className, disabled, onClick, onKeyDown, ref,
|
|
|
206
274
|
event.preventDefault();
|
|
207
275
|
openAndFocus("last");
|
|
208
276
|
}
|
|
209
|
-
}), type: type, children: children }));
|
|
277
|
+
}), type: type, children: [children, _jsx("span", { className: "zvk-ui-select__indicator", "aria-hidden": "true" })] }));
|
|
210
278
|
}
|
|
211
279
|
function SelectValue({ className, placeholder, ref, ...props }) {
|
|
212
280
|
const context = useSelectContext("Select.Value");
|
|
213
281
|
const displayPlaceholder = context.selectedLabel === undefined;
|
|
214
282
|
return (_jsx("span", { ...props, ref: ref, className: cn("zvk-ui-select__value", className), "data-placeholder": displayPlaceholder ? "true" : undefined, children: displayPlaceholder ? placeholder ?? context.placeholder : context.selectedLabel }));
|
|
215
283
|
}
|
|
216
|
-
function SelectContent({ children, className, collisionPadding = defaultContentPositioning.collisionPadding, container, forceMount = false, matchTriggerWidth = defaultContentPositioning.matchTriggerWidth, onKeyDown, ref, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
|
|
284
|
+
function SelectContent({ children, className, collisionPadding = defaultContentPositioning.collisionPadding, container, forceMount = false, matchTriggerWidth = defaultContentPositioning.matchTriggerWidth, onAnimationEnd, onKeyDown, ref, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
|
|
217
285
|
const context = useSelectContext("Select.Content");
|
|
286
|
+
const presence = usePresence({ open: context.open, forceMount, exitDurationMs: selectExitDurationMs });
|
|
287
|
+
const wasOpenRef = React.useRef(context.open);
|
|
288
|
+
const isClosing = wasOpenRef.current && !context.open;
|
|
289
|
+
const resolvedPlacement = placementParts(context.floatingPlacement);
|
|
218
290
|
React.useEffect(() => {
|
|
219
291
|
context.updateContentPositioning({
|
|
220
292
|
sideOffset,
|
|
@@ -223,6 +295,9 @@ function SelectContent({ children, className, collisionPadding = defaultContentP
|
|
|
223
295
|
});
|
|
224
296
|
return () => context.updateContentPositioning(defaultContentPositioning);
|
|
225
297
|
}, [collisionPadding, context, matchTriggerWidth, sideOffset]);
|
|
298
|
+
React.useEffect(() => {
|
|
299
|
+
wasOpenRef.current = context.open;
|
|
300
|
+
}, [context.open]);
|
|
226
301
|
const moveFocus = React.useCallback((direction) => {
|
|
227
302
|
const items = context.getEnabledItems();
|
|
228
303
|
const currentIndex = items.findIndex((item) => item.element === document.activeElement);
|
|
@@ -232,10 +307,11 @@ function SelectContent({ children, className, collisionPadding = defaultContentP
|
|
|
232
307
|
const items = context.getEnabledItems();
|
|
233
308
|
focusItem(items, end ? items.length - 1 : 0);
|
|
234
309
|
}, [context]);
|
|
235
|
-
if (!
|
|
310
|
+
if (!presence.present) {
|
|
236
311
|
return null;
|
|
237
312
|
}
|
|
238
|
-
const
|
|
313
|
+
const hidden = presence.inert && !isClosing && presence.motionState !== "exiting" ? true : undefined;
|
|
314
|
+
const content = (_jsx("div", { ...props, ref: composeRefs(ref, context.floatingRef), "aria-hidden": presence.inert ? true : undefined, "aria-labelledby": context.triggerId, className: cn("zvk-ui-select__content", className), "data-align": resolvedPlacement.align, "data-side": resolvedPlacement.side, "data-state": presence.state, hidden: hidden, id: context.contentId, inert: presence.inert ? true : undefined, onAnimationEnd: composeEventHandlers(onAnimationEnd, presence.onExitComplete), onKeyDown: composeEventHandlers(onKeyDown, (event) => {
|
|
239
315
|
if (event.key === "ArrowDown") {
|
|
240
316
|
event.preventDefault();
|
|
241
317
|
moveFocus("next");
|
|
@@ -255,11 +331,14 @@ function SelectContent({ children, className, collisionPadding = defaultContentP
|
|
|
255
331
|
if (event.key === "Escape") {
|
|
256
332
|
context.setOpen(false);
|
|
257
333
|
}
|
|
334
|
+
if (isPrintableTypeaheadKey(event) && context.focusTypeaheadMatch(event.key)) {
|
|
335
|
+
event.preventDefault();
|
|
336
|
+
}
|
|
258
337
|
}), role: "listbox", style: { ...context.floatingStyle, ...style }, tabIndex: -1, children: children }));
|
|
259
338
|
if (!context.open) {
|
|
260
339
|
return _jsx(Portal, { ...(container === undefined ? {} : { container }), children: content });
|
|
261
340
|
}
|
|
262
|
-
return (_jsx(Portal, { ...(container === undefined ? {} : { container }), children: _jsx(DismissableLayer, { open:
|
|
341
|
+
return (_jsx(Portal, { ...(container === undefined ? {} : { container }), children: _jsx(DismissableLayer, { open: context.open, onDismiss: () => context.setOpen(false), children: content }) }));
|
|
263
342
|
}
|
|
264
343
|
function SelectItem({ children, className, disabled = false, onClick, onKeyDown, ref, value, ...props }) {
|
|
265
344
|
const context = useSelectContext("Select.Item");
|
|
@@ -272,6 +351,7 @@ function SelectItem({ children, className, disabled = false, onClick, onKeyDown,
|
|
|
272
351
|
element: itemRef.current,
|
|
273
352
|
id: itemId,
|
|
274
353
|
label: getTextLabel(children),
|
|
354
|
+
labelText: getTypeaheadLabel(children, value),
|
|
275
355
|
value
|
|
276
356
|
});
|
|
277
357
|
return () => context.unregisterItem(itemId);
|
|
@@ -37,7 +37,7 @@ export interface SheetCloseProps extends React.ButtonHTMLAttributes<HTMLButtonEl
|
|
|
37
37
|
}
|
|
38
38
|
declare function SheetRoot({ children, className, container, defaultOpen, disableEscapeKeyDown, disableOutsidePointerDown, onOpenChange, open: openProp, ref, side, ...props }: SheetProps): React.JSX.Element;
|
|
39
39
|
declare function SheetTrigger({ asChild, className, disabled, onClick, ref, type, ...props }: SheetTriggerProps): React.JSX.Element;
|
|
40
|
-
declare function SheetContent({ children, className, forceMount, ref, ...props }: SheetContentProps): React.JSX.Element | null;
|
|
40
|
+
declare function SheetContent({ children, className, forceMount, onAnimationEnd, onTransitionEnd, ref, ...props }: SheetContentProps): React.JSX.Element | null;
|
|
41
41
|
declare function SheetHeader({ className, ref, ...props }: SheetHeaderProps): React.JSX.Element;
|
|
42
42
|
declare function SheetTitle({ className, ref, ...props }: SheetTitleProps): React.JSX.Element;
|
|
43
43
|
declare function SheetDescription({ className, ref, ...props }: SheetDescriptionProps): React.JSX.Element;
|
|
@@ -6,6 +6,7 @@ import { cn } from "../../utils/cn.js";
|
|
|
6
6
|
import { useControllableState } from "../../hooks/use-controllable-state.js";
|
|
7
7
|
import { DismissableLayer } from "../../internal/dismissable-layer/index.js";
|
|
8
8
|
import { FocusScope } from "../../internal/focus/index.js";
|
|
9
|
+
import { usePresence } from "../../internal/presence/index.js";
|
|
9
10
|
import { Portal } from "../../internal/portal/index.js";
|
|
10
11
|
import { lockScroll, unlockScroll } from "../../internal/scroll-lock/index.js";
|
|
11
12
|
import { Slot } from "../../internal/slot/index.js";
|
|
@@ -29,6 +30,7 @@ function composeRefs(...refs) {
|
|
|
29
30
|
}
|
|
30
31
|
};
|
|
31
32
|
}
|
|
33
|
+
const sheetExitDurationMs = 240;
|
|
32
34
|
function SheetRoot({ children, className, container, defaultOpen = false, disableEscapeKeyDown = false, disableOutsidePointerDown = false, onOpenChange, open: openProp, ref, side = "right", ...props }) {
|
|
33
35
|
const [open, setOpen] = useControllableState({
|
|
34
36
|
...(openProp !== undefined ? { value: openProp } : {}),
|
|
@@ -47,13 +49,6 @@ function SheetRoot({ children, className, container, defaultOpen = false, disabl
|
|
|
47
49
|
setDescribedBy(id);
|
|
48
50
|
return () => setDescribedBy((current) => (current === id ? undefined : current));
|
|
49
51
|
}, []);
|
|
50
|
-
React.useLayoutEffect(() => {
|
|
51
|
-
if (!open) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
lockScroll();
|
|
55
|
-
return () => unlockScroll();
|
|
56
|
-
}, [open]);
|
|
57
52
|
return (_jsx(SheetContext.Provider, { value: {
|
|
58
53
|
close: () => setOpen(false),
|
|
59
54
|
...(container === undefined ? {} : { container }),
|
|
@@ -84,16 +79,26 @@ function SheetTrigger({ asChild = false, className, disabled, onClick, ref, type
|
|
|
84
79
|
}
|
|
85
80
|
return (_jsx("button", { ...props, ref: composeRefs(ref, triggerRef), type: type, disabled: disabled, "aria-controls": contentId, "aria-expanded": open ? "true" : "false", className: cn("zvk-ui-sheet__trigger", className), "data-state": open ? "open" : "closed", onClick: composeEventHandlers(onClick, handleClick) }));
|
|
86
81
|
}
|
|
87
|
-
function SheetContent({ children, className, forceMount = false, ref, ...props }) {
|
|
82
|
+
function SheetContent({ children, className, forceMount = false, onAnimationEnd, onTransitionEnd, ref, ...props }) {
|
|
88
83
|
const { close, container, contentId, describedBy, disableEscapeKeyDown, disableOutsidePointerDown, labelledBy, open, side } = useSheetContext("Sheet.Content");
|
|
89
|
-
|
|
84
|
+
const presence = usePresence({ open, forceMount, exitDurationMs: sheetExitDurationMs });
|
|
85
|
+
const hidden = !open && forceMount && presence.motionState === "idle";
|
|
86
|
+
const shouldLockScroll = presence.present && (!presence.inert || presence.motionState === "exiting");
|
|
87
|
+
React.useLayoutEffect(() => {
|
|
88
|
+
if (!shouldLockScroll) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
lockScroll();
|
|
92
|
+
return () => unlockScroll();
|
|
93
|
+
}, [shouldLockScroll]);
|
|
94
|
+
if (!presence.present) {
|
|
90
95
|
return null;
|
|
91
96
|
}
|
|
92
|
-
const content = (_jsx("div", { ...props, ref: ref, id: contentId, role: "dialog", "aria-modal":
|
|
97
|
+
const content = (_jsx("div", { ...props, ref: ref, id: contentId, role: "dialog", "aria-hidden": presence.inert ? true : undefined, "aria-modal": open ? true : undefined, "aria-describedby": describedBy, "aria-labelledby": labelledBy, className: cn("zvk-ui-sheet__content", className), "data-motion-state": presence.motionState, "data-side": side, "data-state": presence.state, hidden: hidden ? true : undefined, inert: presence.inert ? true : undefined, onAnimationEnd: composeEventHandlers(onAnimationEnd, presence.onExitComplete, { checkDefaultPrevented: false }), onTransitionEnd: composeEventHandlers(onTransitionEnd, presence.onExitComplete, { checkDefaultPrevented: false }), children: children }));
|
|
93
98
|
if (!open) {
|
|
94
|
-
return (_jsxs(Portal, { ...(container === undefined ? {} : { container }), children: [_jsx("div", { "aria-hidden": "true", className: "zvk-ui-sheet__overlay", hidden: true }), content] }));
|
|
99
|
+
return (_jsxs(Portal, { ...(container === undefined ? {} : { container }), children: [_jsx("div", { "aria-hidden": "true", className: "zvk-ui-sheet__overlay", "data-state": presence.state, hidden: hidden ? true : undefined }), content] }));
|
|
95
100
|
}
|
|
96
|
-
return (_jsxs(Portal, { ...(container === undefined ? {} : { container }), children: [_jsx("div", { "aria-hidden": "true", className: "zvk-ui-sheet__overlay" }), _jsx(DismissableLayer, { open: open, disableEscapeKeyDown: disableEscapeKeyDown, disableOutsidePointerDown: disableOutsidePointerDown, onDismiss: close, children: _jsx(FocusScope, { active: open, trapped: true, children: content }) })] }));
|
|
101
|
+
return (_jsxs(Portal, { ...(container === undefined ? {} : { container }), children: [_jsx("div", { "aria-hidden": "true", className: "zvk-ui-sheet__overlay", "data-state": presence.state }), _jsx(DismissableLayer, { open: open, disableEscapeKeyDown: disableEscapeKeyDown, disableOutsidePointerDown: disableOutsidePointerDown, onDismiss: close, children: _jsx(FocusScope, { active: open, trapped: true, children: content }) })] }));
|
|
97
102
|
}
|
|
98
103
|
function SheetHeader({ className, ref, ...props }) {
|
|
99
104
|
return _jsx("div", { ...props, ref: ref, className: cn("zvk-ui-sheet__header", className) });
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Table } from "./table.js";
|
|
2
|
-
export type { TableBodyProps, TableCaptionProps, TableCellProps, TableContainerProps, TableFooterProps, TableHeadProps, TableHeaderProps, TableProps, TableRowProps } from "./table.js";
|
|
2
|
+
export type { TableBodyProps, TableCaptionProps, TableCellProps, TableContainerProps, TableFooterProps, TableHeadProps, TableHeaderProps, TableProps, TableRowProps, TableSelectionCellProps, TableSortButtonProps, TableStateRowProps, TableToolbarActionsProps, TableToolbarProps, TableToolbarTitleProps } from "./table.js";
|
|
@@ -28,6 +28,31 @@ export interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableHeaderCe
|
|
|
28
28
|
export interface TableCellProps extends React.TdHTMLAttributes<HTMLTableCellElement> {
|
|
29
29
|
ref?: React.Ref<HTMLTableCellElement>;
|
|
30
30
|
}
|
|
31
|
+
export interface TableToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
32
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
33
|
+
}
|
|
34
|
+
export interface TableToolbarTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
|
|
35
|
+
ref?: React.Ref<HTMLHeadingElement>;
|
|
36
|
+
}
|
|
37
|
+
export interface TableToolbarActionsProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
38
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
39
|
+
}
|
|
40
|
+
export interface TableSelectionCellProps extends Omit<React.TdHTMLAttributes<HTMLTableCellElement>, "children"> {
|
|
41
|
+
checked: boolean;
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
44
|
+
ref?: React.Ref<HTMLTableCellElement>;
|
|
45
|
+
"aria-label": string;
|
|
46
|
+
}
|
|
47
|
+
export interface TableSortButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
48
|
+
direction?: "ascending" | "descending" | "none";
|
|
49
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
50
|
+
}
|
|
51
|
+
export interface TableStateRowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
|
52
|
+
colSpan: number;
|
|
53
|
+
tone?: "empty" | "loading" | "error";
|
|
54
|
+
ref?: React.Ref<HTMLTableRowElement>;
|
|
55
|
+
}
|
|
31
56
|
declare function TableRoot({ className, density, ref, variant, ...props }: TableProps): React.JSX.Element;
|
|
32
57
|
declare function TableCaption({ className, ref, ...props }: TableCaptionProps): React.JSX.Element;
|
|
33
58
|
declare function TableHeader({ className, ref, ...props }: TableHeaderProps): React.JSX.Element;
|
|
@@ -36,6 +61,12 @@ declare function TableFooter({ className, ref, ...props }: TableFooterProps): Re
|
|
|
36
61
|
declare function TableRow({ className, ref, ...props }: TableRowProps): React.JSX.Element;
|
|
37
62
|
declare function TableHead({ className, ref, scope, ...props }: TableHeadProps): React.JSX.Element;
|
|
38
63
|
declare function TableCell({ className, ref, ...props }: TableCellProps): React.JSX.Element;
|
|
64
|
+
declare function TableToolbar({ className, ref, ...props }: TableToolbarProps): React.JSX.Element;
|
|
65
|
+
declare function TableToolbarTitle({ className, ref, ...props }: TableToolbarTitleProps): React.JSX.Element;
|
|
66
|
+
declare function TableToolbarActions({ className, ref, ...props }: TableToolbarActionsProps): React.JSX.Element;
|
|
67
|
+
declare function TableSelectionCell({ checked, className, disabled, onCheckedChange, ref, "aria-label": ariaLabel, ...props }: TableSelectionCellProps): React.JSX.Element;
|
|
68
|
+
declare function TableSortButton({ children, className, direction, ref, type, ...props }: TableSortButtonProps): React.JSX.Element;
|
|
69
|
+
declare function TableStateRow({ children, className, colSpan, ref, tone, ...props }: TableStateRowProps): React.JSX.Element;
|
|
39
70
|
declare function TableContainer({ className, ref, ...props }: TableContainerProps): React.JSX.Element;
|
|
40
71
|
export declare const Table: typeof TableRoot & {
|
|
41
72
|
Container: typeof TableContainer;
|
|
@@ -46,5 +77,11 @@ export declare const Table: typeof TableRoot & {
|
|
|
46
77
|
Row: typeof TableRow;
|
|
47
78
|
Head: typeof TableHead;
|
|
48
79
|
Cell: typeof TableCell;
|
|
80
|
+
Toolbar: typeof TableToolbar;
|
|
81
|
+
ToolbarTitle: typeof TableToolbarTitle;
|
|
82
|
+
ToolbarActions: typeof TableToolbarActions;
|
|
83
|
+
SelectionCell: typeof TableSelectionCell;
|
|
84
|
+
SortButton: typeof TableSortButton;
|
|
85
|
+
StateRow: typeof TableStateRow;
|
|
49
86
|
};
|
|
50
87
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { Checkbox } from "../checkbox/checkbox.js";
|
|
3
4
|
import { cn } from "../../utils/cn.js";
|
|
4
5
|
function TableRoot({ className, density = "default", ref, variant = "plain", ...props }) {
|
|
5
6
|
return (_jsx("table", { ...props, ref: ref, className: cn("zvk-ui-table", className), "data-density": density, "data-variant": variant }));
|
|
@@ -25,6 +26,27 @@ function TableHead({ className, ref, scope = "col", ...props }) {
|
|
|
25
26
|
function TableCell({ className, ref, ...props }) {
|
|
26
27
|
return _jsx("td", { ...props, ref: ref, className: cn("zvk-ui-table__cell", className) });
|
|
27
28
|
}
|
|
29
|
+
function TableToolbar({ className, ref, ...props }) {
|
|
30
|
+
return _jsx("div", { ...props, ref: ref, className: cn("zvk-ui-table__toolbar", className) });
|
|
31
|
+
}
|
|
32
|
+
function TableToolbarTitle({ className, ref, ...props }) {
|
|
33
|
+
return _jsx("h3", { ...props, ref: ref, className: cn("zvk-ui-table__toolbar-title", className) });
|
|
34
|
+
}
|
|
35
|
+
function TableToolbarActions({ className, ref, ...props }) {
|
|
36
|
+
return _jsx("div", { ...props, ref: ref, className: cn("zvk-ui-table__toolbar-actions", className) });
|
|
37
|
+
}
|
|
38
|
+
function TableSelectionCell({ checked, className, disabled, onCheckedChange, ref, "aria-label": ariaLabel, ...props }) {
|
|
39
|
+
return (_jsx("td", { ...props, ref: ref, className: cn("zvk-ui-table__cell", "zvk-ui-table__selection-cell", className), "data-disabled": disabled ? "true" : undefined, children: _jsx(Checkbox, { "aria-label": ariaLabel, checked: checked, className: "zvk-ui-table__selection-checkbox", disabled: disabled, onChange: (event) => onCheckedChange?.(event.currentTarget.checked) }) }));
|
|
40
|
+
}
|
|
41
|
+
function TableSortButton({ children, className, direction = "none", ref, type = "button", ...props }) {
|
|
42
|
+
const directionLabel = direction === "ascending" ? "sorted ascending" : direction === "descending" ? "sorted descending" : "not sorted";
|
|
43
|
+
const visibleDirection = direction === "ascending" ? "Asc" : direction === "descending" ? "Desc" : "Sort";
|
|
44
|
+
const accessibleLabel = props["aria-label"] ?? (typeof children === "string" ? `${children} ${directionLabel}` : undefined);
|
|
45
|
+
return (_jsxs("button", { ...props, "aria-label": accessibleLabel, ref: ref, className: cn("zvk-ui-table__sort-button", className), "data-direction": direction, type: type, children: [_jsx("span", { className: "zvk-ui-table__sort-label", children: children }), _jsxs("span", { className: "zvk-ui-sr-only", children: [" ", directionLabel] }), _jsx("span", { "aria-hidden": "true", className: "zvk-ui-table__sort-direction", children: visibleDirection })] }));
|
|
46
|
+
}
|
|
47
|
+
function TableStateRow({ children, className, colSpan, ref, tone = "empty", ...props }) {
|
|
48
|
+
return (_jsx("tr", { ...props, ref: ref, className: cn("zvk-ui-table__row", "zvk-ui-table__state-row", className), "data-state": tone, children: _jsx("td", { className: "zvk-ui-table__cell zvk-ui-table__state-cell", colSpan: colSpan, children: children }) }));
|
|
49
|
+
}
|
|
28
50
|
function TableContainer({ className, ref, ...props }) {
|
|
29
51
|
return _jsx("div", { ...props, ref: ref, className: cn("zvk-ui-table-container", className) });
|
|
30
52
|
}
|
|
@@ -36,5 +58,11 @@ export const Table = Object.assign(TableRoot, {
|
|
|
36
58
|
Footer: TableFooter,
|
|
37
59
|
Row: TableRow,
|
|
38
60
|
Head: TableHead,
|
|
39
|
-
Cell: TableCell
|
|
61
|
+
Cell: TableCell,
|
|
62
|
+
Toolbar: TableToolbar,
|
|
63
|
+
ToolbarTitle: TableToolbarTitle,
|
|
64
|
+
ToolbarActions: TableToolbarActions,
|
|
65
|
+
SelectionCell: TableSelectionCell,
|
|
66
|
+
SortButton: TableSortButton,
|
|
67
|
+
StateRow: TableStateRow
|
|
40
68
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { createToastController, toast, Toast, Toaster, ToastProvider, ToastViewport, useToast } from "./toast.js";
|
|
2
|
-
export type { ToasterPosition, ToasterProps, ToastActionInput, ToastActionProps, ToastCloseProps, ToastController, ToastInput, ToastOptions, ToastPlacement, ToastProviderProps, ToastProps, ToastTextProps, ToastTone, ToastViewportProps } from "./toast.js";
|
|
2
|
+
export type { ToasterPosition, ToasterProps, ToastActionInput, ToastActionProps, ToastCloseProps, ToastController, ToastInput, ToastOptions, ToastPlacement, ToastPromiseMessages, ToastPromiseOptions, ToastProviderProps, ToastProps, ToastTextProps, ToastTone, ToastUpdateOptions, ToastUpdateType, ToastViewportProps } from "./toast.js";
|
|
@@ -17,12 +17,30 @@ export type ToastOptions = {
|
|
|
17
17
|
action?: ToastActionInput;
|
|
18
18
|
cancel?: ToastActionInput;
|
|
19
19
|
};
|
|
20
|
+
export type ToastUpdateType = "default" | "success" | "error" | "warning" | "info";
|
|
21
|
+
export type ToastUpdateOptions = Omit<ToastOptions, "id"> & {
|
|
22
|
+
type?: ToastUpdateType;
|
|
23
|
+
message?: ToastInput;
|
|
24
|
+
};
|
|
25
|
+
export type ToastPromiseMessages<T> = {
|
|
26
|
+
loading: ToastInput;
|
|
27
|
+
success: ToastInput | ((value: T) => ToastInput);
|
|
28
|
+
error: ToastInput | ((error: unknown) => ToastInput);
|
|
29
|
+
};
|
|
30
|
+
export type ToastPromiseOptions<T> = ToastPromiseMessages<T> & {
|
|
31
|
+
loadingOptions?: ToastOptions;
|
|
32
|
+
successOptions?: Omit<ToastUpdateOptions, "message" | "type">;
|
|
33
|
+
errorOptions?: Omit<ToastUpdateOptions, "message" | "type">;
|
|
34
|
+
};
|
|
20
35
|
export type ToastController = {
|
|
21
36
|
(input: ToastInput, options?: ToastOptions): string;
|
|
22
37
|
success: (input: ToastInput, options?: ToastOptions) => string;
|
|
23
38
|
error: (input: ToastInput, options?: ToastOptions) => string;
|
|
24
39
|
warning: (input: ToastInput, options?: ToastOptions) => string;
|
|
25
40
|
info: (input: ToastInput, options?: ToastOptions) => string;
|
|
41
|
+
loading: (input: ToastInput, options?: ToastOptions) => string;
|
|
42
|
+
update: (id: string, options: ToastUpdateOptions) => string;
|
|
43
|
+
promise: <T>(promise: Promise<T>, options: ToastPromiseOptions<T>) => Promise<T>;
|
|
26
44
|
dismiss: (id?: string) => void;
|
|
27
45
|
};
|
|
28
46
|
export interface ToastViewportProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -45,6 +45,18 @@ function toastTypeFromTone(tone) {
|
|
|
45
45
|
}
|
|
46
46
|
return tone;
|
|
47
47
|
}
|
|
48
|
+
function toastToneFromUpdateType(type) {
|
|
49
|
+
if (type === "success" || type === "warning" || type === "info") {
|
|
50
|
+
return type;
|
|
51
|
+
}
|
|
52
|
+
if (type === "error") {
|
|
53
|
+
return "destructive";
|
|
54
|
+
}
|
|
55
|
+
return "neutral";
|
|
56
|
+
}
|
|
57
|
+
function resolveToastPromiseMessage(message, value) {
|
|
58
|
+
return typeof message === "function" ? message(value) : message;
|
|
59
|
+
}
|
|
48
60
|
function isToastActionObject(input) {
|
|
49
61
|
return typeof input === "object" && input !== null && !React.isValidElement(input) && "label" in input && "onClick" in input;
|
|
50
62
|
}
|
|
@@ -89,6 +101,54 @@ export function createToastController() {
|
|
|
89
101
|
controller.error = (input, options) => show("destructive", input, options);
|
|
90
102
|
controller.warning = (input, options) => show("warning", input, options);
|
|
91
103
|
controller.info = (input, options) => show("info", input, options);
|
|
104
|
+
controller.loading = (input, options) => show("neutral", input, { ...options, duration: options?.duration ?? Infinity });
|
|
105
|
+
controller.update = (id, options) => {
|
|
106
|
+
const existingRecord = records.find((record) => record.id === id);
|
|
107
|
+
if (!existingRecord) {
|
|
108
|
+
return id;
|
|
109
|
+
}
|
|
110
|
+
const { message, type, ...toastOptions } = options;
|
|
111
|
+
const { title, description } = message === undefined
|
|
112
|
+
? {
|
|
113
|
+
title: existingRecord.title,
|
|
114
|
+
description: toastOptions.description ?? existingRecord.description
|
|
115
|
+
}
|
|
116
|
+
: resolveToastContent(message, toastOptions);
|
|
117
|
+
toastVersionCounter += 1;
|
|
118
|
+
const nextRecord = {
|
|
119
|
+
id,
|
|
120
|
+
tone: type === undefined ? existingRecord.tone : toastToneFromUpdateType(type),
|
|
121
|
+
duration: toastOptions.duration ?? DEFAULT_TOAST_DURATION,
|
|
122
|
+
version: toastVersionCounter,
|
|
123
|
+
...(title !== undefined ? { title } : {}),
|
|
124
|
+
...(description !== undefined ? { description } : {}),
|
|
125
|
+
...(toastOptions.action !== undefined ? { action: toastOptions.action } : {}),
|
|
126
|
+
...(toastOptions.cancel !== undefined ? { cancel: toastOptions.cancel } : {})
|
|
127
|
+
};
|
|
128
|
+
records = records.map((record) => (record.id === id ? nextRecord : record));
|
|
129
|
+
notify();
|
|
130
|
+
return id;
|
|
131
|
+
};
|
|
132
|
+
controller.promise = async (promise, options) => {
|
|
133
|
+
const id = controller.loading(options.loading, options.loadingOptions);
|
|
134
|
+
try {
|
|
135
|
+
const value = await promise;
|
|
136
|
+
controller.update(id, {
|
|
137
|
+
...options.successOptions,
|
|
138
|
+
type: "success",
|
|
139
|
+
message: resolveToastPromiseMessage(options.success, value)
|
|
140
|
+
});
|
|
141
|
+
return value;
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
controller.update(id, {
|
|
145
|
+
...options.errorOptions,
|
|
146
|
+
type: "error",
|
|
147
|
+
message: resolveToastPromiseMessage(options.error, error)
|
|
148
|
+
});
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
92
152
|
controller.dismiss = (id) => {
|
|
93
153
|
records = id ? records.filter((toast) => toast.id !== id) : [];
|
|
94
154
|
notify();
|
|
@@ -41,7 +41,7 @@ export interface TooltipContentProps extends React.HTMLAttributes<HTMLSpanElemen
|
|
|
41
41
|
declare function TooltipProvider({ children, delayDuration }: TooltipProviderProps): React.JSX.Element;
|
|
42
42
|
declare function TooltipRoot({ children, defaultOpen, delay, disabled, onOpenChange, open: openProp, placement }: TooltipRootProps): React.JSX.Element;
|
|
43
43
|
declare function TooltipTrigger({ children, asChild: _asChild }: TooltipTriggerProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
44
|
-
declare function TooltipContent({ align, alignOffset, children, className, collisionPadding, container, forceMount, placement, ref, side, sideOffset, style, ...props }: TooltipContentProps): React.JSX.Element | null;
|
|
44
|
+
declare function TooltipContent({ align, alignOffset, children, className, collisionPadding, container, forceMount, onAnimationEnd, placement, ref, side, sideOffset, style, ...props }: TooltipContentProps): React.JSX.Element | null;
|
|
45
45
|
declare function TooltipWrapper({ children, content, delay, disabled, placement }: TooltipProps): React.JSX.Element;
|
|
46
46
|
export declare const Tooltip: typeof TooltipWrapper & {
|
|
47
47
|
Content: typeof TooltipContent;
|