@zvk/ui 0.1.2 → 0.1.5
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/dist/components/alert-dialog/alert-dialog.d.ts +8 -5
- package/dist/components/alert-dialog/alert-dialog.js +21 -8
- package/dist/components/button/button.d.ts +3 -2
- package/dist/components/button/button.js +5 -1
- package/dist/components/calendar/calendar.d.ts +51 -0
- package/dist/components/calendar/calendar.js +190 -0
- package/dist/components/calendar/index.d.ts +2 -0
- package/dist/components/calendar/index.js +2 -0
- package/dist/components/carousel/carousel.d.ts +51 -0
- package/dist/components/carousel/carousel.js +210 -0
- package/dist/components/carousel/index.d.ts +2 -0
- package/dist/components/carousel/index.js +2 -0
- package/dist/components/collapsible/collapsible.d.ts +3 -2
- package/dist/components/collapsible/collapsible.js +5 -1
- package/dist/components/command/command.d.ts +11 -4
- package/dist/components/command/command.js +27 -16
- package/dist/components/command/index.d.ts +1 -1
- package/dist/components/context-menu/context-menu.d.ts +17 -6
- package/dist/components/context-menu/context-menu.js +139 -36
- package/dist/components/date-picker/date-picker.d.ts +16 -0
- package/dist/components/date-picker/date-picker.js +50 -0
- package/dist/components/date-picker/index.d.ts +2 -0
- package/dist/components/date-picker/index.js +2 -0
- package/dist/components/dialog/dialog.d.ts +6 -4
- package/dist/components/dialog/dialog.js +14 -4
- package/dist/components/dropdown-menu/dropdown-menu.d.ts +13 -7
- package/dist/components/dropdown-menu/dropdown-menu.js +127 -72
- package/dist/components/hover-card/hover-card.d.ts +37 -0
- package/dist/components/hover-card/hover-card.js +271 -0
- package/dist/components/hover-card/index.d.ts +2 -0
- package/dist/components/hover-card/index.js +2 -0
- package/dist/components/index.d.ts +14 -6
- package/dist/components/index.js +5 -1
- package/dist/components/menubar/menubar.d.ts +24 -5
- package/dist/components/menubar/menubar.js +182 -33
- package/dist/components/popover/popover.d.ts +9 -3
- package/dist/components/popover/popover.js +15 -5
- package/dist/components/sheet/sheet.d.ts +6 -4
- package/dist/components/sheet/sheet.js +21 -8
- package/dist/components/toast/index.d.ts +2 -2
- package/dist/components/toast/index.js +2 -1
- package/dist/components/toast/toast.d.ts +40 -0
- package/dist/components/toast/toast.js +144 -2
- package/dist/components/tooltip/tooltip.d.ts +8 -2
- package/dist/components/tooltip/tooltip.js +8 -5
- package/dist/internal/floating/placement-aliases.d.ts +7 -0
- package/dist/internal/floating/placement-aliases.js +13 -0
- package/dist/internal/slot/index.d.ts +2 -0
- package/dist/internal/slot/index.js +1 -0
- package/dist/internal/slot/slot.d.ts +6 -0
- package/dist/internal/slot/slot.js +53 -0
- package/dist/styles.css +356 -4
- package/package.json +18 -2
|
@@ -1,11 +1,153 @@
|
|
|
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
3
|
import { cn } from "../../utils/cn.js";
|
|
4
|
+
const DEFAULT_TOAST_DURATION = 5000;
|
|
5
|
+
const toastControllerStores = new WeakMap();
|
|
6
|
+
const ToastControllerContext = React.createContext(null);
|
|
7
|
+
const emptyToastRecords = [];
|
|
8
|
+
const emptyToastStore = {
|
|
9
|
+
getSnapshot: () => emptyToastRecords,
|
|
10
|
+
subscribe: () => () => undefined
|
|
11
|
+
};
|
|
12
|
+
let toastIdCounter = 0;
|
|
13
|
+
let toastVersionCounter = 0;
|
|
14
|
+
function createToastId() {
|
|
15
|
+
toastIdCounter += 1;
|
|
16
|
+
return `toast-${toastIdCounter}`;
|
|
17
|
+
}
|
|
18
|
+
function resolveToastContent(input, options) {
|
|
19
|
+
if (typeof input === "string") {
|
|
20
|
+
return {
|
|
21
|
+
title: input,
|
|
22
|
+
description: options.description
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
title: input.title,
|
|
27
|
+
description: options.description ?? input.description
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function mapToPlacement(position) {
|
|
31
|
+
if (position === "top-center") {
|
|
32
|
+
return "top-right";
|
|
33
|
+
}
|
|
34
|
+
if (position === "bottom-center") {
|
|
35
|
+
return "bottom-right";
|
|
36
|
+
}
|
|
37
|
+
return position;
|
|
38
|
+
}
|
|
39
|
+
function toastTypeFromTone(tone) {
|
|
40
|
+
if (tone === "destructive") {
|
|
41
|
+
return "error";
|
|
42
|
+
}
|
|
43
|
+
if (tone === "neutral") {
|
|
44
|
+
return "default";
|
|
45
|
+
}
|
|
46
|
+
return tone;
|
|
47
|
+
}
|
|
48
|
+
function isToastActionObject(input) {
|
|
49
|
+
return typeof input === "object" && input !== null && !React.isValidElement(input) && "label" in input && "onClick" in input;
|
|
50
|
+
}
|
|
51
|
+
function renderToastControl(input) {
|
|
52
|
+
if (input === undefined) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (isToastActionObject(input)) {
|
|
56
|
+
return _jsx(Toast.Action, { onClick: input.onClick, children: input.label });
|
|
57
|
+
}
|
|
58
|
+
return input;
|
|
59
|
+
}
|
|
4
60
|
export function ToastViewport({ className, placement = "top-right", ref, ...props }) {
|
|
5
61
|
return (_jsx("div", { ...props, ref: ref, "aria-label": props["aria-label"] ?? "Notifications", className: cn("liano-toast-viewport", className), "data-placement": placement, role: "region" }));
|
|
6
62
|
}
|
|
63
|
+
export function createToastController() {
|
|
64
|
+
const listeners = new Set();
|
|
65
|
+
let records = [];
|
|
66
|
+
function notify() {
|
|
67
|
+
listeners.forEach((listener) => listener());
|
|
68
|
+
}
|
|
69
|
+
function show(tone, input, options = {}) {
|
|
70
|
+
const id = options.id ?? createToastId();
|
|
71
|
+
const { title, description } = resolveToastContent(input, options);
|
|
72
|
+
toastVersionCounter += 1;
|
|
73
|
+
const toast = {
|
|
74
|
+
id,
|
|
75
|
+
tone,
|
|
76
|
+
duration: options.duration ?? DEFAULT_TOAST_DURATION,
|
|
77
|
+
version: toastVersionCounter,
|
|
78
|
+
...(title !== undefined ? { title } : {}),
|
|
79
|
+
...(description !== undefined ? { description } : {}),
|
|
80
|
+
...(options.action !== undefined ? { action: options.action } : {}),
|
|
81
|
+
...(options.cancel !== undefined ? { cancel: options.cancel } : {})
|
|
82
|
+
};
|
|
83
|
+
records = [...records.filter((record) => record.id !== id), toast];
|
|
84
|
+
notify();
|
|
85
|
+
return id;
|
|
86
|
+
}
|
|
87
|
+
const controller = ((input, options) => show("neutral", input, options));
|
|
88
|
+
controller.success = (input, options) => show("success", input, options);
|
|
89
|
+
controller.error = (input, options) => show("destructive", input, options);
|
|
90
|
+
controller.warning = (input, options) => show("warning", input, options);
|
|
91
|
+
controller.info = (input, options) => show("info", input, options);
|
|
92
|
+
controller.dismiss = (id) => {
|
|
93
|
+
records = id ? records.filter((toast) => toast.id !== id) : [];
|
|
94
|
+
notify();
|
|
95
|
+
};
|
|
96
|
+
toastControllerStores.set(controller, {
|
|
97
|
+
getSnapshot() {
|
|
98
|
+
return records;
|
|
99
|
+
},
|
|
100
|
+
subscribe(listener) {
|
|
101
|
+
listeners.add(listener);
|
|
102
|
+
return () => {
|
|
103
|
+
listeners.delete(listener);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
return controller;
|
|
108
|
+
}
|
|
109
|
+
export const toast = createToastController();
|
|
110
|
+
export function useToast() {
|
|
111
|
+
const controller = React.useContext(ToastControllerContext);
|
|
112
|
+
if (!controller) {
|
|
113
|
+
throw new Error("useToast must be used within a ToastProvider.");
|
|
114
|
+
}
|
|
115
|
+
return controller;
|
|
116
|
+
}
|
|
117
|
+
export function ToastProvider({ children, controller, placement = "top-right", ref, ...viewportProps }) {
|
|
118
|
+
const [localController] = React.useState(() => createToastController());
|
|
119
|
+
const activeController = controller ?? localController;
|
|
120
|
+
const store = toastControllerStores.get(activeController) ?? emptyToastStore;
|
|
121
|
+
const toasts = React.useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
122
|
+
const dismissToast = React.useCallback((id) => {
|
|
123
|
+
activeController.dismiss(id);
|
|
124
|
+
}, [activeController]);
|
|
125
|
+
const viewportRefProps = ref ? { ref } : {};
|
|
126
|
+
return (_jsxs(ToastControllerContext.Provider, { value: activeController, children: [children, _jsx(ToastViewport, { ...viewportProps, ...viewportRefProps, placement: placement, children: toasts.map((toast) => (_jsx(ToastProviderItem, { toast: toast, onDismiss: dismissToast }, toast.id))) })] }));
|
|
127
|
+
}
|
|
128
|
+
export function Toaster({ controller = toast, expand: _expand, offset, position = "top-right", richColors: _richColors, style, toastOptions: _toastOptions, ...props }) {
|
|
129
|
+
const placement = mapToPlacement(position);
|
|
130
|
+
const offsetStyle = offset === undefined
|
|
131
|
+
? style
|
|
132
|
+
: {
|
|
133
|
+
...style,
|
|
134
|
+
"--liano-toast-viewport-inset": typeof offset === "number" ? `${offset}px` : offset
|
|
135
|
+
};
|
|
136
|
+
return (_jsx(ToastProvider, { ...props, controller: controller, placement: placement, ...(offsetStyle === undefined ? {} : { style: offsetStyle }) }));
|
|
137
|
+
}
|
|
138
|
+
function ToastProviderItem({ onDismiss, toast }) {
|
|
139
|
+
React.useEffect(() => {
|
|
140
|
+
if (toast.duration === Infinity) {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
const timeout = window.setTimeout(() => onDismiss(toast.id), toast.duration);
|
|
144
|
+
return () => window.clearTimeout(timeout);
|
|
145
|
+
}, [onDismiss, toast.duration, toast.id, toast.version]);
|
|
146
|
+
return (_jsxs(Toast, { tone: toast.tone, children: [(toast.title || toast.description) && (_jsxs("div", { className: "liano-toast__content", children: [toast.title && _jsx(Toast.Title, { children: toast.title }), toast.description && _jsx(Toast.Description, { children: toast.description })] })), (toast.action || toast.cancel) && (_jsxs("div", { className: "liano-toast__controls", children: [renderToastControl(toast.action), renderToastControl(toast.cancel)] })), _jsx(Toast.Close, { "aria-label": "Dismiss notification", onClick: () => onDismiss(toast.id) })] }));
|
|
147
|
+
}
|
|
7
148
|
function ToastRoot({ className, ref, role, tone = "neutral", ...props }) {
|
|
8
|
-
|
|
149
|
+
const toastType = toastTypeFromTone(tone);
|
|
150
|
+
return (_jsx("div", { ...props, ref: ref, className: cn("liano-toast", className), "data-sonner-toast": "", "data-tone": tone, "data-type": toastType, "data-visible": "true", role: role ?? (tone === "destructive" ? "alert" : "status") }));
|
|
9
151
|
}
|
|
10
152
|
function ToastTitle({ className, ref, ...props }) {
|
|
11
153
|
return _jsx("div", { ...props, ref: ref, className: cn("liano-toast__title", className) });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { PortalProps } from "../../internal/portal/index.js";
|
|
3
3
|
import type { FloatingPlacement } from "../../internal/floating/index.js";
|
|
4
|
+
import type { FloatingAlign, FloatingSide } from "../../internal/floating/placement-aliases.js";
|
|
4
5
|
export interface TooltipProps {
|
|
5
6
|
children: React.ReactElement;
|
|
6
7
|
content: React.ReactNode;
|
|
@@ -23,9 +24,14 @@ export interface TooltipRootProps {
|
|
|
23
24
|
onOpenChange?: (open: boolean) => void;
|
|
24
25
|
}
|
|
25
26
|
export interface TooltipTriggerProps {
|
|
27
|
+
asChild?: boolean;
|
|
26
28
|
children: React.ReactElement;
|
|
27
29
|
}
|
|
28
30
|
export interface TooltipContentProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
31
|
+
placement?: FloatingPlacement;
|
|
32
|
+
side?: FloatingSide;
|
|
33
|
+
align?: FloatingAlign;
|
|
34
|
+
alignOffset?: number;
|
|
29
35
|
sideOffset?: number;
|
|
30
36
|
collisionPadding?: number;
|
|
31
37
|
forceMount?: boolean;
|
|
@@ -34,8 +40,8 @@ export interface TooltipContentProps extends React.HTMLAttributes<HTMLSpanElemen
|
|
|
34
40
|
}
|
|
35
41
|
declare function TooltipProvider({ children, delayDuration }: TooltipProviderProps): React.JSX.Element;
|
|
36
42
|
declare function TooltipRoot({ children, defaultOpen, delay, disabled, onOpenChange, open: openProp, placement }: TooltipRootProps): React.JSX.Element;
|
|
37
|
-
declare function TooltipTrigger({ children }: TooltipTriggerProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
38
|
-
declare function TooltipContent({ children, className, collisionPadding, container, forceMount, ref, sideOffset, style, ...props }: TooltipContentProps): React.JSX.Element | null;
|
|
43
|
+
declare function TooltipTrigger({ children, asChild: _asChild }: TooltipTriggerProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
44
|
+
declare function TooltipContent({ align, alignOffset: _alignOffset, children, className, collisionPadding, container, forceMount, placement, ref, side, sideOffset, style, ...props }: TooltipContentProps): React.JSX.Element | null;
|
|
39
45
|
declare function TooltipWrapper({ children, content, delay, disabled, placement }: TooltipProps): React.JSX.Element;
|
|
40
46
|
export declare const Tooltip: typeof TooltipWrapper & {
|
|
41
47
|
Content: typeof TooltipContent;
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Portal } from "../../internal/portal/index.js";
|
|
5
5
|
import { useFloatingPosition } from "../../internal/floating/index.js";
|
|
6
|
+
import { placementFromSideAlign } from "../../internal/floating/placement-aliases.js";
|
|
6
7
|
import { useControllableState } from "../../hooks/use-controllable-state.js";
|
|
7
8
|
import { composeEventHandlers } from "../../utils/compose-event-handlers.js";
|
|
8
9
|
import { cn } from "../../utils/cn.js";
|
|
@@ -27,7 +28,7 @@ function composeRefs(...refs) {
|
|
|
27
28
|
if (typeof ref === "function") {
|
|
28
29
|
ref(node);
|
|
29
30
|
}
|
|
30
|
-
else if (ref !== undefined) {
|
|
31
|
+
else if (ref !== undefined && ref !== null) {
|
|
31
32
|
ref.current = node;
|
|
32
33
|
}
|
|
33
34
|
}
|
|
@@ -59,7 +60,7 @@ function TooltipRoot({ children, defaultOpen = false, delay, disabled = false, o
|
|
|
59
60
|
const openTimeoutRef = React.useRef(null);
|
|
60
61
|
const { floatingRef, floatingStyle, placement: resolvedPlacement, referenceRef, updatePosition } = useFloatingPosition({
|
|
61
62
|
open,
|
|
62
|
-
placement,
|
|
63
|
+
placement: contentPositioning.placement ?? placement,
|
|
63
64
|
offset: contentPositioning.sideOffset,
|
|
64
65
|
collisionPadding: contentPositioning.collisionPadding,
|
|
65
66
|
flip: true,
|
|
@@ -137,7 +138,7 @@ function TooltipRoot({ children, defaultOpen = false, delay, disabled = false, o
|
|
|
137
138
|
]);
|
|
138
139
|
return (_jsx(TooltipRootContext.Provider, { value: contextValue, children: children }));
|
|
139
140
|
}
|
|
140
|
-
function TooltipTrigger({ children }) {
|
|
141
|
+
function TooltipTrigger({ children, asChild: _asChild = false }) {
|
|
141
142
|
const context = useTooltipRootContext("Tooltip.Trigger");
|
|
142
143
|
if (!React.isValidElement(children) || context.disabled) {
|
|
143
144
|
return children;
|
|
@@ -164,15 +165,17 @@ function TooltipTrigger({ children }) {
|
|
|
164
165
|
}, { checkDefaultPrevented: false })
|
|
165
166
|
});
|
|
166
167
|
}
|
|
167
|
-
function TooltipContent({ children, className, collisionPadding = defaultContentPositioning.collisionPadding, container, forceMount = false, ref, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
|
|
168
|
+
function TooltipContent({ align, alignOffset: _alignOffset, children, className, collisionPadding = defaultContentPositioning.collisionPadding, container, forceMount = false, placement, ref, side, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
|
|
168
169
|
const context = useTooltipRootContext("Tooltip.Content");
|
|
169
170
|
React.useEffect(() => {
|
|
171
|
+
const resolvedPlacement = side === undefined ? placement : placementFromSideAlign(side, align, placement ?? "bottom");
|
|
170
172
|
context.setContentPositioning({
|
|
173
|
+
...(resolvedPlacement === undefined ? {} : { placement: resolvedPlacement }),
|
|
171
174
|
sideOffset,
|
|
172
175
|
collisionPadding
|
|
173
176
|
});
|
|
174
177
|
return () => context.setContentPositioning(defaultContentPositioning);
|
|
175
|
-
}, [collisionPadding, context, sideOffset]);
|
|
178
|
+
}, [align, collisionPadding, context, placement, side, sideOffset]);
|
|
176
179
|
if (!context.open && !forceMount) {
|
|
177
180
|
return null;
|
|
178
181
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FloatingAlign, FloatingPlacement, FloatingSide } from "./floating-types.js";
|
|
2
|
+
export type { FloatingAlign, FloatingSide } from "./floating-types.js";
|
|
3
|
+
export declare function placementFromSideAlign(side: FloatingSide | undefined, align: FloatingAlign | undefined, fallback: FloatingPlacement): FloatingPlacement;
|
|
4
|
+
export declare function placementParts(placement: FloatingPlacement): {
|
|
5
|
+
side: FloatingSide;
|
|
6
|
+
align: FloatingAlign;
|
|
7
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function placementFromSideAlign(side, align, fallback) {
|
|
2
|
+
if (side === undefined) {
|
|
3
|
+
return fallback;
|
|
4
|
+
}
|
|
5
|
+
if (align === undefined || align === "center") {
|
|
6
|
+
return side;
|
|
7
|
+
}
|
|
8
|
+
return `${side}-${align}`;
|
|
9
|
+
}
|
|
10
|
+
export function placementParts(placement) {
|
|
11
|
+
const [side, align = "center"] = placement.split("-");
|
|
12
|
+
return { side, align };
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Slot } from "./slot.js";
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../../utils/cn.js";
|
|
3
|
+
function composeRefs(...refs) {
|
|
4
|
+
return (node) => {
|
|
5
|
+
for (const ref of refs) {
|
|
6
|
+
if (typeof ref === "function") {
|
|
7
|
+
ref(node);
|
|
8
|
+
}
|
|
9
|
+
else if (ref !== undefined && ref !== null) {
|
|
10
|
+
ref.current = node;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function isEventHandler(key, value) {
|
|
16
|
+
return /^on[A-Z]/.test(key) && typeof value === "function";
|
|
17
|
+
}
|
|
18
|
+
function mergeProps(slotProps, childProps) {
|
|
19
|
+
const merged = { ...slotProps, ...childProps };
|
|
20
|
+
for (const key of Object.keys(slotProps)) {
|
|
21
|
+
const slotValue = slotProps[key];
|
|
22
|
+
const childValue = childProps[key];
|
|
23
|
+
if (key === "className") {
|
|
24
|
+
merged[key] = cn(slotValue, childValue);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (key === "style" && typeof slotValue === "object" && typeof childValue === "object") {
|
|
28
|
+
merged[key] = { ...slotValue, ...childValue };
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (isEventHandler(key, slotValue) && isEventHandler(key, childValue)) {
|
|
32
|
+
merged[key] = (event) => {
|
|
33
|
+
childValue(event);
|
|
34
|
+
if (!event.defaultPrevented) {
|
|
35
|
+
slotValue(event);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return merged;
|
|
41
|
+
}
|
|
42
|
+
export function Slot({ children, ref, ...props }) {
|
|
43
|
+
if (!React.isValidElement(children)) {
|
|
44
|
+
throw new Error("Slot requires a single valid React element child.");
|
|
45
|
+
}
|
|
46
|
+
const child = children;
|
|
47
|
+
const childRef = child.props.ref;
|
|
48
|
+
const mergedProps = mergeProps(props, child.props);
|
|
49
|
+
return React.cloneElement(child, {
|
|
50
|
+
...mergedProps,
|
|
51
|
+
ref: composeRefs(childRef, ref)
|
|
52
|
+
});
|
|
53
|
+
}
|