@zvk/ui 0.1.3 → 0.1.6

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 (108) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +5 -5
  3. package/dist/components/accordion/accordion.js +4 -4
  4. package/dist/components/alert/alert.d.ts +5 -0
  5. package/dist/components/alert/alert.js +8 -4
  6. package/dist/components/alert/index.d.ts +1 -1
  7. package/dist/components/alert-dialog/alert-dialog.d.ts +8 -5
  8. package/dist/components/alert-dialog/alert-dialog.js +26 -13
  9. package/dist/components/avatar/avatar.js +1 -1
  10. package/dist/components/badge/badge.js +1 -1
  11. package/dist/components/breadcrumbs/breadcrumbs.js +3 -3
  12. package/dist/components/button/button.d.ts +3 -2
  13. package/dist/components/button/button.js +50 -2
  14. package/dist/components/calendar/calendar.d.ts +40 -5
  15. package/dist/components/calendar/calendar.js +17 -5
  16. package/dist/components/calendar/index.d.ts +1 -1
  17. package/dist/components/card/card.d.ts +5 -0
  18. package/dist/components/card/card.js +11 -7
  19. package/dist/components/card/index.d.ts +1 -1
  20. package/dist/components/carousel/carousel.js +7 -7
  21. package/dist/components/checkbox/checkbox.js +4 -4
  22. package/dist/components/code-block/code-block.js +2 -2
  23. package/dist/components/collapsible/collapsible.d.ts +3 -2
  24. package/dist/components/collapsible/collapsible.js +8 -4
  25. package/dist/components/combobox/combobox.js +6 -5
  26. package/dist/components/command/command-filter.d.ts +0 -1
  27. package/dist/components/command/command-filter.js +0 -3
  28. package/dist/components/command/command.d.ts +11 -4
  29. package/dist/components/command/command.js +28 -17
  30. package/dist/components/command/index.d.ts +1 -1
  31. package/dist/components/context-menu/context-menu.d.ts +17 -6
  32. package/dist/components/context-menu/context-menu.js +142 -39
  33. package/dist/components/conversation/conversation.js +11 -11
  34. package/dist/components/copy-button/copy-button.js +2 -2
  35. package/dist/components/date-picker/date-picker.js +1 -1
  36. package/dist/components/dialog/dialog.d.ts +6 -4
  37. package/dist/components/dialog/dialog.js +22 -12
  38. package/dist/components/dropdown-menu/dropdown-menu.d.ts +13 -7
  39. package/dist/components/dropdown-menu/dropdown-menu.js +137 -78
  40. package/dist/components/empty-state/empty-state.js +1 -1
  41. package/dist/components/error-boundary/error-boundary.js +1 -1
  42. package/dist/components/field/field.js +4 -4
  43. package/dist/components/file-upload-input/file-upload-input.js +2 -2
  44. package/dist/components/form/form.js +6 -6
  45. package/dist/components/hover-card/hover-card.d.ts +8 -2
  46. package/dist/components/hover-card/hover-card.js +13 -7
  47. package/dist/components/icon-button/icon-button.js +19 -1
  48. package/dist/components/index.d.ts +10 -10
  49. package/dist/components/index.js +1 -1
  50. package/dist/components/input/input.js +1 -1
  51. package/dist/components/label/label.js +1 -1
  52. package/dist/components/menubar/menubar.d.ts +24 -5
  53. package/dist/components/menubar/menubar.js +186 -37
  54. package/dist/components/pagination/pagination.js +12 -12
  55. package/dist/components/popover/popover.d.ts +8 -3
  56. package/dist/components/popover/popover.js +40 -8
  57. package/dist/components/progress/progress.js +3 -3
  58. package/dist/components/radio-group/radio-group.js +3 -3
  59. package/dist/components/responsive-container/responsive-container.js +1 -1
  60. package/dist/components/scroll-area/scroll-area.js +4 -4
  61. package/dist/components/sectioned-sidebar-nav/sectioned-sidebar-nav.js +7 -7
  62. package/dist/components/select/select.js +66 -13
  63. package/dist/components/separator/separator.js +1 -1
  64. package/dist/components/sheet/sheet.d.ts +6 -4
  65. package/dist/components/sheet/sheet.js +29 -16
  66. package/dist/components/sidebar-shell/sidebar-shell.js +6 -6
  67. package/dist/components/skeleton/skeleton.js +1 -1
  68. package/dist/components/slider/slider.js +1 -1
  69. package/dist/components/spinner/spinner.js +1 -1
  70. package/dist/components/stat/stat.js +1 -1
  71. package/dist/components/switch/switch.js +3 -3
  72. package/dist/components/table/index.d.ts +1 -1
  73. package/dist/components/table/table.d.ts +5 -0
  74. package/dist/components/table/table.js +12 -8
  75. package/dist/components/tabs/tabs.js +4 -4
  76. package/dist/components/tabs-with-sidebar/tabs-with-sidebar.js +3 -3
  77. package/dist/components/textarea/textarea.js +1 -1
  78. package/dist/components/toast/index.d.ts +2 -2
  79. package/dist/components/toast/index.js +1 -1
  80. package/dist/components/toast/toast.d.ts +16 -2
  81. package/dist/components/toast/toast.js +49 -7
  82. package/dist/components/toggle/toggle.js +1 -1
  83. package/dist/components/toggle-group/toggle-group.js +1 -1
  84. package/dist/components/tooltip/tooltip.d.ts +8 -2
  85. package/dist/components/tooltip/tooltip.js +12 -6
  86. package/dist/hooks/index.d.ts +2 -2
  87. package/dist/hooks/use-composed-refs.d.ts +2 -2
  88. package/dist/hooks/use-controllable-state.d.ts +2 -2
  89. package/dist/internal/floating/compute-position.js +13 -8
  90. package/dist/internal/floating/floating-types.d.ts +1 -0
  91. package/dist/internal/floating/index.d.ts +1 -0
  92. package/dist/internal/floating/placement-aliases.d.ts +7 -0
  93. package/dist/internal/floating/placement-aliases.js +13 -0
  94. package/dist/internal/floating/use-floating-position.js +6 -4
  95. package/dist/internal/overlay-stack/overlay-stack.js +4 -1
  96. package/dist/internal/slot/index.d.ts +2 -0
  97. package/dist/internal/slot/index.js +1 -0
  98. package/dist/internal/slot/slot.d.ts +6 -0
  99. package/dist/internal/slot/slot.js +53 -0
  100. package/dist/styles.css +2079 -2015
  101. package/dist/tokens/index.d.ts +2 -2
  102. package/dist/tokens/index.js +1 -1
  103. package/dist/tokens/token-types.d.ts +5 -5
  104. package/dist/tokens/tokens.d.ts +16 -10
  105. package/dist/tokens/tokens.js +16 -10
  106. package/dist/utils/cn.d.ts +2 -2
  107. package/dist/utils/index.d.ts +1 -1
  108. package/package.json +13 -12
