@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.
Files changed (53) hide show
  1. package/dist/components/alert-dialog/alert-dialog.d.ts +8 -5
  2. package/dist/components/alert-dialog/alert-dialog.js +21 -8
  3. package/dist/components/button/button.d.ts +3 -2
  4. package/dist/components/button/button.js +5 -1
  5. package/dist/components/calendar/calendar.d.ts +51 -0
  6. package/dist/components/calendar/calendar.js +190 -0
  7. package/dist/components/calendar/index.d.ts +2 -0
  8. package/dist/components/calendar/index.js +2 -0
  9. package/dist/components/carousel/carousel.d.ts +51 -0
  10. package/dist/components/carousel/carousel.js +210 -0
  11. package/dist/components/carousel/index.d.ts +2 -0
  12. package/dist/components/carousel/index.js +2 -0
  13. package/dist/components/collapsible/collapsible.d.ts +3 -2
  14. package/dist/components/collapsible/collapsible.js +5 -1
  15. package/dist/components/command/command.d.ts +11 -4
  16. package/dist/components/command/command.js +27 -16
  17. package/dist/components/command/index.d.ts +1 -1
  18. package/dist/components/context-menu/context-menu.d.ts +17 -6
  19. package/dist/components/context-menu/context-menu.js +139 -36
  20. package/dist/components/date-picker/date-picker.d.ts +16 -0
  21. package/dist/components/date-picker/date-picker.js +50 -0
  22. package/dist/components/date-picker/index.d.ts +2 -0
  23. package/dist/components/date-picker/index.js +2 -0
  24. package/dist/components/dialog/dialog.d.ts +6 -4
  25. package/dist/components/dialog/dialog.js +14 -4
  26. package/dist/components/dropdown-menu/dropdown-menu.d.ts +13 -7
  27. package/dist/components/dropdown-menu/dropdown-menu.js +127 -72
  28. package/dist/components/hover-card/hover-card.d.ts +37 -0
  29. package/dist/components/hover-card/hover-card.js +271 -0
  30. package/dist/components/hover-card/index.d.ts +2 -0
  31. package/dist/components/hover-card/index.js +2 -0
  32. package/dist/components/index.d.ts +14 -6
  33. package/dist/components/index.js +5 -1
  34. package/dist/components/menubar/menubar.d.ts +24 -5
  35. package/dist/components/menubar/menubar.js +182 -33
  36. package/dist/components/popover/popover.d.ts +9 -3
  37. package/dist/components/popover/popover.js +15 -5
  38. package/dist/components/sheet/sheet.d.ts +6 -4
  39. package/dist/components/sheet/sheet.js +21 -8
  40. package/dist/components/toast/index.d.ts +2 -2
  41. package/dist/components/toast/index.js +2 -1
  42. package/dist/components/toast/toast.d.ts +40 -0
  43. package/dist/components/toast/toast.js +144 -2
  44. package/dist/components/tooltip/tooltip.d.ts +8 -2
  45. package/dist/components/tooltip/tooltip.js +8 -5
  46. package/dist/internal/floating/placement-aliases.d.ts +7 -0
  47. package/dist/internal/floating/placement-aliases.js +13 -0
  48. package/dist/internal/slot/index.d.ts +2 -0
  49. package/dist/internal/slot/index.js +1 -0
  50. package/dist/internal/slot/slot.d.ts +6 -0
  51. package/dist/internal/slot/slot.js +53 -0
  52. package/dist/styles.css +356 -4
  53. 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
- return (_jsx("div", { ...props, ref: ref, className: cn("liano-toast", className), "data-tone": tone, role: role ?? (tone === "destructive" ? "alert" : "status") }));
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,2 @@
1
+ export { Slot } from "./slot.js";
2
+ export type { SlotProps } from "./slot.js";
@@ -0,0 +1 @@
1
+ export { Slot } from "./slot.js";
@@ -0,0 +1,6 @@
1
+ import * as React from "react";
2
+ export interface SlotProps extends React.HTMLAttributes<HTMLElement> {
3
+ children: React.ReactElement;
4
+ ref?: React.Ref<HTMLElement>;
5
+ }
6
+ export declare function Slot({ children, ref, ...props }: SlotProps): React.ReactElement;
@@ -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
+ }