@zvk/ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,6 +18,13 @@ import "@zvk/ui/styles.css";
18
18
  - Components use plain CSS classes, CSS variables, and data attributes.
19
19
  - First-slice overlays such as Dialog, Popover, Tooltip, Select, and DropdownMenu are intentionally deferred.
20
20
 
21
+ ## Release Status
22
+
23
+ GitHub Actions runs `npm run preflight` before release publishing. Release Please opens
24
+ reviewable version-bump PRs from conventional commits; merging a release PR creates the
25
+ GitHub release and publishes `@zvk/ui` to npm through trusted publishing in the protected
26
+ `npm-publish` environment.
27
+
21
28
  ## First Slice
22
29
 
23
30
  The initial build includes Button, IconButton, Badge, Card, Separator, Spinner, Skeleton, Field, Input, and the hooks/utilities named in the checked-in plans.
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  export type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "destructive";
3
- export type ButtonSize = "sm" | "md" | "lg";
3
+ export type ButtonSize = "sm" | "md" | "lg" | "icon";
4
4
  export interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "disabled"> {
5
5
  variant?: ButtonVariant;
6
6
  size?: ButtonSize;
@@ -1,20 +1,29 @@
1
1
  import * as React from "react";
2
+ import type { FloatingPlacement } from "../../internal/floating/index.js";
3
+ import type { PortalProps } from "../../internal/portal/index.js";
2
4
  export interface ComboboxOption {
3
5
  value: string;
4
6
  label: string;
5
7
  disabled?: boolean;
6
8
  keywords?: readonly string[];
7
9
  }
10
+ export type ComboboxValue = string | null;
8
11
  export interface ComboboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "size"> {
9
12
  label?: React.ReactNode;
10
13
  description?: React.ReactNode;
11
14
  error?: React.ReactNode;
12
15
  options: readonly ComboboxOption[];
13
- value?: string;
14
- defaultValue?: string;
15
- onValueChange?: (value: string) => void;
16
+ value?: ComboboxValue;
17
+ defaultValue?: ComboboxValue;
18
+ onValueChange?: (value: ComboboxValue) => void;
19
+ clearable?: boolean;
20
+ placement?: FloatingPlacement;
21
+ sideOffset?: number;
22
+ collisionPadding?: number;
23
+ matchTriggerWidth?: boolean;
24
+ container?: PortalProps["container"];
16
25
  placeholder?: string;
17
26
  size?: "sm" | "md" | "lg";
18
27
  ref?: React.Ref<HTMLInputElement>;
19
28
  }
20
- export declare function Combobox({ "aria-describedby": ariaDescribedBy, className, defaultValue, description, disabled, error, id, label, onBlur, onFocus, onKeyDown, onValueChange, options, placeholder, ref, required, size, value, ...props }: ComboboxProps): React.JSX.Element;
29
+ export declare function Combobox({ "aria-describedby": ariaDescribedBy, className, clearable, collisionPadding, container, defaultValue, description, disabled, error, id, label, matchTriggerWidth, onBlur, onFocus, onKeyDown, onValueChange, options, placement, placeholder, ref, required, sideOffset, size, value, ...props }: ComboboxProps): React.JSX.Element;
@@ -5,6 +5,9 @@ import { commandItemMatches } from "../command/command-filter.js";
5
5
  import { composeEventHandlers } from "../../utils/compose-event-handlers.js";
6
6
  import { cn } from "../../utils/cn.js";
7
7
  import { useControllableState } from "../../hooks/use-controllable-state.js";
8
+ import { DismissableLayer } from "../../internal/dismissable-layer/index.js";
9
+ import { useFloatingPosition } from "../../internal/floating/index.js";
10
+ import { Portal } from "../../internal/portal/index.js";
8
11
  function hasRenderableNode(value) {
9
12
  return value !== undefined && value !== null && value !== false;
10
13
  }
@@ -15,6 +18,18 @@ function joinIds(...ids) {
15
18
  function optionId(baseId, value) {
16
19
  return `${baseId}-option-${value.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
17
20
  }
21
+ function composeRefs(...refs) {
22
+ return (node) => {
23
+ for (const ref of refs) {
24
+ if (typeof ref === "function") {
25
+ ref(node);
26
+ }
27
+ else if (ref && "current" in ref) {
28
+ ref.current = node;
29
+ }
30
+ }
31
+ };
32
+ }
18
33
  function firstEnabled(options) {
19
34
  return options.find((option) => option.disabled !== true);
20
35
  }
@@ -28,7 +43,7 @@ function nextEnabled(options, currentValue, direction) {
28
43
  const wrapped = ((nextIndex % enabled.length) + enabled.length) % enabled.length;
29
44
  return enabled[wrapped];
30
45
  }
31
- export function Combobox({ "aria-describedby": ariaDescribedBy, className, defaultValue = "", description, disabled, error, id, label, onBlur, onFocus, onKeyDown, onValueChange, options, placeholder, ref, required, size = "md", value, ...props }) {
46
+ export function Combobox({ "aria-describedby": ariaDescribedBy, className, clearable = false, collisionPadding = 0, container, defaultValue = null, description, disabled, error, id, label, matchTriggerWidth = true, onBlur, onFocus, onKeyDown, onValueChange, options, placement = "bottom-start", placeholder, ref, required, sideOffset = 4, size = "md", value, ...props }) {
32
47
  const generatedId = React.useId();
33
48
  const inputId = id ?? generatedId;
34
49
  const listboxId = `${inputId}-listbox`;
@@ -39,6 +54,7 @@ export function Combobox({ "aria-describedby": ariaDescribedBy, className, defau
39
54
  const errorId = hasError ? `${inputId}-error` : undefined;
40
55
  const describedBy = joinIds(ariaDescribedBy, descriptionId, errorId);
41
56
  const invalid = hasError || props["aria-invalid"] === true || props["aria-invalid"] === "true";
57
+ const inputRef = React.useRef(null);
42
58
  const [selectedValue, setSelectedValue] = useControllableState({
43
59
  ...(value !== undefined ? { value } : {}),
44
60
  defaultValue,
@@ -48,6 +64,14 @@ export function Combobox({ "aria-describedby": ariaDescribedBy, className, defau
48
64
  const [open, setOpen] = React.useState(false);
49
65
  const [query, setQuery] = React.useState(selectedOption?.label ?? "");
50
66
  const [activeValue, setActiveValue] = React.useState(selectedOption?.value);
67
+ const floating = useFloatingPosition({
68
+ open,
69
+ placement,
70
+ strategy: "absolute",
71
+ offset: sideOffset,
72
+ collisionPadding,
73
+ matchReferenceWidth: matchTriggerWidth
74
+ });
51
75
  const filteredOptions = options.filter((option) => commandItemMatches(option, query));
52
76
  const activeOption = filteredOptions.find((option) => option.value === activeValue && option.disabled !== true) ??
53
77
  firstEnabled(filteredOptions);
@@ -71,11 +95,18 @@ export function Combobox({ "aria-describedby": ariaDescribedBy, className, defau
71
95
  setActiveValue(option.value);
72
96
  setOpen(false);
73
97
  }, [setSelectedValue]);
98
+ const clearSelection = React.useCallback(() => {
99
+ setSelectedValue(null);
100
+ setQuery("");
101
+ setActiveValue(firstEnabled(options)?.value);
102
+ setOpen(false);
103
+ inputRef.current?.focus();
104
+ }, [options, setSelectedValue]);
74
105
  const moveActive = React.useCallback((direction) => {
75
106
  const next = nextEnabled(filteredOptions, activeOption?.value, direction);
76
107
  setActiveValue(next?.value);
77
108
  }, [activeOption?.value, filteredOptions]);
78
- const input = (_jsx("input", { ...props, ref: ref, id: inputId, role: "combobox", "aria-activedescendant": open && activeOption ? optionId(inputId, activeOption.value) : undefined, "aria-autocomplete": "list", "aria-controls": listboxId, "aria-describedby": describedBy, "aria-expanded": open ? "true" : "false", "aria-invalid": invalid ? "true" : undefined, className: cn("liano-combobox__input", className), "data-invalid": invalid ? "true" : undefined, "data-size": size, disabled: disabled, onBlur: onBlur, onChange: (event) => {
109
+ const input = (_jsx("input", { ...props, ref: composeRefs(ref, inputRef, floating.referenceRef), id: inputId, role: "combobox", "aria-activedescendant": open && activeOption ? optionId(inputId, activeOption.value) : undefined, "aria-autocomplete": "list", "aria-controls": listboxId, "aria-describedby": describedBy, "aria-expanded": open ? "true" : "false", "aria-invalid": invalid ? "true" : undefined, className: cn("liano-combobox__input", className), "data-invalid": invalid ? "true" : undefined, "data-size": size, disabled: disabled, onBlur: onBlur, onChange: (event) => {
79
110
  if (!open) {
80
111
  setOpen(true);
81
112
  }
@@ -113,9 +144,9 @@ export function Combobox({ "aria-describedby": ariaDescribedBy, className, defau
113
144
  setOpen(false);
114
145
  }
115
146
  }), placeholder: placeholder, required: required, value: open ? query : selectedOption?.label ?? query }));
116
- return (_jsxs("div", { className: "liano-combobox", "data-disabled": disabled ? "true" : undefined, "data-invalid": invalid ? "true" : undefined, children: [hasLabel ? _jsx("label", { className: "liano-combobox__label", htmlFor: inputId, children: label }) : null, input, hasDescription ? _jsx("div", { className: "liano-combobox__description", id: descriptionId, children: description }) : null, hasError ? _jsx("div", { className: "liano-combobox__error", id: errorId, children: error }) : null, open ? (_jsx("div", { className: "liano-combobox__popup", children: _jsx("div", { id: listboxId, role: "listbox", className: "liano-combobox__list", children: filteredOptions.map((option) => {
117
- const isActive = activeOption?.value === option.value;
118
- const isSelected = selectedValue === option.value;
119
- return (_jsx("div", { id: optionId(inputId, option.value), role: "option", "aria-disabled": option.disabled ? "true" : undefined, "aria-selected": isSelected ? "true" : "false", className: "liano-combobox__option", "data-disabled": option.disabled ? "true" : undefined, "data-highlighted": isActive ? "true" : undefined, "data-selected": isSelected ? "true" : undefined, onMouseDown: (event) => event.preventDefault(), onClick: () => selectOption(option), children: option.label }, option.value));
120
- }) }) })) : null] }));
147
+ return (_jsxs("div", { className: "liano-combobox", "data-disabled": disabled ? "true" : undefined, "data-invalid": invalid ? "true" : undefined, children: [hasLabel ? _jsx("label", { className: "liano-combobox__label", htmlFor: inputId, children: label }) : null, _jsxs("div", { className: "liano-combobox__control", children: [input, clearable && selectedValue !== null ? (_jsx("button", { type: "button", className: "liano-combobox__clear", "aria-label": "Clear selection", disabled: disabled, onMouseDown: (event) => event.preventDefault(), onClick: clearSelection, children: "Clear" })) : null] }), hasDescription ? _jsx("div", { className: "liano-combobox__description", id: descriptionId, children: description }) : null, hasError ? _jsx("div", { className: "liano-combobox__error", id: errorId, children: error }) : null, open ? (_jsx(Portal, { ...(container === undefined ? {} : { container }), children: _jsx(DismissableLayer, { open: open, onDismiss: () => setOpen(false), children: _jsx("div", { id: listboxId, role: "listbox", className: "liano-combobox__popup", ref: floating.floatingRef, style: floating.floatingStyle, children: filteredOptions.map((option) => {
148
+ const isActive = activeOption?.value === option.value;
149
+ const isSelected = selectedValue === option.value;
150
+ return (_jsx("div", { id: optionId(inputId, option.value), role: "option", "aria-disabled": option.disabled ? "true" : undefined, "aria-selected": isSelected ? "true" : "false", className: "liano-combobox__option", "data-disabled": option.disabled ? "true" : undefined, "data-highlighted": isActive ? "true" : undefined, "data-selected": isSelected ? "true" : undefined, onMouseDown: (event) => event.preventDefault(), onClick: () => selectOption(option), children: option.label }, option.value));
151
+ }) }) }) })) : null] }));
121
152
  }
@@ -1,2 +1,2 @@
1
1
  export { Combobox } from "./combobox.js";
2
- export type { ComboboxOption, ComboboxProps } from "./combobox.js";
2
+ export type { ComboboxOption, ComboboxProps, ComboboxValue } from "./combobox.js";
@@ -19,14 +19,26 @@ export interface ContextMenuItemProps extends React.ButtonHTMLAttributes<HTMLBut
19
19
  export interface ContextMenuSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
20
20
  ref?: React.Ref<HTMLDivElement>;
21
21
  }
22
+ export interface ContextMenuLabelProps extends React.HTMLAttributes<HTMLDivElement> {
23
+ ref?: React.Ref<HTMLDivElement>;
24
+ }
25
+ export interface ContextMenuCheckboxItemProps extends ContextMenuItemProps {
26
+ checked?: boolean;
27
+ defaultChecked?: boolean;
28
+ onCheckedChange?: (checked: boolean) => void;
29
+ }
22
30
  declare function ContextMenuRoot({ children, className, defaultOpen, onOpenChange, open: openProp, ref, ...props }: ContextMenuProps): React.JSX.Element;
23
31
  declare function ContextMenuTrigger({ children, className, onContextMenu, onKeyDown, ref, ...props }: ContextMenuTriggerProps): React.JSX.Element;
24
32
  declare function ContextMenuContent({ children, className, onKeyDown, ref, style, ...props }: ContextMenuContentProps): React.JSX.Element | null;
25
33
  declare function ContextMenuItem({ children, className, disabled, onClick, onKeyDown, onSelect, ref, tone, type, ...props }: ContextMenuItemProps): React.JSX.Element;
34
+ declare function ContextMenuLabel({ className, ref, ...props }: ContextMenuLabelProps): React.JSX.Element;
35
+ declare function ContextMenuCheckboxItem({ checked, children, className, defaultChecked, disabled, onCheckedChange, onClick, onKeyDown, onSelect, ref, tone, type, ...props }: ContextMenuCheckboxItemProps): React.JSX.Element;
26
36
  declare function ContextMenuSeparator({ className, ref, ...props }: ContextMenuSeparatorProps): React.JSX.Element;
27
37
  type ContextMenuComponent = typeof ContextMenuRoot & {
38
+ CheckboxItem: typeof ContextMenuCheckboxItem;
28
39
  Content: typeof ContextMenuContent;
29
40
  Item: typeof ContextMenuItem;
41
+ Label: typeof ContextMenuLabel;
30
42
  Separator: typeof ContextMenuSeparator;
31
43
  Trigger: typeof ContextMenuTrigger;
32
44
  };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { composeEventHandlers } from "../../utils/compose-event-handlers.js";
5
5
  import { cn } from "../../utils/cn.js";
@@ -143,12 +143,53 @@ function ContextMenuItem({ children, className, disabled, onClick, onKeyDown, on
143
143
  }
144
144
  }), children: children }));
145
145
  }
146
+ function ContextMenuLabel({ className, ref, ...props }) {
147
+ return _jsx("div", { ...props, ref: ref, className: cn("liano-context-menu__label", className) });
148
+ }
149
+ function ContextMenuCheckboxItem({ checked, children, className, defaultChecked = false, disabled, onCheckedChange, onClick, onKeyDown, onSelect, ref, tone = "default", type = "button", ...props }) {
150
+ const { close, registerItem, unregisterItem } = useContextMenuContext("ContextMenu.CheckboxItem");
151
+ const itemId = React.useId();
152
+ const itemRef = React.useRef(null);
153
+ const [currentChecked, setCurrentChecked] = useControllableState({
154
+ ...(checked !== undefined ? { value: checked } : {}),
155
+ defaultValue: defaultChecked,
156
+ ...(onCheckedChange ? { onChange: onCheckedChange } : {})
157
+ });
158
+ React.useLayoutEffect(() => {
159
+ registerItem(itemId, { disabled, ref: itemRef.current });
160
+ return () => unregisterItem(itemId);
161
+ }, [disabled, itemId, registerItem, unregisterItem]);
162
+ const select = (event) => {
163
+ if (disabled) {
164
+ return;
165
+ }
166
+ setCurrentChecked((value) => !value);
167
+ onSelect?.(event);
168
+ close();
169
+ };
170
+ return (_jsxs("button", { ...props, ref: (node) => {
171
+ itemRef.current = node;
172
+ if (typeof ref === "function") {
173
+ ref(node);
174
+ }
175
+ else if (ref) {
176
+ ref.current = node;
177
+ }
178
+ }, type: type, role: "menuitemcheckbox", disabled: disabled, "aria-checked": currentChecked ? "true" : "false", "aria-disabled": disabled ? "true" : undefined, className: cn("liano-context-menu__item", "liano-context-menu__checkbox-item", className), "data-checked": currentChecked ? "true" : undefined, "data-disabled": disabled ? "true" : undefined, "data-tone": tone, onClick: composeEventHandlers(onClick, select), onKeyDown: composeEventHandlers(onKeyDown, (event) => {
179
+ if (event.key === "Enter" || event.key === " ") {
180
+ event.preventDefault();
181
+ select(event);
182
+ }
183
+ }), children: [_jsx("span", { className: "liano-context-menu__item-indicator", "aria-hidden": "true", children: currentChecked ? "✓" : "" }), _jsx("span", { className: "liano-context-menu__item-label", children: children })] }));
184
+ }
146
185
  function ContextMenuSeparator({ className, ref, ...props }) {
147
186
  return _jsx("div", { ...props, ref: ref, role: "separator", "aria-hidden": "true", className: cn("liano-context-menu__separator", className) });
148
187
  }
149
188
  export const ContextMenu = Object.assign(ContextMenuRoot, {
189
+ CheckboxItem: ContextMenuCheckboxItem,
150
190
  Content: ContextMenuContent,
151
191
  Item: ContextMenuItem,
192
+ Label: ContextMenuLabel,
152
193
  Separator: ContextMenuSeparator,
153
194
  Trigger: ContextMenuTrigger
154
195
  });
@@ -1,2 +1,2 @@
1
1
  export { ContextMenu } from "./context-menu.js";
2
- export type { ContextMenuContentProps, ContextMenuItemProps, ContextMenuProps, ContextMenuSeparatorProps, ContextMenuTriggerProps } from "./context-menu.js";
2
+ export type { ContextMenuCheckboxItemProps, ContextMenuContentProps, ContextMenuItemProps, ContextMenuLabelProps, ContextMenuProps, ContextMenuSeparatorProps, ContextMenuTriggerProps } from "./context-menu.js";
@@ -13,6 +13,7 @@ export interface DialogTriggerProps extends React.ButtonHTMLAttributes<HTMLButto
13
13
  ref?: React.Ref<HTMLButtonElement>;
14
14
  }
15
15
  export interface DialogContentProps extends React.HTMLAttributes<HTMLDivElement> {
16
+ forceMount?: boolean;
16
17
  ref?: React.Ref<HTMLDivElement>;
17
18
  }
18
19
  export interface DialogHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -40,7 +41,7 @@ interface DialogPortalProps {
40
41
  declare function DialogRoot({ children, className, container, defaultOpen, disableEscapeKeyDown, disableOutsidePointerDown, onOpenChange, open: openProp, ref, ...props }: DialogProps): React.JSX.Element;
41
42
  declare function DialogPortal({ container, children }: DialogPortalProps): React.JSX.Element;
42
43
  declare function DialogOverlay({ className, ref, ...props }: DialogOverlayProps): React.JSX.Element;
43
- declare function DialogContent({ className, children, ref, ...props }: DialogContentProps): React.JSX.Element | null;
44
+ declare function DialogContent({ className, children, forceMount, ref, ...props }: DialogContentProps): React.JSX.Element | null;
44
45
  declare function DialogHeader({ className, ref, ...props }: DialogHeaderProps): React.JSX.Element;
45
46
  declare function DialogTitle({ className, ref, ...props }: DialogTitleProps): React.JSX.Element;
46
47
  declare function DialogDescription({ className, ref, ...props }: DialogDescriptionProps): React.JSX.Element;
@@ -87,12 +87,16 @@ function DialogPortal({ container, children }) {
87
87
  function DialogOverlay({ className, ref, ...props }) {
88
88
  return _jsx("div", { ...props, ref: ref, className: cn("liano-dialog__overlay", className) });
89
89
  }
90
- function DialogContent({ className, children, ref, ...props }) {
90
+ function DialogContent({ className, children, forceMount = false, ref, ...props }) {
91
91
  const { close, container, contentId, describedBy, disableEscapeKeyDown, disableOutsidePointerDown, labelledBy, open } = useDialogContext("Dialog.Content");
92
- if (!open) {
92
+ if (!open && !forceMount) {
93
93
  return null;
94
94
  }
95
- return (_jsxs(DialogPortal, { container: container, children: [_jsx(DialogOverlay, { "aria-hidden": true }), _jsx(DismissableLayer, { open: open, disableEscapeKeyDown: disableEscapeKeyDown, disableOutsidePointerDown: disableOutsidePointerDown, onDismiss: close, children: _jsx(FocusScope, { trapped: true, active: open, children: _jsx("div", { ...props, ref: ref, id: contentId, role: "dialog", "aria-modal": true, "aria-labelledby": labelledBy, "aria-describedby": describedBy, className: cn("liano-dialog__content", className), "data-state": open ? "open" : "closed", children: children }) }) })] }));
95
+ const content = (_jsx("div", { ...props, ref: ref, id: contentId, role: "dialog", "aria-modal": true, "aria-labelledby": labelledBy, "aria-describedby": describedBy, className: cn("liano-dialog__content", className), "data-state": open ? "open" : "closed", hidden: open ? undefined : true, children: children }));
96
+ if (!open) {
97
+ return (_jsxs(DialogPortal, { container: container, children: [_jsx(DialogOverlay, { "aria-hidden": true, hidden: true }), content] }));
98
+ }
99
+ return (_jsxs(DialogPortal, { container: container, children: [_jsx(DialogOverlay, { "aria-hidden": true }), _jsx(DismissableLayer, { open: open, disableEscapeKeyDown: disableEscapeKeyDown, disableOutsidePointerDown: disableOutsidePointerDown, onDismiss: close, children: _jsx(FocusScope, { trapped: true, active: open, children: content }) })] }));
96
100
  }
97
101
  function DialogHeader({ className, ref, ...props }) {
98
102
  return _jsx("div", { ...props, ref: ref, className: cn("liano-dialog__header", className) });
@@ -28,15 +28,49 @@ export interface DropdownMenuItemProps extends React.ButtonHTMLAttributes<HTMLBu
28
28
  export interface DropdownMenuSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
29
29
  ref?: React.Ref<HTMLDivElement>;
30
30
  }
31
+ export interface DropdownMenuLabelProps extends React.HTMLAttributes<HTMLDivElement> {
32
+ ref?: React.Ref<HTMLDivElement>;
33
+ }
34
+ export interface DropdownMenuShortcutProps extends React.HTMLAttributes<HTMLSpanElement> {
35
+ ref?: React.Ref<HTMLSpanElement>;
36
+ }
37
+ export interface DropdownMenuCheckboxItemProps extends DropdownMenuItemProps {
38
+ checked?: boolean;
39
+ defaultChecked?: boolean;
40
+ onCheckedChange?: (checked: boolean) => void;
41
+ }
42
+ export interface DropdownMenuRadioGroupProps extends React.HTMLAttributes<HTMLDivElement> {
43
+ value?: string;
44
+ onValueChange?: (value: string) => void;
45
+ ref?: React.Ref<HTMLDivElement>;
46
+ }
47
+ export interface DropdownMenuRadioItemProps extends DropdownMenuItemProps {
48
+ value: string;
49
+ }
50
+ export interface DropdownMenuSubProps extends React.HTMLAttributes<HTMLDivElement> {
51
+ ref?: React.Ref<HTMLDivElement>;
52
+ }
31
53
  declare function DropdownMenuRoot({ children, className, container, defaultOpen, onOpenChange, open: openProp, placement, sideOffset, collisionPadding, matchTriggerWidth, ...props }: DropdownMenuProps): React.JSX.Element;
32
54
  declare function DropdownMenuTrigger({ className, disabled, onClick, ref, type, ...props }: DropdownMenuTriggerProps): React.JSX.Element;
33
55
  declare function DropdownMenuContent({ children, className, forceMount, sideOffset, collisionPadding, matchTriggerWidth, ref, onKeyDown, ...props }: DropdownMenuContentProps): React.JSX.Element | null;
34
56
  declare function DropdownMenuItem({ children, className, disabled, onClick, onSelect, onKeyDown, ref, ...props }: DropdownMenuItemProps): React.JSX.Element;
35
57
  declare function DropdownMenuSeparator({ className, ref, ...props }: DropdownMenuSeparatorProps): React.JSX.Element;
58
+ declare function DropdownMenuLabel({ className, ref, ...props }: DropdownMenuLabelProps): React.JSX.Element;
59
+ declare function DropdownMenuShortcut({ className, ref, ...props }: DropdownMenuShortcutProps): React.JSX.Element;
60
+ declare function DropdownMenuCheckboxItem({ checked, children, className, defaultChecked, disabled, onCheckedChange, onClick, onKeyDown, onSelect, ref, ...props }: DropdownMenuCheckboxItemProps): React.JSX.Element;
61
+ declare function DropdownMenuRadioGroup({ children, className, onValueChange, ref, value, ...props }: DropdownMenuRadioGroupProps): React.JSX.Element;
62
+ declare function DropdownMenuRadioItem({ children, className, disabled, onClick, onKeyDown, onSelect, ref, value, ...props }: DropdownMenuRadioItemProps): React.JSX.Element;
63
+ declare function DropdownMenuSub({ children, className, ref, ...props }: DropdownMenuSubProps): React.JSX.Element;
36
64
  type DropdownMenuComponent = typeof DropdownMenuRoot & {
65
+ CheckboxItem: typeof DropdownMenuCheckboxItem;
37
66
  Content: typeof DropdownMenuContent;
38
67
  Item: typeof DropdownMenuItem;
68
+ Label: typeof DropdownMenuLabel;
69
+ RadioGroup: typeof DropdownMenuRadioGroup;
70
+ RadioItem: typeof DropdownMenuRadioItem;
39
71
  Separator: typeof DropdownMenuSeparator;
72
+ Shortcut: typeof DropdownMenuShortcut;
73
+ Sub: typeof DropdownMenuSub;
40
74
  Trigger: typeof DropdownMenuTrigger;
41
75
  };
42
76
  export declare const DropdownMenu: DropdownMenuComponent;
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { composeEventHandlers } from "../../utils/compose-event-handlers.js";
5
5
  import { cn } from "../../utils/cn.js";
@@ -35,6 +35,7 @@ function composeRefs(...refs) {
35
35
  function filterEnabledItems(items) {
36
36
  return items.filter((item) => !item.disabled && item.ref !== null);
37
37
  }
38
+ const DropdownMenuRadioGroupContext = React.createContext(null);
38
39
  function DropdownMenuRoot({ children, className, container, defaultOpen = false, onOpenChange, open: openProp, placement = "bottom", sideOffset = 8, collisionPadding = 0, matchTriggerWidth = false, ...props }) {
39
40
  const [open, setOpen] = useControllableState({
40
41
  ...(openProp !== undefined ? { value: openProp } : {}),
@@ -278,9 +279,112 @@ function DropdownMenuItem({ children, className, disabled, onClick, onSelect, on
278
279
  function DropdownMenuSeparator({ className, ref, ...props }) {
279
280
  return (_jsx("div", { ...props, ref: ref, role: "separator", "aria-hidden": "true", className: cn("liano-dropdown-menu__separator", className) }));
280
281
  }
282
+ function DropdownMenuLabel({ className, ref, ...props }) {
283
+ return (_jsx("div", { ...props, ref: ref, className: cn("liano-dropdown-menu__label", className) }));
284
+ }
285
+ function DropdownMenuShortcut({ className, ref, ...props }) {
286
+ return (_jsx("span", { ...props, ref: ref, className: cn("liano-dropdown-menu__shortcut", className) }));
287
+ }
288
+ function DropdownMenuCheckboxItem({ checked, children, className, defaultChecked = false, disabled, onCheckedChange, onClick, onKeyDown, onSelect, ref, ...props }) {
289
+ const context = useDropdownMenuContext("DropdownMenu.CheckboxItem");
290
+ const itemRef = React.useRef(null);
291
+ const itemId = React.useId();
292
+ const isDisabled = disabled === true;
293
+ const [currentChecked, setCurrentChecked] = useControllableState({
294
+ ...(checked !== undefined ? { value: checked } : {}),
295
+ defaultValue: defaultChecked,
296
+ ...(onCheckedChange ? { onChange: onCheckedChange } : {})
297
+ });
298
+ React.useLayoutEffect(() => {
299
+ context.registerItem({
300
+ id: itemId,
301
+ ref: itemRef.current,
302
+ disabled: isDisabled,
303
+ ...(onSelect ? { onSelect } : {})
304
+ });
305
+ return () => context.unregisterItem(itemId);
306
+ }, [context, isDisabled, itemId, onSelect]);
307
+ const select = (event) => {
308
+ if (isDisabled) {
309
+ return;
310
+ }
311
+ setCurrentChecked((value) => !value);
312
+ onSelect?.(event);
313
+ context.setOpen(false);
314
+ };
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();
326
+ select(event);
327
+ }
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 })] }));
329
+ }
330
+ function DropdownMenuRadioGroup({ children, className, onValueChange, ref, value, ...props }) {
331
+ const contextValue = React.useMemo(() => ({
332
+ ...(value !== undefined ? { value } : {}),
333
+ ...(onValueChange ? { onValueChange } : {})
334
+ }), [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 }) }));
336
+ }
337
+ function DropdownMenuRadioItem({ children, className, disabled, onClick, onKeyDown, onSelect, ref, value, ...props }) {
338
+ const context = useDropdownMenuContext("DropdownMenu.RadioItem");
339
+ const radioGroup = React.useContext(DropdownMenuRadioGroupContext);
340
+ const itemRef = React.useRef(null);
341
+ const itemId = React.useId();
342
+ const isDisabled = disabled === true;
343
+ const checked = radioGroup?.value === value;
344
+ React.useLayoutEffect(() => {
345
+ context.registerItem({
346
+ id: itemId,
347
+ ref: itemRef.current,
348
+ disabled: isDisabled,
349
+ ...(onSelect ? { onSelect } : {})
350
+ });
351
+ return () => context.unregisterItem(itemId);
352
+ }, [context, isDisabled, itemId, onSelect]);
353
+ const select = (event) => {
354
+ if (isDisabled) {
355
+ return;
356
+ }
357
+ radioGroup?.onValueChange?.(value);
358
+ onSelect?.(event);
359
+ context.setOpen(false);
360
+ };
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();
372
+ select(event);
373
+ }
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 })] }));
375
+ }
376
+ function DropdownMenuSub({ children, className, ref, ...props }) {
377
+ return (_jsx("div", { ...props, ref: ref, className: cn("liano-dropdown-menu__sub", className), children: children }));
378
+ }
281
379
  export const DropdownMenu = Object.assign(DropdownMenuRoot, {
380
+ CheckboxItem: DropdownMenuCheckboxItem,
282
381
  Content: DropdownMenuContent,
283
382
  Item: DropdownMenuItem,
383
+ Label: DropdownMenuLabel,
384
+ RadioGroup: DropdownMenuRadioGroup,
385
+ RadioItem: DropdownMenuRadioItem,
284
386
  Separator: DropdownMenuSeparator,
387
+ Shortcut: DropdownMenuShortcut,
388
+ Sub: DropdownMenuSub,
285
389
  Trigger: DropdownMenuTrigger
286
390
  });
@@ -1,2 +1,2 @@
1
1
  export { DropdownMenu } from "./dropdown-menu.js";
2
- export type { DropdownMenuContentProps, DropdownMenuItemProps, DropdownMenuProps, DropdownMenuSeparatorProps, DropdownMenuTriggerProps } from "./dropdown-menu.js";
2
+ export type { DropdownMenuCheckboxItemProps, DropdownMenuContentProps, DropdownMenuItemProps, DropdownMenuLabelProps, DropdownMenuProps, DropdownMenuRadioGroupProps, DropdownMenuRadioItemProps, DropdownMenuSeparatorProps, DropdownMenuShortcutProps, DropdownMenuSubProps, DropdownMenuTriggerProps } from "./dropdown-menu.js";
@@ -1,2 +1,2 @@
1
1
  export { Menubar } from "./menubar.js";
2
- export type { MenubarContentProps, MenubarItemProps, MenubarMenuProps, MenubarProps, MenubarSeparatorProps, MenubarTriggerProps } from "./menubar.js";
2
+ export type { MenubarContentProps, MenubarItemProps, MenubarLabelProps, MenubarMenuProps, MenubarProps, MenubarSeparatorProps, MenubarShortcutProps, MenubarTriggerProps } from "./menubar.js";
@@ -22,17 +22,27 @@ export interface MenubarItemProps extends React.ButtonHTMLAttributes<HTMLButtonE
22
22
  export interface MenubarSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
23
23
  ref?: React.Ref<HTMLDivElement>;
24
24
  }
25
+ export interface MenubarLabelProps extends React.HTMLAttributes<HTMLDivElement> {
26
+ ref?: React.Ref<HTMLDivElement>;
27
+ }
28
+ export interface MenubarShortcutProps extends React.HTMLAttributes<HTMLSpanElement> {
29
+ ref?: React.Ref<HTMLSpanElement>;
30
+ }
25
31
  declare function MenubarRoot({ children, className, defaultValue, onKeyDown, onValueChange, ref, value, ...props }: MenubarProps): React.JSX.Element;
26
32
  declare function MenubarMenu({ children, value }: MenubarMenuProps): React.JSX.Element;
27
33
  declare function MenubarTrigger({ children, className, disabled, onClick, onKeyDown, ref, type, ...props }: MenubarTriggerProps): React.JSX.Element;
28
34
  declare function MenubarContent({ children, className, onKeyDown, ref, ...props }: MenubarContentProps): React.JSX.Element | null;
29
35
  declare function MenubarItem({ children, className, disabled, onClick, onKeyDown, onSelect, ref, type, ...props }: MenubarItemProps): React.JSX.Element;
30
36
  declare function MenubarSeparator({ className, ref, ...props }: MenubarSeparatorProps): React.JSX.Element;
37
+ declare function MenubarLabel({ className, ref, ...props }: MenubarLabelProps): React.JSX.Element;
38
+ declare function MenubarShortcut({ className, ref, ...props }: MenubarShortcutProps): React.JSX.Element;
31
39
  type MenubarComponent = typeof MenubarRoot & {
32
40
  Content: typeof MenubarContent;
33
41
  Item: typeof MenubarItem;
42
+ Label: typeof MenubarLabel;
34
43
  Menu: typeof MenubarMenu;
35
44
  Separator: typeof MenubarSeparator;
45
+ Shortcut: typeof MenubarShortcut;
36
46
  Trigger: typeof MenubarTrigger;
37
47
  };
38
48
  export declare const Menubar: MenubarComponent;
@@ -205,10 +205,18 @@ function MenubarItem({ children, className, disabled, onClick, onKeyDown, onSele
205
205
  function MenubarSeparator({ className, ref, ...props }) {
206
206
  return _jsx("div", { ...props, ref: ref, role: "separator", "aria-hidden": "true", className: cn("liano-menubar__separator", className) });
207
207
  }
208
+ function MenubarLabel({ className, ref, ...props }) {
209
+ return _jsx("div", { ...props, ref: ref, className: cn("liano-menubar__label", className) });
210
+ }
211
+ function MenubarShortcut({ className, ref, ...props }) {
212
+ return _jsx("span", { ...props, ref: ref, className: cn("liano-menubar__shortcut", className) });
213
+ }
208
214
  export const Menubar = Object.assign(MenubarRoot, {
209
215
  Content: MenubarContent,
210
216
  Item: MenubarItem,
217
+ Label: MenubarLabel,
211
218
  Menu: MenubarMenu,
212
219
  Separator: MenubarSeparator,
220
+ Shortcut: MenubarShortcut,
213
221
  Trigger: MenubarTrigger
214
222
  });
@@ -1,4 +1,5 @@
1
1
  import * as React from "react";
2
+ import type { PortalProps } from "../../internal/portal/index.js";
2
3
  import type { FloatingPlacement } from "../../internal/floating/floating-types.js";
3
4
  export interface PopoverProps extends React.HTMLAttributes<HTMLDivElement> {
4
5
  open?: boolean;
@@ -16,11 +17,14 @@ export interface PopoverContentProps extends React.HTMLAttributes<HTMLDivElement
16
17
  collisionPadding?: number;
17
18
  matchTriggerWidth?: boolean;
18
19
  forceMount?: boolean;
20
+ container?: PortalProps["container"];
21
+ disableOutsidePointerDown?: boolean;
22
+ disableEscapeKeyDown?: boolean;
19
23
  ref?: React.Ref<HTMLDivElement>;
20
24
  }
21
25
  declare function PopoverRoot({ children, className, defaultOpen, modal, onOpenChange, open, placement, ref, ...props }: PopoverProps): React.JSX.Element;
22
26
  declare function PopoverTrigger({ className, onClick, ref, type, ...props }: PopoverTriggerProps): React.JSX.Element;
23
- declare function PopoverContent({ className, forceMount, id, ref, sideOffset, collisionPadding, matchTriggerWidth, style, ...props }: PopoverContentProps): React.JSX.Element | null;
27
+ declare function PopoverContent({ className, container, forceMount, id, disableEscapeKeyDown, disableOutsidePointerDown, ref, sideOffset, collisionPadding, matchTriggerWidth, style, ...props }: PopoverContentProps): React.JSX.Element | null;
24
28
  export declare const Popover: typeof PopoverRoot & {
25
29
  Trigger: typeof PopoverTrigger;
26
30
  Content: typeof PopoverContent;
@@ -108,7 +108,7 @@ function PopoverTrigger({ className, onClick, ref, type = "button", ...props })
108
108
  }, [context.referenceRef, ref]);
109
109
  return (_jsx("button", { ...props, ref: handleRef, type: type, className: cn("liano-popover__trigger", className), "aria-expanded": context.open, "aria-controls": context.contentId, "aria-haspopup": "dialog", "data-state": context.open ? "open" : "closed", onClick: composeEventHandlers(onClick, handleClick) }));
110
110
  }
111
- function PopoverContent({ className, forceMount = false, id, ref, sideOffset = 0, collisionPadding = 0, matchTriggerWidth = false, style, ...props }) {
111
+ function PopoverContent({ className, container, forceMount = false, id, disableEscapeKeyDown = false, disableOutsidePointerDown = false, ref, sideOffset = 0, collisionPadding = 0, matchTriggerWidth = false, style, ...props }) {
112
112
  const context = usePopoverContext("Popover.Content");
113
113
  const { defaultContentId, setContentId } = context;
114
114
  const transformOrigin = getTransformOrigin(context.floatingPlacement);
@@ -154,9 +154,9 @@ function PopoverContent({ className, forceMount = false, id, ref, sideOffset = 0
154
154
  }
155
155
  const content = (_jsx("div", { ...props, ref: handleRef, id: contentId, role: "dialog", className: cn("liano-popover__content", className), "data-align": placementParts.align, "data-side": placementParts.side, "data-state": context.open ? "open" : "closed", hidden: context.open ? undefined : true, style: contentStyle }));
156
156
  if (!context.open) {
157
- return _jsx(Portal, { children: content });
157
+ return _jsx(Portal, { ...(container === undefined ? {} : { container }), children: content });
158
158
  }
159
- return (_jsx(Portal, { children: _jsx(DismissableLayer, { open: true, onDismiss: close, modal: context.modal, onPointerDownOutside: ignoreTriggerPointerDown, children: content }) }));
159
+ return (_jsx(Portal, { ...(container === undefined ? {} : { container }), children: _jsx(DismissableLayer, { open: true, disableEscapeKeyDown: disableEscapeKeyDown, disableOutsidePointerDown: disableOutsidePointerDown, onDismiss: close, modal: context.modal, onPointerDownOutside: ignoreTriggerPointerDown, children: content }) }));
160
160
  }
161
161
  export const Popover = Object.assign(PopoverRoot, {
162
162
  Trigger: PopoverTrigger,
@@ -1,2 +1,2 @@
1
1
  export { Select } from "./select.js";
2
- export type { SelectContentProps, SelectItemProps, SelectProps, SelectSize, SelectTriggerProps, SelectValueProps } from "./select.js";
2
+ export type { SelectClearProps, SelectContentProps, SelectGroupProps, SelectItemProps, SelectLabelProps, SelectProps, SelectSeparatorProps, SelectSize, SelectTriggerProps, SelectValue, SelectValueProps } from "./select.js";