@@ -7,8 +7,11 @@ import { useControllableState } from "../../hooks/use-controllable-state.js";
7
7
  import { Portal } from "../../internal/portal/index.js";
8
8
  import { DismissableLayer } from "../../internal/dismissable-layer/dismissable-layer.js";
9
9
  import { useFloatingPosition } from "../../internal/floating/index.js";
10
+ import { placementFromSideAlign, placementParts } from "../../internal/floating/placement-aliases.js";
11
+ import { Slot } from "../../internal/slot/index.js";
10
12
  const defaultContentPositioning = {
11
13
  sideOffset: 8,
14
+ alignOffset: 0,
12
15
  collisionPadding: 0,
13
16
  matchTriggerWidth: false
14
17
  };
@@ -35,6 +38,18 @@ function composeRefs(...refs) {
35
38
  function filterEnabledItems(items) {
36
39
  return items.filter((item) => !item.disabled && item.ref !== null);
37
40
  }
41
+ function setComposedRef(internalRef, externalRef, node) {
42
+ internalRef.current = node;
43
+ if (typeof externalRef === "function") {
44
+ externalRef(node);
45
+ }
46
+ else if (externalRef !== undefined && externalRef !== null) {
47
+ externalRef.current = node;
48
+ }
49
+ }
50
+ function isActivationKey(key) {
51
+ return key === " " || key === "Space" || key === "Enter" || key === "Spacebar";
52
+ }
38
53
  const DropdownMenuRadioGroupContext = React.createContext(null);
39
54
  function DropdownMenuRoot({ children, className, container, defaultOpen = false, onOpenChange, open: openProp, placement = "bottom", sideOffset = 8, collisionPadding = 0, matchTriggerWidth = false, ...props }) {
40
55
  const [open, setOpen] = useControllableState({
@@ -51,14 +66,16 @@ function DropdownMenuRoot({ children, className, container, defaultOpen = false,
51
66
  const wasOpenRef = React.useRef(false);
52
67
  const [contentPositioning, setContentPositioning] = React.useState({
53
68
  sideOffset,
69
+ alignOffset: defaultContentPositioning.alignOffset,
54
70
  collisionPadding,
55
71
  matchTriggerWidth
56
72
  });
57
73
  const floating = useFloatingPosition({
58
74
  open,
59
- placement,
75
+ placement: contentPositioning.placement ?? placement,
60
76
  strategy: "absolute",
61
77
  offset: contentPositioning.sideOffset,
78
+ alignmentOffset: contentPositioning.alignOffset,
62
79
  collisionPadding: contentPositioning.collisionPadding,
63
80
  matchReferenceWidth: contentPositioning.matchTriggerWidth
64
81
  });
@@ -116,32 +133,37 @@ function DropdownMenuRoot({ children, className, container, defaultOpen = false,
116
133
  triggerRef,
117
134
  referenceRef: floating.referenceRef,
118
135
  floatingRef: floating.floatingRef,
136
+ floatingPlacement: floating.placement,
119
137
  floatingStyle: floating.floatingStyle,
120
138
  getEnabledItems,
121
139
  registerItem,
122
140
  unregisterItem,
123
141
  ...(container === undefined ? {} : { container })
124
- }, children: _jsx("div", { ...props, className: cn("liano-dropdown-menu", className), "data-state": open ? "open" : "closed", children: children }) }));
142
+ }, children: _jsx("div", { ...props, className: cn("zvk-ui-dropdown-menu", className), "data-state": open ? "open" : "closed", children: children }) }));
125
143
  }
126
- function DropdownMenuTrigger({ className, disabled, onClick, ref, type = "button", ...props }) {
144
+ function DropdownMenuTrigger({ asChild = false, className, disabled, onClick, ref, type = "button", ...props }) {
127
145
  const { contentId, open, setOpen, triggerRef, triggerId, referenceRef, getEnabledItems } = useDropdownMenuContext("DropdownMenu.Trigger");
128
- return (_jsx("button", { ...props, ref: composeRefs(ref, triggerRef, referenceRef), type: type, disabled: disabled, id: triggerId, role: "button", "aria-haspopup": "menu", "aria-expanded": open ? "true" : "false", "aria-controls": contentId, className: cn("liano-dropdown-menu__trigger", className), onClick: composeEventHandlers(onClick, () => {
129
- if (disabled) {
130
- return;
131
- }
132
- const nextOpen = !open;
133
- setOpen(nextOpen);
134
- if (!nextOpen) {
135
- return;
136
- }
137
- queueMicrotask(() => {
138
- const first = getEnabledItems()[0];
139
- first?.ref?.focus();
140
- });
141
- }) }));
146
+ const handleClick = () => {
147
+ if (disabled) {
148
+ return;
149
+ }
150
+ const nextOpen = !open;
151
+ setOpen(nextOpen);
152
+ if (!nextOpen) {
153
+ return;
154
+ }
155
+ queueMicrotask(() => {
156
+ const first = getEnabledItems()[0];
157
+ first?.ref?.focus();
158
+ });
159
+ };
160
+ if (asChild) {
161
+ return (_jsx(Slot, { ...props, ref: composeRefs(ref, triggerRef, referenceRef), id: triggerId, "aria-controls": contentId, "aria-disabled": disabled ? true : undefined, "aria-expanded": open ? "true" : "false", "aria-haspopup": "menu", className: cn("zvk-ui-dropdown-menu__trigger", className), "data-disabled": disabled ? "true" : undefined, "data-state": open ? "open" : "closed", onClick: composeEventHandlers(onClick, handleClick), children: props.children }));
162
+ }
163
+ return (_jsx("button", { ...props, ref: composeRefs(ref, triggerRef, referenceRef), type: type, disabled: disabled, id: triggerId, role: "button", "aria-haspopup": "menu", "aria-expanded": open ? "true" : "false", "aria-controls": contentId, className: cn("zvk-ui-dropdown-menu__trigger", className), "data-state": open ? "open" : "closed", onClick: composeEventHandlers(onClick, handleClick) }));
142
164
  }
143
- function DropdownMenuContent({ children, className, forceMount = false, sideOffset = defaultContentPositioning.sideOffset, collisionPadding = defaultContentPositioning.collisionPadding, matchTriggerWidth = defaultContentPositioning.matchTriggerWidth, ref, onKeyDown, ...props }) {
144
- const { container, contentId, open, setOpen, updateContentPositioning, triggerId, floatingRef, floatingStyle, getEnabledItems } = useDropdownMenuContext("DropdownMenu.Content");
165
+ function DropdownMenuContent({ align, alignOffset = defaultContentPositioning.alignOffset, children, className, forceMount = false, side, sideOffset = defaultContentPositioning.sideOffset, collisionPadding = defaultContentPositioning.collisionPadding, matchTriggerWidth = defaultContentPositioning.matchTriggerWidth, ref, onKeyDown, ...props }) {
166
+ const { container, contentId, open, setOpen, updateContentPositioning, triggerId, floatingRef, floatingPlacement, floatingStyle, getEnabledItems } = useDropdownMenuContext("DropdownMenu.Content");
145
167
  const focusItem = React.useCallback((index, items) => {
146
168
  if (items.length === 0) {
147
169
  return;
@@ -181,14 +203,16 @@ function DropdownMenuContent({ children, className, forceMount = false, sideOffs
181
203
  }, [getEnabledItems]);
182
204
  React.useEffect(() => {
183
205
  updateContentPositioning({
206
+ ...(side === undefined ? {} : { placement: placementFromSideAlign(side, align, "bottom") }),
184
207
  sideOffset,
208
+ alignOffset,
185
209
  collisionPadding,
186
210
  matchTriggerWidth
187
211
  });
188
212
  return () => {
189
213
  updateContentPositioning(defaultContentPositioning);
190
214
  };
191
- }, [collisionPadding, matchTriggerWidth, sideOffset, updateContentPositioning]);
215
+ }, [align, alignOffset, collisionPadding, matchTriggerWidth, side, sideOffset, updateContentPositioning]);
192
216
  if (!open && !forceMount) {
193
217
  return null;
194
218
  }
@@ -200,7 +224,7 @@ function DropdownMenuContent({ children, className, forceMount = false, sideOffs
200
224
  else if (ref) {
201
225
  ref.current = node;
202
226
  }
203
- }, id: contentId, role: "menu", "aria-labelledby": triggerId, className: cn("liano-dropdown-menu__content", className), style: floatingStyle, "data-state": open ? "open" : "closed", hidden: open ? undefined : true, onKeyDown: composeEventHandlers(onKeyDown, (event) => {
227
+ }, id: contentId, role: "menu", "aria-labelledby": triggerId, className: cn("zvk-ui-dropdown-menu__content", className), style: floatingStyle, "data-align": placementParts(floatingPlacement).align, "data-side": placementParts(floatingPlacement).side, "data-state": open ? "open" : "closed", hidden: open ? undefined : true, onKeyDown: composeEventHandlers(onKeyDown, (event) => {
204
228
  if (event.key === "ArrowDown") {
205
229
  event.preventDefault();
206
230
  moveFocus("next");
@@ -236,7 +260,7 @@ function DropdownMenuContent({ children, className, forceMount = false, sideOffs
236
260
  setOpen(false);
237
261
  }, children: content }) }));
238
262
  }
239
- function DropdownMenuItem({ children, className, disabled, onClick, onSelect, onKeyDown, ref, ...props }) {
263
+ function DropdownMenuItem({ asChild = false, children, className, disabled, onClick, onSelect, onKeyDown, ref, ...props }) {
240
264
  const context = useDropdownMenuContext("DropdownMenu.Item");
241
265
  const itemRef = React.useRef(null);
242
266
  const itemId = React.useId();
@@ -251,41 +275,50 @@ function DropdownMenuItem({ children, className, disabled, onClick, onSelect, on
251
275
  context.registerItem(itemRecord);
252
276
  return () => context.unregisterItem(itemId);
253
277
  }, [context, isDisabled, itemId, onSelect]);
254
- return (_jsx("button", { ...props, ref: (node) => {
255
- itemRef.current = node;
256
- if (typeof ref === "function") {
257
- ref(node);
258
- }
259
- else if (ref) {
260
- ref.current = node;
261
- }
262
- }, type: "button", role: "menuitem", disabled: isDisabled, "aria-disabled": isDisabled ? "true" : undefined, className: cn("liano-dropdown-menu__item", className), "data-state": isDisabled ? "disabled" : "enabled", onClick: composeEventHandlers(onClick, (event) => {
263
- if (isDisabled) {
264
- return;
265
- }
266
- onSelect?.(event);
278
+ const select = (event) => {
279
+ if (isDisabled) {
280
+ return;
281
+ }
282
+ onSelect?.(event);
283
+ if (!event.defaultPrevented) {
267
284
  context.setOpen(false);
268
- }), onKeyDown: composeEventHandlers(onKeyDown, (event) => {
269
- if (event.key === " " || event.key === "Space" || event.key === "Enter" || event.key === "Spacebar") {
285
+ }
286
+ };
287
+ const itemProps = {
288
+ ...props,
289
+ ref: (node) => {
290
+ setComposedRef(itemRef, ref, node);
291
+ },
292
+ role: "menuitem",
293
+ "aria-disabled": isDisabled ? true : undefined,
294
+ className: cn("zvk-ui-dropdown-menu__item", className),
295
+ "data-disabled": isDisabled ? "true" : undefined,
296
+ "data-state": isDisabled ? "disabled" : "enabled",
297
+ onClick: composeEventHandlers(onClick, select),
298
+ onKeyDown: composeEventHandlers(onKeyDown, (event) => {
299
+ if (isActivationKey(event.key)) {
300
+ select(event);
270
301
  event.preventDefault();
271
- if (isDisabled) {
272
- return;
273
- }
274
- onSelect?.(event);
275
- context.setOpen(false);
276
302
  }
277
- }), children: children }));
303
+ })
304
+ };
305
+ if (asChild) {
306
+ return _jsx(Slot, { ...itemProps, children: children });
307
+ }
308
+ return (_jsx("button", { ...props, ref: (node) => {
309
+ setComposedRef(itemRef, ref, node);
310
+ }, type: "button", role: "menuitem", disabled: isDisabled, "aria-disabled": itemProps["aria-disabled"], className: itemProps.className, "data-disabled": itemProps["data-disabled"], "data-state": itemProps["data-state"], onClick: itemProps.onClick, onKeyDown: itemProps.onKeyDown, children: children }));
278
311
  }
279
312
  function DropdownMenuSeparator({ className, ref, ...props }) {
280
- return (_jsx("div", { ...props, ref: ref, role: "separator", "aria-hidden": "true", className: cn("liano-dropdown-menu__separator", className) }));
313
+ return (_jsx("div", { ...props, ref: ref, role: "separator", "aria-hidden": "true", className: cn("zvk-ui-dropdown-menu__separator", className) }));
281
314
  }
282
315
  function DropdownMenuLabel({ className, ref, ...props }) {
283
- return (_jsx("div", { ...props, ref: ref, className: cn("liano-dropdown-menu__label", className) }));
316
+ return (_jsx("div", { ...props, ref: ref, className: cn("zvk-ui-dropdown-menu__label", className) }));
284
317
  }
285
318
  function DropdownMenuShortcut({ className, ref, ...props }) {
286
- return (_jsx("span", { ...props, ref: ref, className: cn("liano-dropdown-menu__shortcut", className) }));
319
+ return (_jsx("span", { ...props, ref: ref, className: cn("zvk-ui-dropdown-menu__shortcut", className) }));
287
320
  }
288
- function DropdownMenuCheckboxItem({ checked, children, className, defaultChecked = false, disabled, onCheckedChange, onClick, onKeyDown, onSelect, ref, ...props }) {
321
+ function DropdownMenuCheckboxItem({ asChild = false, checked, children, className, defaultChecked = false, disabled, onCheckedChange, onClick, onKeyDown, onSelect, ref, ...props }) {
289
322
  const context = useDropdownMenuContext("DropdownMenu.CheckboxItem");
290
323
  const itemRef = React.useRef(null);
291
324
  const itemId = React.useId();
@@ -308,33 +341,46 @@ function DropdownMenuCheckboxItem({ checked, children, className, defaultChecked
308
341
  if (isDisabled) {
309
342
  return;
310
343
  }
311
- setCurrentChecked((value) => !value);
312
344
  onSelect?.(event);
313
- context.setOpen(false);
345
+ if (!event.defaultPrevented) {
346
+ setCurrentChecked((value) => !value);
347
+ context.setOpen(false);
348
+ }
314
349
  };
315
- return (_jsxs("button", { ...props, ref: (node) => {
316
- itemRef.current = node;
317
- if (typeof ref === "function") {
318
- ref(node);
319
- }
320
- else if (ref) {
321
- ref.current = node;
322
- }
323
- }, type: "button", role: "menuitemcheckbox", disabled: isDisabled, "aria-checked": currentChecked ? "true" : "false", "aria-disabled": isDisabled ? "true" : undefined, className: cn("liano-dropdown-menu__item", "liano-dropdown-menu__checkbox-item", className), "data-checked": currentChecked ? "true" : undefined, "data-disabled": isDisabled ? "true" : undefined, onClick: composeEventHandlers(onClick, select), onKeyDown: composeEventHandlers(onKeyDown, (event) => {
324
- if (event.key === " " || event.key === "Space" || event.key === "Enter" || event.key === "Spacebar") {
325
- event.preventDefault();
350
+ const itemProps = {
351
+ ...props,
352
+ ref: (node) => {
353
+ setComposedRef(itemRef, ref, node);
354
+ },
355
+ role: "menuitemcheckbox",
356
+ "aria-checked": currentChecked,
357
+ "aria-disabled": isDisabled ? true : undefined,
358
+ className: cn("zvk-ui-dropdown-menu__item", "zvk-ui-dropdown-menu__checkbox-item", className),
359
+ "data-checked": currentChecked ? "true" : undefined,
360
+ "data-disabled": isDisabled ? "true" : undefined,
361
+ onClick: composeEventHandlers(onClick, select),
362
+ onKeyDown: composeEventHandlers(onKeyDown, (event) => {
363
+ if (isActivationKey(event.key)) {
326
364
  select(event);
365
+ event.preventDefault();
327
366
  }
328
- }), children: [_jsx("span", { className: "liano-dropdown-menu__item-indicator", "aria-hidden": "true", children: currentChecked ? "✓" : "" }), _jsx("span", { className: "liano-dropdown-menu__item-label", children: children })] }));
367
+ })
368
+ };
369
+ if (asChild) {
370
+ return _jsx(Slot, { ...itemProps, children: children });
371
+ }
372
+ return (_jsxs("button", { ...props, ref: (node) => {
373
+ setComposedRef(itemRef, ref, node);
374
+ }, type: "button", role: "menuitemcheckbox", disabled: isDisabled, "aria-checked": itemProps["aria-checked"], "aria-disabled": itemProps["aria-disabled"], className: itemProps.className, "data-checked": itemProps["data-checked"], "data-disabled": itemProps["data-disabled"], onClick: itemProps.onClick, onKeyDown: itemProps.onKeyDown, children: [_jsx("span", { className: "zvk-ui-dropdown-menu__item-indicator", "aria-hidden": "true", children: currentChecked ? "✓" : "" }), _jsx("span", { className: "zvk-ui-dropdown-menu__item-label", children: children })] }));
329
375
  }
330
376
  function DropdownMenuRadioGroup({ children, className, onValueChange, ref, value, ...props }) {
331
377
  const contextValue = React.useMemo(() => ({
332
378
  ...(value !== undefined ? { value } : {}),
333
379
  ...(onValueChange ? { onValueChange } : {})
334
380
  }), [onValueChange, value]);
335
- return (_jsx(DropdownMenuRadioGroupContext.Provider, { value: contextValue, children: _jsx("div", { ...props, ref: ref, className: cn("liano-dropdown-menu__radio-group", className), role: "group", children: children }) }));
381
+ return (_jsx(DropdownMenuRadioGroupContext.Provider, { value: contextValue, children: _jsx("div", { ...props, ref: ref, className: cn("zvk-ui-dropdown-menu__radio-group", className), role: "group", children: children }) }));
336
382
  }
337
- function DropdownMenuRadioItem({ children, className, disabled, onClick, onKeyDown, onSelect, ref, value, ...props }) {
383
+ function DropdownMenuRadioItem({ asChild = false, children, className, disabled, onClick, onKeyDown, onSelect, ref, value, ...props }) {
338
384
  const context = useDropdownMenuContext("DropdownMenu.RadioItem");
339
385
  const radioGroup = React.useContext(DropdownMenuRadioGroupContext);
340
386
  const itemRef = React.useRef(null);
@@ -354,27 +400,40 @@ function DropdownMenuRadioItem({ children, className, disabled, onClick, onKeyDo
354
400
  if (isDisabled) {
355
401
  return;
356
402
  }
357
- radioGroup?.onValueChange?.(value);
358
403
  onSelect?.(event);
359
- context.setOpen(false);
404
+ if (!event.defaultPrevented) {
405
+ radioGroup?.onValueChange?.(value);
406
+ context.setOpen(false);
407
+ }
360
408
  };
361
- return (_jsxs("button", { ...props, ref: (node) => {
362
- itemRef.current = node;
363
- if (typeof ref === "function") {
364
- ref(node);
365
- }
366
- else if (ref) {
367
- ref.current = node;
368
- }
369
- }, type: "button", role: "menuitemradio", disabled: isDisabled, "aria-checked": checked ? "true" : "false", "aria-disabled": isDisabled ? "true" : undefined, className: cn("liano-dropdown-menu__item", "liano-dropdown-menu__radio-item", className), "data-checked": checked ? "true" : undefined, "data-disabled": isDisabled ? "true" : undefined, onClick: composeEventHandlers(onClick, select), onKeyDown: composeEventHandlers(onKeyDown, (event) => {
370
- if (event.key === " " || event.key === "Space" || event.key === "Enter" || event.key === "Spacebar") {
371
- event.preventDefault();
409
+ const itemProps = {
410
+ ...props,
411
+ ref: (node) => {
412
+ setComposedRef(itemRef, ref, node);
413
+ },
414
+ role: "menuitemradio",
415
+ "aria-checked": checked,
416
+ "aria-disabled": isDisabled ? true : undefined,
417
+ className: cn("zvk-ui-dropdown-menu__item", "zvk-ui-dropdown-menu__radio-item", className),
418
+ "data-checked": checked ? "true" : undefined,
419
+ "data-disabled": isDisabled ? "true" : undefined,
420
+ onClick: composeEventHandlers(onClick, select),
421
+ onKeyDown: composeEventHandlers(onKeyDown, (event) => {
422
+ if (isActivationKey(event.key)) {
372
423
  select(event);
424
+ event.preventDefault();
373
425
  }
374
- }), children: [_jsx("span", { className: "liano-dropdown-menu__item-indicator", "aria-hidden": "true", children: checked ? "●" : "" }), _jsx("span", { className: "liano-dropdown-menu__item-label", children: children })] }));
426
+ })
427
+ };
428
+ if (asChild) {
429
+ return _jsx(Slot, { ...itemProps, children: children });
430
+ }
431
+ return (_jsxs("button", { ...props, ref: (node) => {
432
+ setComposedRef(itemRef, ref, node);
433
+ }, type: "button", role: "menuitemradio", disabled: isDisabled, "aria-checked": itemProps["aria-checked"], "aria-disabled": itemProps["aria-disabled"], className: itemProps.className, "data-checked": itemProps["data-checked"], "data-disabled": itemProps["data-disabled"], onClick: itemProps.onClick, onKeyDown: itemProps.onKeyDown, children: [_jsx("span", { className: "zvk-ui-dropdown-menu__item-indicator", "aria-hidden": "true", children: checked ? "●" : "" }), _jsx("span", { className: "zvk-ui-dropdown-menu__item-label", children: children })] }));
375
434
  }
376
435
  function DropdownMenuSub({ children, className, ref, ...props }) {
377
- return (_jsx("div", { ...props, ref: ref, className: cn("liano-dropdown-menu__sub", className), children: children }));
436
+ return (_jsx("div", { ...props, ref: ref, className: cn("zvk-ui-dropdown-menu__sub", className), children: children }));
378
437
  }
379
438
  export const DropdownMenu = Object.assign(DropdownMenuRoot, {
380
439
  CheckboxItem: DropdownMenuCheckboxItem,
@@ -30,5 +30,5 @@ export function EmptyState({ actions, align = "center", children: _children, cla
30
30
  const hasIcon = hasRenderableNode(icon);
31
31
  const showDescription = description !== null && description !== undefined;
32
32
  const showActions = actions !== null && actions !== undefined;
33
- return (_jsxs("div", { ...props, ref: ref, className: cn("liano-empty-state", className), "data-align": align, "data-size": size, children: [icon ? _jsx("div", { "aria-hidden": hasIcon ? undefined : true, className: "liano-empty-state__icon", children: icon }) : null, _jsx("h3", { className: "liano-empty-state__title", children: title }), showDescription ? _jsx("p", { className: "liano-empty-state__description", children: description }) : null, showActions ? _jsx("div", { className: "liano-empty-state__actions", children: actions }) : null] }));
33
+ return (_jsxs("div", { ...props, ref: ref, className: cn("zvk-ui-empty-state", className), "data-align": align, "data-size": size, children: [icon ? _jsx("div", { "aria-hidden": hasIcon ? undefined : true, className: "zvk-ui-empty-state__icon", children: icon }) : null, _jsx("h3", { className: "zvk-ui-empty-state__title", children: title }), showDescription ? _jsx("p", { className: "zvk-ui-empty-state__description", children: description }) : null, showActions ? _jsx("div", { className: "zvk-ui-empty-state__actions", children: actions }) : null] }));
34
34
  }
@@ -39,5 +39,5 @@ export class ErrorBoundary extends React.Component {
39
39
  }
40
40
  }
41
41
  export function ErrorFallback({ action, className, error, ref, title = "Something went wrong", ...props }) {
42
- return (_jsxs("div", { ...props, ref: ref, "aria-label": typeof title === "string" ? title : undefined, className: cn("liano-error-fallback", className), role: "alert", children: [_jsx("h3", { className: "liano-error-fallback__title", children: title }), error ? _jsx("p", { className: "liano-error-fallback__message", children: error.message }) : null, action ? _jsx("div", { className: "liano-error-fallback__action", children: action }) : null] }));
42
+ return (_jsxs("div", { ...props, ref: ref, "aria-label": typeof title === "string" ? title : undefined, className: cn("zvk-ui-error-fallback", className), role: "alert", children: [_jsx("h3", { className: "zvk-ui-error-fallback__title", children: title }), error ? _jsx("p", { className: "zvk-ui-error-fallback__message", children: error.message }) : null, action ? _jsx("div", { className: "zvk-ui-error-fallback__action", children: action }) : null] }));
43
43
  }
@@ -2,16 +2,16 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cn } from "../../utils/cn.js";
4
4
  function FieldRoot({ className, disabled, invalid, ref, required, ...props }) {
5
- return (_jsx("div", { ...props, ref: ref, className: cn("liano-field", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": invalid ? "true" : undefined, "data-required": required ? "true" : undefined }));
5
+ return (_jsx("div", { ...props, ref: ref, className: cn("zvk-ui-field", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": invalid ? "true" : undefined, "data-required": required ? "true" : undefined }));
6
6
  }
7
7
  function FieldLabel({ className, ref, ...props }) {
8
- return _jsx("label", { ...props, ref: ref, className: cn("liano-field__label", className) });
8
+ return _jsx("label", { ...props, ref: ref, className: cn("zvk-ui-field__label", className) });
9
9
  }
10
10
  function FieldDescription({ className, ref, ...props }) {
11
- return _jsx("p", { ...props, ref: ref, className: cn("liano-field__description", className) });
11
+ return _jsx("p", { ...props, ref: ref, className: cn("zvk-ui-field__description", className) });
12
12
  }
13
13
  function FieldError({ className, ref, role = "alert", ...props }) {
14
- return _jsx("p", { ...props, ref: ref, className: cn("liano-field__error", className), role: role });
14
+ return _jsx("p", { ...props, ref: ref, className: cn("zvk-ui-field__error", className), role: role });
15
15
  }
16
16
  export const Field = Object.assign(FieldRoot, {
17
17
  Label: FieldLabel,
@@ -28,12 +28,12 @@ export function FileUploadInput({ "aria-describedby": ariaDescribedBy, buttonLab
28
28
  const descriptionId = hasDescription ? `${inputId}-description` : undefined;
29
29
  const errorId = hasError ? `${inputId}-error` : undefined;
30
30
  const summaryId = `${inputId}-summary`;
31
- const describedBy = joinIds(ariaDescribedBy, descriptionId, errorId);
31
+ const describedBy = joinIds(ariaDescribedBy, descriptionId, errorId, summaryId);
32
32
  function handleChange(event) {
33
33
  setFiles(event.currentTarget.files);
34
34
  onChange?.(event);
35
35
  }
36
- const input = (_jsxs("div", { className: "liano-file-upload-input", children: [_jsx("span", { "aria-hidden": "true", className: "liano-file-upload-input__button", children: buttonLabel }), _jsx("input", { ...props, ref: ref, "aria-describedby": describedBy, "aria-invalid": invalidState ? true : undefined, className: cn("liano-file-upload-input__control", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": invalidState ? "true" : undefined, "data-required": required ? "true" : undefined, "data-size": size, disabled: disabled, id: inputId, onChange: handleChange, required: required, type: "file" }), _jsx("span", { className: "liano-file-upload-input__summary", id: summaryId, children: getFileSummary(files, emptyLabel) })] }));
36
+ const input = (_jsxs("div", { className: "zvk-ui-file-upload-input", children: [_jsx("span", { "aria-hidden": "true", className: "zvk-ui-file-upload-input__button", children: buttonLabel }), _jsx("input", { ...props, ref: ref, "aria-describedby": describedBy, "aria-invalid": invalidState ? true : undefined, className: cn("zvk-ui-file-upload-input__control", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": invalidState ? "true" : undefined, "data-required": required ? "true" : undefined, "data-size": size, disabled: disabled, id: inputId, onChange: handleChange, required: required, type: "file" }), _jsx("span", { className: "zvk-ui-file-upload-input__summary", id: summaryId, children: getFileSummary(files, emptyLabel) })] }));
37
37
  if (!hasRenderableNode(label) && !hasDescription && !hasError) {
38
38
  return input;
39
39
  }
@@ -15,7 +15,7 @@ function joinIds(ids) {
15
15
  return value.length > 0 ? value : undefined;
16
16
  }
17
17
  export function Form({ className, ref, ...props }) {
18
- return _jsx("form", { ...props, ref: ref, className: cn("liano-form", className) });
18
+ return _jsx("form", { ...props, ref: ref, className: cn("zvk-ui-form", className) });
19
19
  }
20
20
  export function FormField({ children, className, disabled, id, invalid, required, ...props }) {
21
21
  const generatedId = React.useId();
@@ -36,14 +36,14 @@ export function FormField({ children, className, disabled, id, invalid, required
36
36
  registerDescription,
37
37
  unregisterDescription
38
38
  }), [controlId, descriptionIds, disabled, invalid, registerDescription, required, unregisterDescription]);
39
- return (_jsx(FormFieldContext.Provider, { value: context, children: _jsx("div", { ...props, className: cn("liano-form-field", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": invalid ? "true" : undefined, "data-required": required ? "true" : undefined, children: children }) }));
39
+ return (_jsx(FormFieldContext.Provider, { value: context, children: _jsx("div", { ...props, className: cn("zvk-ui-form-field", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": invalid ? "true" : undefined, "data-required": required ? "true" : undefined, children: children }) }));
40
40
  }
41
41
  export function FormItem({ className, ref, ...props }) {
42
- return _jsx("div", { ...props, ref: ref, className: cn("liano-form-item", className) });
42
+ return _jsx("div", { ...props, ref: ref, className: cn("zvk-ui-form-item", className) });
43
43
  }
44
44
  export function FormLabel({ className, htmlFor, ref, ...props }) {
45
45
  const context = useFormFieldContext("FormLabel");
46
- return _jsx("label", { ...props, ref: ref, className: cn("liano-form-label", className), htmlFor: htmlFor ?? context.controlId });
46
+ return _jsx("label", { ...props, ref: ref, className: cn("zvk-ui-form-label", className), htmlFor: htmlFor ?? context.controlId });
47
47
  }
48
48
  export function FormControl({ children }) {
49
49
  const context = useFormFieldContext("FormControl");
@@ -81,8 +81,8 @@ function FormText({ className, id, ref, role, textRole, ...props }) {
81
81
  return _jsx("p", { ...props, ref: ref, id: textId, role: role, className: className });
82
82
  }
83
83
  export function FormDescription({ className, ...props }) {
84
- return _jsx(FormText, { ...props, className: cn("liano-form-description", className), textRole: "FormDescription" });
84
+ return _jsx(FormText, { ...props, className: cn("zvk-ui-form-description", className), textRole: "FormDescription" });
85
85
  }
86
86
  export function FormMessage({ className, role = "alert", ...props }) {
87
- return _jsx(FormText, { ...props, className: cn("liano-form-message", className), role: role, textRole: "FormMessage" });
87
+ return _jsx(FormText, { ...props, className: cn("zvk-ui-form-message", className), role: role, textRole: "FormMessage" });
88
88
  }
@@ -1,5 +1,6 @@
1
1
  import * as React from "react";
2
2
  import type { FloatingPlacement } from "../../internal/floating/index.js";
3
+ import type { FloatingAlign, FloatingSide } from "../../internal/floating/placement-aliases.js";
3
4
  import type { PortalProps } from "../../internal/portal/index.js";
4
5
  export interface HoverCardProps {
5
6
  children?: React.ReactNode;
@@ -12,9 +13,14 @@ export interface HoverCardProps {
12
13
  disabled?: boolean;
13
14
  }
14
15
  export interface HoverCardTriggerProps {
16
+ asChild?: boolean;
15
17
  children: React.ReactElement;
16
18
  }
17
19
  export interface HoverCardContentProps extends React.HTMLAttributes<HTMLDivElement> {
20
+ placement?: FloatingPlacement;
21
+ side?: FloatingSide;
22
+ align?: FloatingAlign;
23
+ alignOffset?: number;
18
24
  sideOffset?: number;
19
25
  collisionPadding?: number;
20
26
  forceMount?: boolean;
@@ -22,8 +28,8 @@ export interface HoverCardContentProps extends React.HTMLAttributes<HTMLDivEleme
22
28
  ref?: React.Ref<HTMLDivElement>;
23
29
  }
24
30
  declare function HoverCardRoot({ children, closeDelay, defaultOpen, disabled, onOpenChange, open: openProp, openDelay, placement }: HoverCardProps): React.JSX.Element;
25
- declare function HoverCardTrigger({ children }: HoverCardTriggerProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
26
- declare function HoverCardContent({ children, className, collisionPadding, container, forceMount, id, onBlur, onFocus, onMouseEnter, onMouseLeave, onPointerEnter, onPointerLeave, ref, sideOffset, style, ...props }: HoverCardContentProps): React.JSX.Element | null;
31
+ declare function HoverCardTrigger({ children, asChild: _asChild }: HoverCardTriggerProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
32
+ declare function HoverCardContent({ align, alignOffset, children, className, collisionPadding, container, forceMount, id, onBlur, onFocus, onMouseEnter, onMouseLeave, onPointerEnter, onPointerLeave, placement, ref, side, sideOffset, style, ...props }: HoverCardContentProps): React.JSX.Element | null;
27
33
  export declare const HoverCard: typeof HoverCardRoot & {
28
34
  Trigger: typeof HoverCardTrigger;
29
35
  Content: typeof HoverCardContent;
@@ -3,12 +3,14 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { DismissableLayer } from "../../internal/dismissable-layer/index.js";
5
5
  import { useFloatingPosition } from "../../internal/floating/index.js";
6
+ import { placementFromSideAlign } from "../../internal/floating/placement-aliases.js";
6
7
  import { Portal } from "../../internal/portal/index.js";
7
8
  import { useControllableState } from "../../hooks/use-controllable-state.js";
8
9
  import { cn } from "../../utils/cn.js";
9
10
  import { composeEventHandlers } from "../../utils/compose-event-handlers.js";
10
11
  const defaultContentPositioning = {
11
12
  sideOffset: 8,
13
+ alignOffset: 0,
12
14
  collisionPadding: 0
13
15
  };
14
16
  const HoverCardContext = React.createContext(null);
@@ -25,7 +27,7 @@ function composeRefs(...refs) {
25
27
  if (typeof ref === "function") {
26
28
  ref(node);
27
29
  }
28
- else if (ref !== undefined) {
30
+ else if (ref !== undefined && ref !== null) {
29
31
  ref.current = node;
30
32
  }
31
33
  }
@@ -55,8 +57,9 @@ function HoverCardRoot({ children, closeDelay = 300, defaultOpen = false, disabl
55
57
  const contentElement = React.useRef(null);
56
58
  const { floatingRef, floatingStyle, placement: resolvedPlacement, referenceRef, updatePosition } = useFloatingPosition({
57
59
  open,
58
- placement,
60
+ placement: contentPositioning.placement ?? placement,
59
61
  offset: contentPositioning.sideOffset,
62
+ alignmentOffset: contentPositioning.alignOffset,
60
63
  collisionPadding: contentPositioning.collisionPadding,
61
64
  flip: true,
62
65
  shift: true
@@ -204,7 +207,7 @@ function HoverCardRoot({ children, closeDelay = 300, defaultOpen = false, disabl
204
207
  ]);
205
208
  return (_jsx(HoverCardContext.Provider, { value: contextValue, children: children }));
206
209
  }
207
- function HoverCardTrigger({ children }) {
210
+ function HoverCardTrigger({ children, asChild: _asChild = false }) {
208
211
  const context = useHoverCardContext("HoverCard.Trigger");
209
212
  if (!React.isValidElement(children) || context.disabled) {
210
213
  return children;
@@ -221,7 +224,7 @@ function HoverCardTrigger({ children }) {
221
224
  "aria-describedby": joinIds(typeof childAriaDescribedBy === "string" ? childAriaDescribedBy : undefined, context.open ? context.contentId : undefined),
222
225
  "aria-expanded": context.open,
223
226
  "aria-haspopup": "dialog",
224
- className: cn("liano-hover-card__trigger", childClassName),
227
+ className: cn("zvk-ui-hover-card__trigger", childClassName),
225
228
  "data-state": context.open ? "open" : "closed",
226
229
  onBlur: composeEventHandlers(childProps.onBlur, (event) => {
227
230
  context.closeAfterFocusLeaves(event.relatedTarget);
@@ -233,22 +236,25 @@ function HoverCardTrigger({ children }) {
233
236
  onPointerLeave: composeEventHandlers(childProps.onPointerLeave, context.closeWithDelay)
234
237
  });
235
238
  }
236
- function HoverCardContent({ children, className, collisionPadding = defaultContentPositioning.collisionPadding, container, forceMount = false, id, onBlur, onFocus, onMouseEnter, onMouseLeave, onPointerEnter, onPointerLeave, ref, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
239
+ function HoverCardContent({ align, alignOffset = defaultContentPositioning.alignOffset, children, className, collisionPadding = defaultContentPositioning.collisionPadding, container, forceMount = false, id, onBlur, onFocus, onMouseEnter, onMouseLeave, onPointerEnter, onPointerLeave, placement, ref, side, sideOffset = defaultContentPositioning.sideOffset, style, ...props }) {
237
240
  const context = useHoverCardContext("HoverCard.Content");
238
241
  const contentId = id ?? context.contentId;
239
242
  const placementParts = getPlacementParts(context.resolvedPlacement);
240
243
  const { setContentPositioning } = context;
241
244
  React.useEffect(() => {
245
+ const resolvedPlacement = side === undefined ? placement : placementFromSideAlign(side, align, placement ?? "bottom");
242
246
  setContentPositioning({
247
+ ...(resolvedPlacement === undefined ? {} : { placement: resolvedPlacement }),
243
248
  sideOffset,
249
+ alignOffset,
244
250
  collisionPadding
245
251
  });
246
252
  return () => setContentPositioning(defaultContentPositioning);
247
- }, [collisionPadding, setContentPositioning, sideOffset]);
253
+ }, [align, alignOffset, collisionPadding, placement, setContentPositioning, side, sideOffset]);
248
254
  if (!context.open && !forceMount) {
249
255
  return null;
250
256
  }
251
- const content = (_jsx("div", { ...props, ref: composeRefs(ref, context.floatingRef), id: contentId, role: "dialog", className: cn("liano-hover-card__content", className), "data-align": placementParts.align, "data-side": placementParts.side, "data-state": context.open ? "open" : "closed", hidden: context.open ? undefined : true, onBlur: composeEventHandlers(onBlur, (event) => {
257
+ const content = (_jsx("div", { ...props, ref: composeRefs(ref, context.floatingRef), id: contentId, role: "dialog", className: cn("zvk-ui-hover-card__content", className), "data-align": placementParts.align, "data-side": placementParts.side, "data-state": context.open ? "open" : "closed", hidden: context.open ? undefined : true, onBlur: composeEventHandlers(onBlur, (event) => {
252
258
  context.closeAfterFocusLeaves(event.relatedTarget);
253
259
  }), onFocus: composeEventHandlers(onFocus, context.openImmediately), onMouseEnter: composeEventHandlers(onMouseEnter, context.cancelClose), onMouseLeave: composeEventHandlers(onMouseLeave, context.closeWithDelay), onPointerEnter: composeEventHandlers(onPointerEnter, context.cancelClose), onPointerLeave: composeEventHandlers(onPointerLeave, context.closeWithDelay), style: { ...context.floatingStyle, ...style }, children: children }));
254
260
  if (!context.open) {
@@ -2,7 +2,25 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { Spinner } from "../spinner/spinner.js";
4
4
  import { cn } from "../../utils/cn.js";
5
+ function isDevelopmentEnvironment() {
6
+ const runtimeProcess = globalThis.process;
7
+ if (runtimeProcess?.env?.NODE_ENV) {
8
+ return runtimeProcess.env.NODE_ENV !== "production";
9
+ }
10
+ const metaEnv = import.meta.env;
11
+ if (typeof metaEnv?.DEV === "boolean") {
12
+ return metaEnv.DEV;
13
+ }
14
+ if (typeof metaEnv?.PROD === "boolean") {
15
+ return !metaEnv.PROD;
16
+ }
17
+ return typeof metaEnv?.MODE === "string" && metaEnv.MODE !== "production";
18
+ }
5
19
  export function IconButton({ children, className, disabled, loading = false, ref, size = "md", type = "button", variant = "secondary", ...props }) {
6
20
  const isDisabled = disabled || loading;
7
- return (_jsx("button", { ...props, ref: ref, "aria-busy": loading ? true : undefined, className: cn("liano-icon-button", className), "data-disabled": isDisabled ? "true" : undefined, "data-loading": loading ? "true" : undefined, "data-size": size, "data-variant": variant, disabled: isDisabled, type: type, children: loading ? _jsx(Spinner, { size: "sm" }) : children }));
21
+ const hasAccessibleName = props["aria-label"] !== undefined || props["aria-labelledby"] !== undefined;
22
+ if (!hasAccessibleName && isDevelopmentEnvironment()) {
23
+ console.warn("IconButton requires an accessible name. Provide aria-label or aria-labelledby.");
24
+ }
25
+ return (_jsx("button", { ...props, ref: ref, "aria-busy": loading ? true : undefined, className: cn("zvk-ui-icon-button", className), "data-disabled": isDisabled ? "true" : undefined, "data-loading": loading ? "true" : undefined, "data-size": size, "data-variant": variant, disabled: isDisabled, type: type, children: loading ? _jsx(Spinner, { size: "sm" }) : children }));
8
26
  }