@xingxingmofashu/ui 0.2.1 → 0.3.0

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.
@@ -5,9 +5,9 @@ import { type ComponentConfig } from "../internal/tv";
5
5
  import theme from "../theme/accordion";
6
6
  type Accordion = ComponentConfig<typeof theme>;
7
7
  /** One panel managed by the accordion. */
8
- type AccordionItemProps<Value extends string = string> = Omit<UIAccordion.Item.Props, "children" | "className" | "content" | "title" | "value"> & {
8
+ type AccordionItemProps = Omit<UIAccordion.Item.Props, "children" | "className" | "content" | "title" | "value"> & {
9
9
  /** Value that identifies the item in the accordion value. */
10
- value: Value;
10
+ value: any;
11
11
  /** Visible heading inside the trigger. */
12
12
  title: React.ReactNode;
13
13
  /** Panel content. */
@@ -19,14 +19,14 @@ type AccordionItemProps<Value extends string = string> = Omit<UIAccordion.Item.P
19
19
  /** Tailwind classes for this item's internal slots, keyed by `data-slot`. */
20
20
  ui?: Partial<Pick<Accordion["slots"], "item" | "header" | "trigger" | "title" | "indicator" | "indicatorIcon" | "panel" | "panelContent">>;
21
21
  };
22
- type AccordionProps<Value extends string = string> = Omit<UIAccordion.Root.Props<Value>, "className" | "children"> & VariantProps<typeof theme> & {
22
+ type AccordionProps<Value = any> = Omit<UIAccordion.Root.Props<Value>, "className" | "children"> & VariantProps<typeof theme> & {
23
23
  /** Panels rendered by the accordion. */
24
- items: readonly AccordionItemProps<Value>[];
24
+ items: readonly AccordionItemProps[];
25
25
  /** Tailwind classes merged into the root. */
26
26
  className?: string;
27
27
  /** Tailwind classes for internal slots, keyed by `data-slot`. */
28
28
  ui?: Accordion["slots"];
29
29
  };
30
- declare function Accordion<Value extends string = string>({ className, ui, size, items, ...props }: AccordionProps<Value>): React.JSX.Element;
30
+ declare function Accordion<Value = any>({ className, ui, size, items, ...props }: AccordionProps<Value>): React.JSX.Element;
31
31
  export { Accordion };
32
32
  export type { AccordionItemProps, AccordionProps };
@@ -35,10 +35,10 @@ function Accordion({
35
35
  }
36
36
  )
37
37
  ),
38
- /* @__PURE__ */ React.createElement("div", { "data-slot": "content", className: cn(styles.content(), ui?.content) }, items.map((item) => /* @__PURE__ */ React.createElement(
38
+ /* @__PURE__ */ React.createElement("div", { "data-slot": "content", className: cn(styles.content(), ui?.content) }, items.map((item, index) => /* @__PURE__ */ React.createElement(
39
39
  AccordionItem,
40
40
  {
41
- key: item.value,
41
+ key: index,
42
42
  ...item,
43
43
  ui: {
44
44
  item: cn(ui?.item, item.ui?.item),
@@ -6,9 +6,9 @@ import { type ComponentConfig } from "../internal/tv";
6
6
  import theme from "../theme/checkbox-group";
7
7
  type CheckboxGroup = ComponentConfig<typeof theme>;
8
8
  /** One entry in the group. */
9
- type CheckboxGroupItemProps<Value extends string = string> = Omit<UICheckbox.Root.Props, "checked" | "defaultChecked" | "children" | "className" | "onCheckedChange" | "parent" | "value"> & {
9
+ type CheckboxGroupItemProps = Omit<UICheckbox.Root.Props, "checked" | "defaultChecked" | "children" | "className" | "onCheckedChange" | "parent" | "value"> & {
10
10
  /** Value that identifies the option in the group value. */
11
- value: Value;
11
+ value: string;
12
12
  /** Visible label rendered next to the control. */
13
13
  label?: React.ReactNode;
14
14
  /** Supporting text rendered under the label. */
@@ -18,24 +18,24 @@ type CheckboxGroupItemProps<Value extends string = string> = Omit<UICheckbox.Roo
18
18
  /** Tailwind classes for control slots, keyed by `data-slot`. */
19
19
  ui?: Partial<Pick<CheckboxGroup["slots"], "item" | "box" | "fill" | "glass" | "overlay" | "indicator" | "indicatorIcon" | "wrapper" | "itemLabel" | "itemDescription">>;
20
20
  };
21
- type CheckboxGroupProps<Value extends string = string> = Omit<UICheckboxGroup.Props, "allValues" | "children" | "className" | "defaultValue" | "onValueChange" | "value"> & VariantProps<typeof theme> & Pick<UICheckbox.Root.Props, "name" | "form" | "readOnly"> & {
21
+ type CheckboxGroupProps = Omit<UICheckboxGroup.Props, "allValues" | "children" | "className" | "defaultValue" | "onValueChange" | "value"> & VariantProps<typeof theme> & Pick<UICheckbox.Root.Props, "name" | "form" | "readOnly"> & {
22
22
  /** Entries rendered by the group. */
23
- items: readonly CheckboxGroupItemProps<Value>[];
23
+ items: readonly CheckboxGroupItemProps[];
24
24
  /** Visible label for the group. */
25
25
  label?: React.ReactNode;
26
26
  /** Supporting text rendered under the group label. */
27
27
  description?: React.ReactNode;
28
28
  /** Controlled checked values. */
29
- value?: readonly Value[];
29
+ value?: readonly string[];
30
30
  /** Initial checked values when uncontrolled. */
31
- defaultValue?: readonly Value[];
31
+ defaultValue?: readonly string[];
32
32
  /** Called when a checkbox in the group is ticked or unticked. */
33
- onValueChange?: (value: Value[], details: UICheckboxGroup.ChangeEventDetails) => void;
33
+ onValueChange?: (value: string[], details: UICheckboxGroup.ChangeEventDetails) => void;
34
34
  /** Tailwind classes merged into the group root. */
35
35
  className?: string;
36
36
  /** Tailwind classes for internal slots, keyed by `data-slot`. */
37
37
  ui?: CheckboxGroup["slots"];
38
38
  };
39
- declare function CheckboxGroup<Value extends string = string>({ className, ui, size, items, label, description, name, form, readOnly, disabled, value, defaultValue, onValueChange, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: CheckboxGroupProps<Value>): React.JSX.Element;
39
+ declare function CheckboxGroup({ className, ui, size, items, label, description, name, form, readOnly, disabled, value, defaultValue, onValueChange, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: CheckboxGroupProps): React.JSX.Element;
40
40
  export { CheckboxGroup };
41
41
  export type { CheckboxGroupItemProps, CheckboxGroupProps };
@@ -44,13 +44,13 @@ function CheckboxGroup({
44
44
  UICheckboxGroup,
45
45
  {
46
46
  ...props,
47
+ value,
48
+ defaultValue,
49
+ onValueChange: handleValueChange,
47
50
  "aria-describedby": describedBy,
48
51
  "aria-label": ariaLabel,
49
52
  ...labelledBy ? { "aria-labelledby": labelledBy } : {},
50
53
  disabled: fieldsetDisabled,
51
- value,
52
- defaultValue,
53
- onValueChange: handleValueChange,
54
54
  "data-slot": "root",
55
55
  className: cn(styles.root(), ui?.root, className)
56
56
  },
@@ -4,10 +4,10 @@ import { type ComponentConfig } from "../internal/tv";
4
4
  import theme from "../theme/menu";
5
5
  type Menu = ComponentConfig<typeof theme>;
6
6
  /** One action item in the menu. */
7
- type MenuItemProps<Value extends string = string> = Omit<UIMenu.Item.Props, "children" | "className" | "label" | "onClick"> & {
7
+ type MenuItemProps = Omit<UIMenu.Item.Props, "children" | "className" | "label" | "onClick"> & {
8
8
  type?: "item";
9
9
  /** Value that identifies the item in the menu. */
10
- value: Value;
10
+ value: string;
11
11
  /** Visible content inside the item. */
12
12
  label: React.ReactNode;
13
13
  /** Plain-text label used for keyboard navigation. Defaults to the rendered text. */
@@ -24,12 +24,12 @@ type MenuItemProps<Value extends string = string> = Omit<UIMenu.Item.Props, "chi
24
24
  ui?: Partial<Pick<Menu["slots"], "item" | "itemIcon" | "itemLabel" | "shortcut">>;
25
25
  };
26
26
  /** A labelled group of menu items. */
27
- type MenuGroupProps<Value extends string = string> = Omit<UIMenu.Group.Props, "children" | "className"> & {
27
+ type MenuGroupProps = Omit<UIMenu.Group.Props, "children" | "className"> & {
28
28
  type: "group";
29
29
  /** Group heading. */
30
30
  label: React.ReactNode;
31
31
  /** Items rendered inside the group. */
32
- items: readonly MenuItemProps<Value>[];
32
+ items: readonly MenuItemProps[];
33
33
  /** Tailwind classes merged into the group. */
34
34
  className?: string;
35
35
  /** Tailwind classes for the group slots. */
@@ -43,11 +43,11 @@ type MenuSeparatorProps = Omit<UIMenu.Separator.Props, "children" | "className"
43
43
  /** Tailwind classes for the separator slot. */
44
44
  ui?: Partial<Pick<Menu["slots"], "separator">>;
45
45
  };
46
- type MenuProps<Value extends string = string> = Omit<UIMenu.Root.Props, "children" | "className"> & Pick<UIMenu.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "collisionPadding"> & {
46
+ type MenuProps = Omit<UIMenu.Root.Props, "children" | "className"> & Pick<UIMenu.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "collisionPadding"> & {
47
47
  /** Element that opens the menu. It must forward props and refs. */
48
48
  trigger: React.ReactElement;
49
49
  /** Items, groups, and separators rendered by the menu. */
50
- items: readonly (MenuItemProps<Value> | MenuGroupProps<Value> | MenuSeparatorProps)[];
50
+ items: readonly (MenuItemProps | MenuGroupProps | MenuSeparatorProps)[];
51
51
  /** Tailwind classes merged into the popup. */
52
52
  className?: string;
53
53
  /** Inline styles merged into the popup. */
@@ -55,6 +55,6 @@ type MenuProps<Value extends string = string> = Omit<UIMenu.Root.Props, "childre
55
55
  /** Tailwind classes for internal slots, keyed by `data-slot`. */
56
56
  ui?: Menu["slots"];
57
57
  };
58
- declare function Menu<Value extends string = string>({ trigger, items, side, align, sideOffset, alignOffset, collisionPadding, className, style, ui, ...props }: MenuProps<Value>): React.JSX.Element;
58
+ declare function Menu({ trigger, items, side, align, sideOffset, alignOffset, collisionPadding, className, style, ui, ...props }: MenuProps): React.JSX.Element;
59
59
  export { Menu };
60
60
  export type { MenuGroupProps, MenuItemProps, MenuProps, MenuSeparatorProps, };
@@ -87,7 +87,7 @@ function Menu({
87
87
  return /* @__PURE__ */ React.createElement(
88
88
  MenuActionItem,
89
89
  {
90
- key: entry.value,
90
+ key: index,
91
91
  ...entry,
92
92
  ui: {
93
93
  item: cn(ui?.item, entry.ui?.item),
@@ -174,10 +174,10 @@ function MenuGroup({
174
174
  },
175
175
  label
176
176
  ),
177
- items.map((item) => /* @__PURE__ */ React.createElement(
177
+ items.map((item, index) => /* @__PURE__ */ React.createElement(
178
178
  MenuActionItem,
179
179
  {
180
- key: item.value,
180
+ key: index,
181
181
  ...item,
182
182
  ui: {
183
183
  item: cn(ui?.item, item.ui?.item),
@@ -6,7 +6,7 @@ import { type ComponentConfig } from "../internal/tv";
6
6
  import theme from "../theme/radio-group";
7
7
  type RadioGroup = ComponentConfig<typeof theme>;
8
8
  /** One option managed by the radio group. */
9
- type RadioGroupItemProps<Value extends string = string> = Omit<UIRadio.Root.Props<Value>, "className" | "children" | "value"> & {
9
+ type RadioGroupItemProps<Value = any> = Omit<UIRadio.Root.Props<Value>, "className" | "children" | "value"> & {
10
10
  /** Value that identifies the option in the group value. */
11
11
  value: Value;
12
12
  /** Visible label rendered next to the control. */
@@ -18,7 +18,7 @@ type RadioGroupItemProps<Value extends string = string> = Omit<UIRadio.Root.Prop
18
18
  /** Tailwind classes for this item's internal slots, keyed by `data-slot`. */
19
19
  ui?: Partial<Pick<RadioGroup["slots"], "item" | "box" | "glass" | "overlay" | "indicator" | "dot" | "wrapper" | "itemLabel" | "itemDescription">>;
20
20
  };
21
- type RadioGroupProps<Value extends string = string> = Omit<UIRadioGroup.Props<Value>, "className" | "children"> & VariantProps<typeof theme> & {
21
+ type RadioGroupProps<Value = any> = Omit<UIRadioGroup.Props<Value>, "className" | "children"> & VariantProps<typeof theme> & {
22
22
  /** Options rendered by the radio group. */
23
23
  items: readonly RadioGroupItemProps<Value>[];
24
24
  /** Visible label for the group. */
@@ -30,6 +30,6 @@ type RadioGroupProps<Value extends string = string> = Omit<UIRadioGroup.Props<Va
30
30
  /** Tailwind classes for internal slots, keyed by `data-slot`. */
31
31
  ui?: RadioGroup["slots"];
32
32
  };
33
- declare function RadioGroup<Value extends string = string>({ className, ui, size, items, label, description, disabled, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...props }: RadioGroupProps<Value>): React.JSX.Element;
33
+ declare function RadioGroup<Value>({ className, ui, size, items, label, description, disabled, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...props }: RadioGroupProps<Value>): React.JSX.Element;
34
34
  export { RadioGroup };
35
35
  export type { RadioGroupItemProps, RadioGroupProps };
@@ -59,10 +59,10 @@ function RadioGroup({
59
59
  },
60
60
  description
61
61
  ) : null) : null,
62
- /* @__PURE__ */ React.createElement("div", { "data-slot": "items", className: cn(styles.items(), ui?.items) }, items.map((item) => /* @__PURE__ */ React.createElement(
62
+ /* @__PURE__ */ React.createElement("div", { "data-slot": "items", className: cn(styles.items(), ui?.items) }, items.map((item, index) => /* @__PURE__ */ React.createElement(
63
63
  RadioGroupItem,
64
64
  {
65
- key: item.value,
65
+ key: index,
66
66
  ...item,
67
67
  ui: {
68
68
  item: cn(ui?.item, item.ui?.item),
@@ -5,9 +5,9 @@ import { type ComponentConfig } from "../internal/tv";
5
5
  import theme from "../theme/select";
6
6
  type Select = ComponentConfig<typeof theme>;
7
7
  /** One option rendered by the select. */
8
- type SelectItemProps<Value extends string = string> = {
8
+ type SelectItemProps = {
9
9
  /** Value submitted and passed to `onValueChange`. */
10
- value: Value;
10
+ value: any;
11
11
  /** Visible content inside the option and the trigger. */
12
12
  label: React.ReactNode;
13
13
  /** Plain-text label used for keyboard navigation. Defaults to a string label. */
@@ -15,21 +15,31 @@ type SelectItemProps<Value extends string = string> = {
15
15
  /** Whether the option is disabled. */
16
16
  disabled?: boolean;
17
17
  };
18
- type SelectProps<Value extends string = string> = Omit<UISelect.Root.Props<Value>, "children" | "className" | "items" | "multiple" | "value" | "defaultValue" | "onValueChange"> & VariantProps<typeof theme> & Pick<UISelect.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "collisionPadding" | "alignItemWithTrigger"> & {
19
- /** Options rendered by the select. */
20
- items: readonly SelectItemProps<Value>[];
18
+ /** A labelled group of options. */
19
+ type SelectItemGroupProps = {
20
+ /** Visible heading rendered above the group. */
21
+ label: React.ReactNode;
22
+ /** Options in the group. */
23
+ items: readonly SelectItemProps[];
24
+ };
25
+ /** An option or a labelled group of options. */
26
+ type SelectEntry = SelectItemProps | SelectItemGroupProps;
27
+ /** Value shape exposed to `renderValue` for the active selection mode. */
28
+ type SelectValue<Value, Multiple extends boolean | undefined> = Multiple extends true ? readonly Value[] : Value | null;
29
+ type SelectProps<Value, Multiple extends boolean | undefined = false> = Omit<UISelect.Root.Props<Value, Multiple>, "children" | "className" | "items"> & VariantProps<typeof theme> & Pick<UISelect.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "collisionPadding" | "alignItemWithTrigger"> & {
30
+ /** Options rendered by the select, optionally grouped. */
31
+ items: readonly SelectEntry[];
21
32
  /** Visible label associated with the trigger. */
22
33
  label?: React.ReactNode;
23
34
  /** Supporting text under the trigger. */
24
35
  description?: React.ReactNode;
25
36
  /** Text shown when no value is selected. */
26
37
  placeholder?: React.ReactNode;
27
- /** Controlled value. */
28
- value?: Value | null;
29
- /** Initial value when uncontrolled. */
30
- defaultValue?: Value | null;
31
- /** Called when the selected value changes. */
32
- onValueChange?: (value: Value | null, details: UISelect.Root.ChangeEventDetails) => void;
38
+ /**
39
+ * Formats the selected value inside the trigger.
40
+ * When provided, it overrides `placeholder`.
41
+ */
42
+ renderValue?: (value: SelectValue<Value, Multiple>) => React.ReactNode;
33
43
  /** Accessible name for the trigger when no visible label is rendered. */
34
44
  "aria-label"?: string;
35
45
  /** Names the trigger from another element. */
@@ -43,6 +53,6 @@ type SelectProps<Value extends string = string> = Omit<UISelect.Root.Props<Value
43
53
  /** Tailwind classes for internal slots, keyed by `data-slot`. */
44
54
  ui?: Select["slots"];
45
55
  };
46
- declare function Select<Value extends string = string>({ className, ui, size, items, label, description, placeholder, value, defaultValue, onValueChange, side, align, sideOffset, alignOffset, collisionPadding, alignItemWithTrigger, disabled, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, style, ...props }: SelectProps<Value>): React.JSX.Element;
56
+ declare function Select<Value, Multiple extends boolean | undefined = false>({ className, ui, size, items, label, description, placeholder, renderValue, side, align, sideOffset, alignOffset, collisionPadding, alignItemWithTrigger, disabled, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, style, ...props }: SelectProps<Value, Multiple>): React.JSX.Element;
47
57
  export { Select };
48
- export type { SelectItemProps, SelectProps };
58
+ export type { SelectEntry, SelectItemGroupProps, SelectItemProps, SelectProps, };
@@ -15,9 +15,7 @@ function Select({
15
15
  label,
16
16
  description,
17
17
  placeholder,
18
- value,
19
- defaultValue,
20
- onValueChange,
18
+ renderValue,
21
19
  side,
22
20
  align,
23
21
  sideOffset = 6,
@@ -43,157 +41,169 @@ function Select({
43
41
  ...ariaLabelledBy ? { "aria-labelledby": ariaLabelledBy } : {},
44
42
  ...describedBy ? { "aria-describedby": describedBy } : {}
45
43
  };
46
- return /* @__PURE__ */ React.createElement(
47
- UISelect.Root,
44
+ const flatItems = items.flatMap(
45
+ (entry) => "items" in entry ? entry.items : [entry]
46
+ );
47
+ function renderItem(item, key) {
48
+ return /* @__PURE__ */ React.createElement(
49
+ UISelect.Item,
50
+ {
51
+ key,
52
+ value: item.value,
53
+ disabled: item.disabled,
54
+ label: item.textValue ?? (typeof item.label === "string" ? item.label : void 0),
55
+ "data-slot": "item",
56
+ className: cn(styles.item(), ui?.item)
57
+ },
58
+ /* @__PURE__ */ React.createElement(
59
+ UISelect.ItemIndicator,
60
+ {
61
+ keepMounted: true,
62
+ "data-slot": "item-indicator",
63
+ className: cn(styles.itemIndicator(), ui?.itemIndicator)
64
+ },
65
+ /* @__PURE__ */ React.createElement(CheckIcon, null)
66
+ ),
67
+ /* @__PURE__ */ React.createElement(
68
+ UISelect.ItemText,
69
+ {
70
+ "data-slot": "item-text",
71
+ className: cn(styles.itemText(), ui?.itemText)
72
+ },
73
+ item.label
74
+ )
75
+ );
76
+ }
77
+ return /* @__PURE__ */ React.createElement(UISelect.Root, { ...props, items: flatItems, disabled: fieldsetDisabled }, /* @__PURE__ */ React.createElement(
78
+ "div",
48
79
  {
49
- ...props,
50
- items,
51
- value,
52
- defaultValue,
53
- onValueChange,
54
- disabled: fieldsetDisabled
80
+ "data-slot": "root",
81
+ "data-disabled": fieldsetDisabled || void 0,
82
+ className: cn(styles.root(), ui?.root, className),
83
+ style
55
84
  },
85
+ label ? /* @__PURE__ */ React.createElement(
86
+ UISelect.Label,
87
+ {
88
+ "data-slot": "label",
89
+ className: cn(styles.label(), ui?.label)
90
+ },
91
+ label
92
+ ) : null,
56
93
  /* @__PURE__ */ React.createElement(
57
- "div",
94
+ UISelect.Trigger,
58
95
  {
59
- "data-slot": "root",
60
- "data-disabled": fieldsetDisabled || void 0,
61
- className: cn(styles.root(), ui?.root, className),
62
- style
96
+ "data-slot": "trigger",
97
+ ...triggerAriaProps,
98
+ disabled: fieldsetDisabled,
99
+ className: cn(styles.trigger(), ui?.trigger)
63
100
  },
64
- label ? /* @__PURE__ */ React.createElement(
65
- UISelect.Label,
66
- {
67
- "data-slot": "label",
68
- className: cn(styles.label(), ui?.label)
69
- },
70
- label
71
- ) : null,
72
101
  /* @__PURE__ */ React.createElement(
73
- UISelect.Trigger,
102
+ "span",
74
103
  {
75
- "data-slot": "trigger",
76
- ...triggerAriaProps,
77
- disabled: fieldsetDisabled,
78
- className: cn(styles.trigger(), ui?.trigger)
104
+ "aria-hidden": "true",
105
+ "data-slot": "glass",
106
+ className: cn(styles.glass(), ui?.glass)
79
107
  },
80
108
  /* @__PURE__ */ React.createElement(
81
109
  "span",
82
110
  {
83
111
  "aria-hidden": "true",
84
- "data-slot": "glass",
85
- className: cn(styles.glass(), ui?.glass)
86
- },
87
- /* @__PURE__ */ React.createElement(
88
- "span",
89
- {
90
- "aria-hidden": "true",
91
- "data-slot": "overlay",
92
- className: cn(styles.overlay(), ui?.overlay)
93
- }
94
- )
95
- ),
96
- /* @__PURE__ */ React.createElement(
97
- UISelect.Value,
98
- {
99
- "data-slot": "value",
100
- placeholder,
101
- className: cn(styles.value(), ui?.value)
112
+ "data-slot": "overlay",
113
+ className: cn(styles.overlay(), ui?.overlay)
102
114
  }
103
- ),
104
- /* @__PURE__ */ React.createElement(
105
- UISelect.Icon,
106
- {
107
- "data-slot": "icon",
108
- className: cn(styles.icon(), ui?.icon)
109
- },
110
- /* @__PURE__ */ React.createElement(ChevronDownIcon, null)
111
115
  )
112
116
  ),
113
- description ? /* @__PURE__ */ React.createElement(
114
- "span",
117
+ /* @__PURE__ */ React.createElement(
118
+ UISelect.Value,
115
119
  {
116
- id: descriptionId,
117
- "data-slot": "description",
118
- className: cn(styles.description(), ui?.description)
120
+ "data-slot": "value",
121
+ placeholder,
122
+ className: cn(styles.value(), ui?.value)
119
123
  },
120
- description
121
- ) : null
124
+ renderValue
125
+ ),
126
+ /* @__PURE__ */ React.createElement(
127
+ UISelect.Icon,
128
+ {
129
+ "data-slot": "icon",
130
+ className: cn(styles.icon(), ui?.icon)
131
+ },
132
+ /* @__PURE__ */ React.createElement(ChevronDownIcon, null)
133
+ )
122
134
  ),
123
- /* @__PURE__ */ React.createElement(UISelect.Portal, null, /* @__PURE__ */ React.createElement(
124
- UISelect.Positioner,
135
+ description ? /* @__PURE__ */ React.createElement(
136
+ "span",
137
+ {
138
+ id: descriptionId,
139
+ "data-slot": "description",
140
+ className: cn(styles.description(), ui?.description)
141
+ },
142
+ description
143
+ ) : null
144
+ ), /* @__PURE__ */ React.createElement(UISelect.Portal, null, /* @__PURE__ */ React.createElement(
145
+ UISelect.Positioner,
146
+ {
147
+ "data-slot": "positioner",
148
+ side,
149
+ align,
150
+ sideOffset,
151
+ alignOffset,
152
+ collisionPadding,
153
+ alignItemWithTrigger,
154
+ className: cn(styles.positioner(), ui?.positioner)
155
+ },
156
+ /* @__PURE__ */ React.createElement(
157
+ UISelect.Popup,
125
158
  {
126
- "data-slot": "positioner",
127
- side,
128
- align,
129
- sideOffset,
130
- alignOffset,
131
- collisionPadding,
132
- alignItemWithTrigger,
133
- className: cn(styles.positioner(), ui?.positioner)
159
+ "data-slot": "popup",
160
+ className: cn(styles.popup(), ui?.popup)
134
161
  },
135
162
  /* @__PURE__ */ React.createElement(
136
- UISelect.Popup,
163
+ "span",
137
164
  {
138
- "data-slot": "popup",
139
- className: cn(styles.popup(), ui?.popup)
165
+ "aria-hidden": "true",
166
+ "data-slot": "popup-glass",
167
+ className: cn(styles.popupGlass(), ui?.popupGlass)
140
168
  },
141
169
  /* @__PURE__ */ React.createElement(
142
170
  "span",
143
171
  {
144
172
  "aria-hidden": "true",
145
- "data-slot": "popup-glass",
146
- className: cn(styles.popupGlass(), ui?.popupGlass)
147
- },
148
- /* @__PURE__ */ React.createElement(
149
- "span",
150
- {
151
- "aria-hidden": "true",
152
- "data-slot": "popup-overlay",
153
- className: cn(styles.popupOverlay(), ui?.popupOverlay)
154
- }
155
- )
156
- ),
157
- /* @__PURE__ */ React.createElement(
158
- UISelect.List,
159
- {
160
- "data-slot": "list",
161
- className: cn(styles.list(), ui?.list)
162
- },
163
- items.map((item) => /* @__PURE__ */ React.createElement(
164
- UISelect.Item,
173
+ "data-slot": "popup-overlay",
174
+ className: cn(styles.popupOverlay(), ui?.popupOverlay)
175
+ }
176
+ )
177
+ ),
178
+ /* @__PURE__ */ React.createElement(
179
+ UISelect.List,
180
+ {
181
+ "data-slot": "list",
182
+ className: cn(styles.list(), ui?.list)
183
+ },
184
+ items.map(
185
+ (entry, index) => "items" in entry ? /* @__PURE__ */ React.createElement(
186
+ UISelect.Group,
165
187
  {
166
- key: item.value,
167
- value: item.value,
168
- disabled: item.disabled,
169
- label: item.textValue ?? (typeof item.label === "string" ? item.label : void 0),
170
- "data-slot": "item",
171
- className: cn(styles.item(), ui?.item)
188
+ key: `group-${index}`,
189
+ "data-slot": "group",
190
+ className: cn(styles.group(), ui?.group)
172
191
  },
173
192
  /* @__PURE__ */ React.createElement(
174
- UISelect.ItemIndicator,
193
+ UISelect.GroupLabel,
175
194
  {
176
- keepMounted: true,
177
- "data-slot": "item-indicator",
178
- className: cn(
179
- styles.itemIndicator(),
180
- ui?.itemIndicator
181
- )
195
+ "data-slot": "group-label",
196
+ className: cn(styles.groupLabel(), ui?.groupLabel)
182
197
  },
183
- /* @__PURE__ */ React.createElement(CheckIcon, null)
198
+ entry.label
184
199
  ),
185
- /* @__PURE__ */ React.createElement(
186
- UISelect.ItemText,
187
- {
188
- "data-slot": "item-text",
189
- className: cn(styles.itemText(), ui?.itemText)
190
- },
191
- item.label
200
+ entry.items.map(
201
+ (item, itemIndex) => renderItem(item, itemIndex)
192
202
  )
193
- ))
203
+ ) : renderItem(entry, index)
194
204
  )
195
205
  )
196
- ))
197
- );
206
+ )
207
+ )));
198
208
  }
199
209
  export { Select };
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  import { type ComponentConfig } from "../internal/tv";
4
4
  import theme from "../theme/slider";
5
5
  type Slider = ComponentConfig<typeof theme>;
6
- type SliderProps<Value extends number | readonly number[] = number> = Omit<UISlider.Root.Props<Value>, "children" | "className"> & Pick<UISlider.Thumb.Props, "getAriaLabel" | "getAriaValueText"> & {
6
+ type SliderProps<Value extends number | readonly number[] = number | readonly number[]> = Omit<UISlider.Root.Props<Value>, "children" | "className"> & Pick<UISlider.Thumb.Props, "getAriaLabel" | "getAriaValueText"> & {
7
7
  /** Tailwind classes merged into the root. */
8
8
  className?: string;
9
9
  /** Tailwind classes for internal slots, keyed by `data-slot`. */
@@ -14,6 +14,6 @@ type SliderProps<Value extends number | readonly number[] = number> = Omit<UISli
14
14
  * both are forwarded from the root to every thumb. `aria-describedby` is also
15
15
  * forwarded so supporting copy is announced with each thumb.
16
16
  */
17
- declare function Slider<Value extends number | readonly number[] = number>({ className, ui, value, defaultValue, disabled, "aria-describedby": ariaDescribedBy, "aria-errormessage": ariaErrorMessage, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, getAriaLabel, getAriaValueText, ...props }: SliderProps<Value>): React.JSX.Element;
17
+ declare function Slider<Value extends number | readonly number[]>({ className, ui, value, defaultValue, disabled, "aria-describedby": ariaDescribedBy, "aria-errormessage": ariaErrorMessage, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, getAriaLabel, getAriaValueText, ...props }: SliderProps<Value>): React.JSX.Element;
18
18
  export { Slider };
19
19
  export type { SliderProps };
@@ -5,48 +5,6 @@ import * as React from "react";
5
5
  import { mergeIds } from "../internal/aria.mjs";
6
6
  import { useFieldsetDisabled } from "../internal/fieldset.mjs";
7
7
  import theme from "../theme/switch.mjs";
8
- function LiquidGlassFilter({ id }) {
9
- return /* @__PURE__ */ React.createElement("svg", { "aria-hidden": "true", focusable: "false", "data-layer": "filter" }, /* @__PURE__ */ React.createElement("defs", null, /* @__PURE__ */ React.createElement("filter", { id: `${id}-goo` }, /* @__PURE__ */ React.createElement(
10
- "feGaussianBlur",
11
- {
12
- in: "SourceGraphic",
13
- stdDeviation: "2",
14
- result: "blur"
15
- }
16
- ), /* @__PURE__ */ React.createElement(
17
- "feColorMatrix",
18
- {
19
- in: "blur",
20
- values: "1 0 0 0 0\n 0 1 0 0 0\n 0 0 1 0 0\n 0 0 0 16 -10",
21
- type: "matrix",
22
- result: "matrix"
23
- }
24
- ), /* @__PURE__ */ React.createElement("feComposite", { in: "matrix", operator: "atop", result: "goo" })), /* @__PURE__ */ React.createElement(
25
- "filter",
26
- {
27
- id: `${id}-remove-black`,
28
- colorInterpolationFilters: "sRGB"
29
- },
30
- /* @__PURE__ */ React.createElement(
31
- "feColorMatrix",
32
- {
33
- type: "matrix",
34
- values: "1 0 0 0 0\n 0 1 0 0 0\n 0 0 1 0 0\n -255 -255 -255 0 1",
35
- result: "black-pixels"
36
- }
37
- ),
38
- /* @__PURE__ */ React.createElement(
39
- "feMorphology",
40
- {
41
- in: "black-pixels",
42
- operator: "dilate",
43
- radius: "0.5",
44
- result: "smoothed"
45
- }
46
- ),
47
- /* @__PURE__ */ React.createElement("feComposite", { in: "SourceGraphic", in2: "smoothed", operator: "out" })
48
- )));
49
- }
50
8
  function Switch({
51
9
  className,
52
10
  ui,
@@ -63,24 +21,32 @@ function Switch({
63
21
  }) {
64
22
  const styles = theme({ size });
65
23
  const fieldsetDisabled = useFieldsetDisabled(disabled);
66
- const filterId = React.useId();
67
24
  const labelId = React.useId();
68
25
  const descriptionId = React.useId();
26
+ const [animating, setAnimating] = React.useState(false);
27
+ const animationTimer = React.useRef(void 0);
69
28
  const hasName = Boolean(label || ariaLabel || ariaLabelledBy);
70
29
  const labelledBy = ariaLabelledBy || (label && !ariaLabel ? labelId : void 0);
71
30
  const describedBy = mergeIds(
72
31
  ariaDescribedBy,
73
32
  description && hasName ? descriptionId : void 0
74
33
  );
75
- const filterStyles = {
76
- "--ui-switch-goo-filter": `url("#${filterId}-goo")`,
77
- "--ui-switch-remove-black-filter": `url("#${filterId}-remove-black")`
34
+ const stopAnimating = () => {
35
+ clearTimeout(animationTimer.current);
36
+ setAnimating(false);
78
37
  };
79
- const [animating, setAnimating] = React.useState(false);
38
+ React.useEffect(
39
+ () => () => {
40
+ clearTimeout(animationTimer.current);
41
+ },
42
+ []
43
+ );
80
44
  const handleCheckedChange = (checked, details) => {
81
45
  onCheckedChange?.(checked, details);
82
46
  if (!details.isCanceled) {
83
47
  setAnimating(true);
48
+ clearTimeout(animationTimer.current);
49
+ animationTimer.current = setTimeout(stopAnimating, 300);
84
50
  }
85
51
  };
86
52
  const handleThumbTransitionEnd = (event) => {
@@ -88,7 +54,7 @@ function Switch({
88
54
  return;
89
55
  }
90
56
  if (event.propertyName === "translate" || event.propertyName === "scale") {
91
- setAnimating(false);
57
+ stopAnimating();
92
58
  }
93
59
  };
94
60
  return /* @__PURE__ */ React.createElement(
@@ -111,28 +77,15 @@ function Switch({
111
77
  "data-slot": "container",
112
78
  className: cn(styles.container(), ui?.container)
113
79
  },
114
- /* @__PURE__ */ React.createElement(
115
- "span",
80
+ /* @__PURE__ */ React.createElement("span", { "data-slot": "base", className: cn(styles.base(), ui?.base) }, /* @__PURE__ */ React.createElement(
81
+ UISwitch.Thumb,
116
82
  {
117
- "data-slot": "base",
118
- className: cn(styles.base(), ui?.base),
119
- style: filterStyles
120
- },
121
- /* @__PURE__ */ React.createElement(LiquidGlassFilter, { id: filterId }),
122
- /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", "data-layer": "mask" }, /* @__PURE__ */ React.createElement("span", { "data-layer": "track" }, /* @__PURE__ */ React.createElement("span", { "data-layer": "track-mask" }))),
123
- /* @__PURE__ */ React.createElement(
124
- UISwitch.Thumb,
125
- {
126
- "data-slot": "thumb",
127
- "aria-hidden": "true",
128
- onTransitionEnd: handleThumbTransitionEnd,
129
- className: cn(styles.thumb(), ui?.thumb)
130
- },
131
- /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", "data-layer": "glass-rim" }),
132
- /* @__PURE__ */ React.createElement("span", { "data-layer": "glass-lens" }, /* @__PURE__ */ React.createElement("span", { "data-layer": "goo" }, /* @__PURE__ */ React.createElement("span", { "data-layer": "inner-glow" }), /* @__PURE__ */ React.createElement("span", { "data-layer": "fill" }))),
133
- /* @__PURE__ */ React.createElement("span", { "data-layer": "cover" })
134
- )
135
- )
83
+ "data-slot": "thumb",
84
+ "aria-hidden": "true",
85
+ onTransitionEnd: handleThumbTransitionEnd,
86
+ className: cn(styles.thumb(), ui?.thumb)
87
+ }
88
+ ))
136
89
  ),
137
90
  label || description ? /* @__PURE__ */ React.createElement(
138
91
  "span",
@@ -1,13 +1,13 @@
1
- import { Tabs as UITabs } from "@base-ui/react/tabs";
1
+ import { Tabs as UITabs, type TabsTabValue } from "@base-ui/react/tabs";
2
2
  import * as React from "react";
3
3
  import { type VariantProps } from "tailwind-variants";
4
4
  import { type ComponentConfig } from "../internal/tv";
5
5
  import theme from "../theme/tabs";
6
6
  type Tabs = ComponentConfig<typeof theme>;
7
7
  /** One tab and its related panel. */
8
- type TabsItemProps<Value extends string = string> = Omit<UITabs.Tab.Props, "children" | "className" | "content" | "value"> & {
8
+ type TabsItemProps = Omit<UITabs.Tab.Props, "children" | "className" | "content" | "value"> & {
9
9
  /** Value that identifies the tab and its panel. */
10
- value: Value;
10
+ value: TabsTabValue;
11
11
  /** Visible label inside the tab. */
12
12
  label?: React.ReactNode;
13
13
  /** Decorative icon rendered before the label. */
@@ -21,15 +21,15 @@ type TabsItemProps<Value extends string = string> = Omit<UITabs.Tab.Props, "chil
21
21
  /** Tailwind classes for this item's internal slots, keyed by `data-slot`. */
22
22
  ui?: Partial<Pick<Tabs["slots"], "tab" | "icon" | "label" | "panel" | "panelContent">>;
23
23
  };
24
- type TabsProps<Value extends string = string> = Omit<UITabs.Root.Props, "children" | "className" | "value" | "defaultValue" | "onValueChange"> & Pick<UITabs.List.Props, "activateOnFocus" | "loopFocus"> & VariantProps<typeof theme> & {
24
+ type TabsProps = Omit<UITabs.Root.Props, "children" | "className" | "value" | "defaultValue" | "onValueChange"> & Pick<UITabs.List.Props, "activateOnFocus" | "loopFocus"> & VariantProps<typeof theme> & {
25
25
  /** Tabs and panels rendered by the component. */
26
- items: readonly TabsItemProps<Value>[];
26
+ items: readonly TabsItemProps[];
27
27
  /** Controlled active value. Use `null` to render no active tab. */
28
- value?: Value | null;
28
+ value?: TabsTabValue;
29
29
  /** Initial active value when uncontrolled. */
30
- defaultValue?: Value | null;
30
+ defaultValue?: TabsTabValue;
31
31
  /** Called when the active value changes. */
32
- onValueChange?: (value: Value, details: UITabs.Root.ChangeEventDetails) => void;
32
+ onValueChange?: (value: TabsTabValue, details: UITabs.Root.ChangeEventDetails) => void;
33
33
  /** Disables every tab. */
34
34
  disabled?: boolean;
35
35
  /** Tailwind classes merged into the root. */
@@ -37,6 +37,6 @@ type TabsProps<Value extends string = string> = Omit<UITabs.Root.Props, "childre
37
37
  /** Tailwind classes for internal slots, keyed by `data-slot`. */
38
38
  ui?: Tabs["slots"];
39
39
  };
40
- declare function Tabs<Value extends string = string>({ className, ui, size, items, value, defaultValue, onValueChange, activateOnFocus, loopFocus, disabled, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...props }: TabsProps<Value>): React.JSX.Element;
40
+ declare function Tabs({ className, ui, size, items, value, defaultValue, onValueChange, activateOnFocus, loopFocus, disabled, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...props }: TabsProps): React.JSX.Element;
41
41
  export { Tabs };
42
42
  export type { TabsItemProps, TabsProps };
@@ -67,10 +67,10 @@ function Tabs({
67
67
  }
68
68
  ),
69
69
  items.map(
70
- ({ content: _content, keepMounted: _keepMounted, ui: itemUi, ...item }) => /* @__PURE__ */ React.createElement(
70
+ ({ content: _content, keepMounted: _keepMounted, ui: itemUi, ...item }, index) => /* @__PURE__ */ React.createElement(
71
71
  TabsItem,
72
72
  {
73
- key: item.value,
73
+ key: index,
74
74
  ...item,
75
75
  disabled: disabled || item.disabled,
76
76
  ui: {
@@ -82,10 +82,10 @@ function Tabs({
82
82
  )
83
83
  )
84
84
  ),
85
- /* @__PURE__ */ React.createElement("div", { "data-slot": "panels", className: cn(styles.panels(), ui?.panels) }, items.map((item) => /* @__PURE__ */ React.createElement(
85
+ /* @__PURE__ */ React.createElement("div", { "data-slot": "panels", className: cn(styles.panels(), ui?.panels) }, items.map((item, index) => /* @__PURE__ */ React.createElement(
86
86
  UITabs.Panel,
87
87
  {
88
- key: item.value,
88
+ key: index,
89
89
  value: item.value,
90
90
  keepMounted: item.keepMounted,
91
91
  "data-slot": "panel",
@@ -6,7 +6,7 @@ import { type ComponentConfig } from "../internal/tv";
6
6
  import theme from "../theme/toggle-group";
7
7
  type ToggleGroup = ComponentConfig<typeof theme>;
8
8
  /** One option managed by the toggle group. */
9
- type ToggleGroupItemProps<Value extends string = string> = Omit<UIToggle.Props<Value>, "className" | "children" | "value" | "pressed" | "defaultPressed"> & {
9
+ type ToggleGroupItemProps<Value extends string> = Omit<UIToggle.Props<Value>, "className" | "children" | "value" | "pressed" | "defaultPressed"> & {
10
10
  /** Value that identifies the option in the group value. */
11
11
  value: Value;
12
12
  /** Visible content inside the item. */
@@ -18,7 +18,7 @@ type ToggleGroupItemProps<Value extends string = string> = Omit<UIToggle.Props<V
18
18
  /** Tailwind classes for this item's internal slots, keyed by `data-slot`. */
19
19
  ui?: Partial<Pick<ToggleGroup["slots"], "item" | "content" | "icon" | "label">>;
20
20
  };
21
- type ToggleGroupProps<Value extends string = string> = Omit<UIToggleGroup.Props<Value>, "className" | "children"> & VariantProps<typeof theme> & {
21
+ type ToggleGroupProps<Value extends string> = Omit<UIToggleGroup.Props<Value>, "className" | "children"> & VariantProps<typeof theme> & {
22
22
  /** Options rendered by the toggle group. */
23
23
  items: readonly ToggleGroupItemProps<Value>[];
24
24
  /** Tailwind classes merged into the group root. */
@@ -26,6 +26,6 @@ type ToggleGroupProps<Value extends string = string> = Omit<UIToggleGroup.Props<
26
26
  /** Tailwind classes for internal slots, keyed by `data-slot`. */
27
27
  ui?: ToggleGroup["slots"];
28
28
  };
29
- declare function ToggleGroup<Value extends string = string>({ className, ui, size, items, disabled, ...props }: ToggleGroupProps<Value>): React.JSX.Element;
29
+ declare function ToggleGroup<Value extends string>({ className, ui, size, items, disabled, ...props }: ToggleGroupProps<Value>): React.JSX.Element;
30
30
  export { ToggleGroup };
31
31
  export type { ToggleGroupItemProps, ToggleGroupProps };
@@ -4,7 +4,7 @@ import { type VariantProps } from "tailwind-variants";
4
4
  import { type ComponentConfig } from "../internal/tv";
5
5
  import theme from "../theme/toggle";
6
6
  type Toggle = ComponentConfig<typeof theme>;
7
- type ToggleProps<Value extends string = string> = Omit<UIToggle.Props<Value>, "className" | "children" | "ref"> & Omit<VariantProps<typeof theme>, "iconOnly"> & {
7
+ type ToggleProps<Value extends string> = Omit<UIToggle.Props<Value>, "className" | "children" | "ref"> & Omit<VariantProps<typeof theme>, "iconOnly"> & {
8
8
  /** Ref to the rendered root element. */
9
9
  ref?: React.Ref<HTMLButtonElement>;
10
10
  /** Tailwind classes merged into the toggle root. */
@@ -16,6 +16,6 @@ type ToggleProps<Value extends string = string> = Omit<UIToggle.Props<Value>, "c
16
16
  /** Icon rendered before the label. */
17
17
  icon?: React.ReactNode;
18
18
  };
19
- declare function Toggle<Value extends string = string>({ className, ui, icon, label, size, disabled, ...props }: ToggleProps<Value>): React.JSX.Element;
19
+ declare function Toggle<Value extends string>({ className, ui, icon, label, size, disabled, ...props }: ToggleProps<Value>): React.JSX.Element;
20
20
  export { Toggle };
21
21
  export type { ToggleProps };
package/dist/index.css CHANGED
@@ -1 +1 @@
1
- @import "./keyframes.css";@source "./**/*.mjs";@source "./**/*.{ts,tsx}";@custom-variant ui-light (&:where(.light, .light *, [data-theme="light"], [data-theme="light"] *));@custom-variant ui-dark (&:where(.dark, .dark *, [data-theme="dark"], [data-theme="dark"] *));@layer theme{:host,:root{color-scheme:light;--ui-ink:#1f1f1f;--ui-ink-active:#000;--ui-surface:#fff;--ui-glass-surface:hsla(0,0%,100%,.2);--ui-glass-overlay-surface:hsla(0,0%,100%,.64);--ui-glass-border:rgba(31,31,31,.16);--ui-glass-focus:rgba(0,0,0,.6);--ui-glass-inner-border:rgba(0,0,0,.9);--ui-glass-rim:hsla(0,0%,100%,.2);--ui-glass-highlight:hsla(0,0%,100%,.8);--ui-glass-glare-stop:15%;--ui-glass-tone:brightness(0.98);--ui-glass-contrast:1.05;--ui-drop-shadow-label:drop-shadow(0 25px 3px hsla(0,0%,40%,.15));--ui-shadow-glass:inset 0 2px 1px -1px hsla(0,0%,100%,.9),inset 0 -2px 1px -1px hsla(0,0%,100%,.9),inset 0 6px 1px -6px hsla(0,0%,100%,.55),inset 0 -6px 1px -6px hsla(0,0%,100%,.55),inset 0 0 0 1px hsla(0,0%,100%,.45),inset 0 0 2px rgba(0,0,0,.8),0 4px 8px rgba(0,0,0,.2)}.dark,[data-theme=dark]{color-scheme:dark;--ui-ink:#f5f5f5;--ui-ink-active:#fff;--ui-surface:#141414;--ui-glass-surface:hsla(0,0%,8%,.62);--ui-glass-overlay-surface:hsla(0,0%,8%,.76);--ui-glass-border:hsla(0,0%,100%,.2);--ui-glass-focus:hsla(0,0%,100%,.7);--ui-glass-inner-border:hsla(0,0%,100%,.4);--ui-glass-rim:hsla(0,0%,100%,.25);--ui-glass-highlight:hsla(0,0%,100%,.45);--ui-glass-tone:brightness(0.95);--ui-glass-contrast:1;--ui-drop-shadow-label:drop-shadow(0 25px 3px rgba(0,0,0,.45));--ui-shadow-glass:inset 0 2px 1px -1px hsla(0,0%,100%,.35),inset 0 -2px 1px -1px hsla(0,0%,100%,.35),inset 0 6px 1px -6px hsla(0,0%,100%,.2),inset 0 -6px 1px -6px hsla(0,0%,100%,.2),inset 0 0 0 1px hsla(0,0%,100%,.14),inset 0 0 2px rgba(0,0,0,.9),0 4px 8px rgba(0,0,0,.35)}.light,[data-theme=light]{color-scheme:light;--ui-ink:#1f1f1f;--ui-ink-active:#000;--ui-surface:#fff;--ui-glass-surface:hsla(0,0%,100%,.2);--ui-glass-overlay-surface:hsla(0,0%,100%,.64);--ui-glass-border:rgba(31,31,31,.16);--ui-glass-focus:rgba(0,0,0,.6);--ui-glass-inner-border:rgba(0,0,0,.9);--ui-glass-rim:hsla(0,0%,100%,.2);--ui-glass-highlight:hsla(0,0%,100%,.8);--ui-glass-glare-stop:15%;--ui-glass-tone:brightness(0.98);--ui-glass-contrast:1.05;--ui-drop-shadow-label:drop-shadow(0 25px 3px hsla(0,0%,40%,.15));--ui-shadow-glass:inset 0 2px 1px -1px hsla(0,0%,100%,.9),inset 0 -2px 1px -1px hsla(0,0%,100%,.9),inset 0 6px 1px -6px hsla(0,0%,100%,.55),inset 0 -6px 1px -6px hsla(0,0%,100%,.55),inset 0 0 0 1px hsla(0,0%,100%,.45),inset 0 0 2px rgba(0,0,0,.8),0 4px 8px rgba(0,0,0,.2)}:host,:root{--ui-glass-blur:4px;--ui-glass-saturate:150%;--ui-glass-chip-blur:8px;--ui-glass-inner-inset:8px;--ui-glass-inner-offset:1px;--ui-glass-inner-blur:8px;--ui-glass-highlight-blur:7px;--ui-glass-rim-inset:9px;--ui-glass-rim-blur:1px}}@theme inline{--color-ui-ink:var(--ui-ink);--color-ui-ink-active:var(--ui-ink-active);--color-ui-surface:var(--ui-surface);--color-ui-glass-surface:var(--ui-glass-surface);--color-ui-glass-overlay-surface:var(--ui-glass-overlay-surface);--color-ui-glass-border:var(--ui-glass-border);--color-ui-glass-focus:var(--ui-glass-focus);--color-ui-glass-inner-border:var(--ui-glass-inner-border);--color-ui-glass-rim:var(--ui-glass-rim);--color-ui-glass-highlight:var(--ui-glass-highlight)}@layer components{.ui-glass{backdrop-filter:blur(var(--ui-glass-blur,4px)) saturate(var(--ui-glass-saturate,150%));background-color:var(--ui-glass-surface);border:1px double var(--ui-glass-border);box-shadow:var(--ui-shadow-glass);filter:var(--ui-glass-tone,brightness(.98)) contrast(var(--ui-glass-contrast,1.05));position:relative}.ui-glass-overlay{background-color:var(--ui-glass-overlay-surface)}.ui-glass-inner:before{border:1px solid var(--ui-glass-inner-border);bottom:calc(var(--ui-glass-inner-inset, 8px) - var(--ui-glass-inner-offset, 1px));filter:blur(var(--ui-glass-inner-blur,8px));left:var(--ui-glass-inner-inset,8px);right:var(--ui-glass-inner-inset,8px);top:calc(var(--ui-glass-inner-inset, 8px) + var(--ui-glass-inner-offset, 1px));z-index:1}.ui-glass-highlight:after,.ui-glass-inner:before{border-radius:inherit;content:"";pointer-events:none;position:absolute}.ui-glass-highlight:after{background:linear-gradient(45deg,var(--ui-glass-highlight) 0,transparent var(--ui-glass-glare-stop),transparent calc(100% - var(--ui-glass-glare-stop)),var(--ui-glass-highlight) 100%);filter:blur(var(--ui-glass-highlight-blur,7px));inset:0;z-index:3}.ui-glass-rim{border:1px solid var(--ui-glass-rim);border-radius:inherit;filter:blur(var(--ui-glass-rim-blur,1px));height:calc(100% - var(--ui-glass-rim-inset, 9px));left:50%;pointer-events:none;position:absolute;top:50%;transform:translate(-50%,-50%);width:calc(100% - var(--ui-glass-rim-inset, 9px));z-index:2}.ui-glass-chip{backdrop-filter:blur(var(--ui-glass-chip-blur,8px)) saturate(var(--ui-glass-saturate,150%));background-color:var(--ui-glass-surface);border:1px solid var(--ui-glass-border);box-shadow:inset 0 1px 0 var(--ui-glass-highlight)}}
1
+ @import "./keyframes.css";@source "./**/*.mjs";@source "./**/*.{ts,tsx}";@custom-variant ui-light (&:where(.light, .light *, [data-theme="light"], [data-theme="light"] *));@custom-variant ui-dark (&:where(.dark, .dark *, [data-theme="dark"], [data-theme="dark"] *));@layer theme{:host,:root{color-scheme:light;--ui-ink:#1f1f1f;--ui-ink-active:#000;--ui-surface:#fff;--ui-glass-surface:hsla(0,0%,100%,.2);--ui-glass-overlay-surface:hsla(0,0%,100%,.64);--ui-glass-border:rgba(31,31,31,.16);--ui-glass-focus:rgba(0,0,0,.6);--ui-glass-inner-border:rgba(0,0,0,.9);--ui-glass-rim:hsla(0,0%,100%,.2);--ui-glass-highlight:hsla(0,0%,100%,.8);--ui-glass-glare-stop:15%;--ui-glass-tone:brightness(0.98);--ui-glass-contrast:1.05;--ui-drop-shadow-label:drop-shadow(0 25px 3px hsla(0,0%,40%,.15));--ui-shadow-glass:inset 0 2px 1px -1px hsla(0,0%,100%,.9),inset 0 -2px 1px -1px hsla(0,0%,100%,.9),inset 0 6px 1px -6px hsla(0,0%,100%,.55),inset 0 -6px 1px -6px hsla(0,0%,100%,.55),inset 0 0 0 1px hsla(0,0%,100%,.45),inset 0 0 2px rgba(0,0,0,.8),0 4px 8px rgba(0,0,0,.2)}.dark,[data-theme=dark]{color-scheme:dark;--ui-ink:#f5f5f5;--ui-ink-active:#fff;--ui-surface:#141414;--ui-glass-surface:hsla(0,0%,8%,.62);--ui-glass-overlay-surface:hsla(0,0%,8%,.76);--ui-glass-border:hsla(0,0%,100%,.2);--ui-glass-focus:hsla(0,0%,100%,.7);--ui-glass-inner-border:hsla(0,0%,100%,.4);--ui-glass-rim:hsla(0,0%,100%,.25);--ui-glass-highlight:hsla(0,0%,100%,.45);--ui-glass-tone:brightness(0.95);--ui-glass-contrast:1;--ui-drop-shadow-label:drop-shadow(0 25px 3px rgba(0,0,0,.45));--ui-shadow-glass:inset 0 2px 1px -1px hsla(0,0%,100%,.35),inset 0 -2px 1px -1px hsla(0,0%,100%,.35),inset 0 6px 1px -6px hsla(0,0%,100%,.2),inset 0 -6px 1px -6px hsla(0,0%,100%,.2),inset 0 0 0 1px hsla(0,0%,100%,.14),inset 0 0 2px rgba(0,0,0,.9),0 4px 8px rgba(0,0,0,.35)}.light,[data-theme=light]{color-scheme:light;--ui-ink:#1f1f1f;--ui-ink-active:#000;--ui-surface:#fff;--ui-glass-surface:hsla(0,0%,100%,.2);--ui-glass-overlay-surface:hsla(0,0%,100%,.64);--ui-glass-border:rgba(31,31,31,.16);--ui-glass-focus:rgba(0,0,0,.6);--ui-glass-inner-border:rgba(0,0,0,.9);--ui-glass-rim:hsla(0,0%,100%,.2);--ui-glass-highlight:hsla(0,0%,100%,.8);--ui-glass-glare-stop:15%;--ui-glass-tone:brightness(0.98);--ui-glass-contrast:1.05;--ui-drop-shadow-label:drop-shadow(0 25px 3px hsla(0,0%,40%,.15));--ui-shadow-glass:inset 0 2px 1px -1px hsla(0,0%,100%,.9),inset 0 -2px 1px -1px hsla(0,0%,100%,.9),inset 0 6px 1px -6px hsla(0,0%,100%,.55),inset 0 -6px 1px -6px hsla(0,0%,100%,.55),inset 0 0 0 1px hsla(0,0%,100%,.45),inset 0 0 2px rgba(0,0,0,.8),0 4px 8px rgba(0,0,0,.2)}:host,:root{--ui-glass-blur:4px;--ui-glass-saturate:150%;--ui-glass-chip-blur:8px;--ui-glass-inner-inset:8px;--ui-glass-inner-offset:1px;--ui-glass-inner-blur:8px;--ui-glass-highlight-blur:7px;--ui-glass-rim-inset:9px;--ui-glass-rim-blur:1px}}@theme inline{--color-ui-ink:var(--ui-ink);--color-ui-ink-active:var(--ui-ink-active);--color-ui-surface:var(--ui-surface);--color-ui-glass-surface:var(--ui-glass-surface);--color-ui-glass-overlay-surface:var(--ui-glass-overlay-surface);--color-ui-glass-border:var(--ui-glass-border);--color-ui-glass-focus:var(--ui-glass-focus);--color-ui-glass-inner-border:var(--ui-glass-inner-border);--color-ui-glass-rim:var(--ui-glass-rim);--color-ui-glass-highlight:var(--ui-glass-highlight)}@layer components{.ui-glass{backdrop-filter:blur(var(--ui-glass-blur,4px)) saturate(var(--ui-glass-saturate,150%));background-color:var(--ui-glass-surface);border:1px double var(--ui-glass-border);box-shadow:var(--ui-shadow-glass);filter:var(--ui-glass-tone,brightness(.98)) contrast(var(--ui-glass-contrast,1.05));position:relative}.ui-glass-overlay{background-color:var(--ui-glass-overlay-surface)}.ui-glass-inner:before{border:1px solid var(--ui-glass-inner-border);bottom:calc(var(--ui-glass-inner-inset, 8px) - var(--ui-glass-inner-offset, 1px));filter:blur(var(--ui-glass-inner-blur,8px));left:var(--ui-glass-inner-inset,8px);right:var(--ui-glass-inner-inset,8px);top:calc(var(--ui-glass-inner-inset, 8px) + var(--ui-glass-inner-offset, 1px));z-index:1}.ui-glass-highlight:after,.ui-glass-inner:before{border-radius:inherit;content:"";pointer-events:none;position:absolute}.ui-glass-highlight:after{background:linear-gradient(45deg,var(--ui-glass-highlight) 0,transparent var(--ui-glass-glare-stop),transparent calc(100% - var(--ui-glass-glare-stop)),var(--ui-glass-highlight) 100%);filter:blur(var(--ui-glass-highlight-blur,7px));inset:0;z-index:3}.ui-glass-rim{border:1px solid var(--ui-glass-rim);border-radius:inherit;filter:blur(var(--ui-glass-rim-blur,1px));inset:calc(var(--ui-glass-rim-inset, 9px)/2);pointer-events:none;position:absolute;z-index:2}.ui-glass-chip{backdrop-filter:blur(var(--ui-glass-chip-blur,8px)) saturate(var(--ui-glass-saturate,150%));background-color:var(--ui-glass-surface);border:1px solid var(--ui-glass-border);box-shadow:inset 0 1px 0 var(--ui-glass-highlight)}}
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ export { Popover, type PopoverProps } from "./components/popover";
15
15
  export { Progress, type ProgressProps } from "./components/progress";
16
16
  export { RadioGroup, type RadioGroupItemProps, type RadioGroupProps, } from "./components/radio-group";
17
17
  export { ScrollArea, type ScrollAreaOrientation, type ScrollAreaProps, } from "./components/scroll-area";
18
- export { Select, type SelectItemProps, type SelectProps, } from "./components/select";
18
+ export { Select, type SelectEntry, type SelectItemGroupProps, type SelectItemProps, type SelectProps, } from "./components/select";
19
19
  export { Slider, type SliderProps } from "./components/slider";
20
20
  export { Switch, type SwitchProps } from "./components/switch";
21
21
  export { Tabs, type TabsItemProps, type TabsProps } from "./components/tabs";
@@ -11,7 +11,10 @@ export default tv({
11
11
  ],
12
12
  glass: [
13
13
  "ui-glass ui-glass-inner ui-glass-highlight",
14
- "pointer-events-none absolute inset-0 z-0 overflow-hidden rounded-[inherit]"
14
+ "pointer-events-none absolute inset-0 z-0 overflow-hidden rounded-[inherit]",
15
+ // Keep the filtered glass on a stable compositor layer so the panel
16
+ // height transition does not re-rasterize the whole surface each frame.
17
+ "will-change-transform"
15
18
  ],
16
19
  overlay: "ui-glass-rim",
17
20
  content: "relative z-10 overflow-hidden rounded-[inherit]",
@@ -35,6 +35,8 @@ declare const _default: import("tailwind-variants").TVReturnType<{
35
35
  popupGlass: string[];
36
36
  popupOverlay: string;
37
37
  list: string[];
38
+ group: string;
39
+ groupLabel: string;
38
40
  item: string[];
39
41
  itemIndicator: string[];
40
42
  itemText: string;
@@ -58,6 +58,8 @@ export default tv({
58
58
  "relative z-10 flex min-w-0 flex-col gap-0.5",
59
59
  "max-h-[min(24rem,var(--available-height))] overflow-y-auto scroll-py-2"
60
60
  ],
61
+ group: "flex min-w-0 flex-col gap-0.5",
62
+ groupLabel: "px-2.5 pb-0.5 pt-1 text-xs font-medium text-ui-ink/55",
61
63
  item: [
62
64
  "relative flex min-h-8 cursor-default select-none items-center gap-2 rounded-xl px-2.5",
63
65
  "text-sm text-ui-ink outline-none",
@@ -1,23 +1,23 @@
1
1
  declare const theme: import("tailwind-variants").TVReturnType<{
2
2
  size: {
3
3
  xs: {
4
- base: string;
4
+ root: string;
5
5
  wrapper: string;
6
6
  };
7
7
  sm: {
8
- base: string;
8
+ root: string;
9
9
  wrapper: string;
10
10
  };
11
11
  md: {
12
- base: string;
12
+ root: string;
13
13
  wrapper: string;
14
14
  };
15
15
  lg: {
16
- base: string;
16
+ root: string;
17
17
  wrapper: string;
18
18
  };
19
19
  xl: {
20
- base: string;
20
+ root: string;
21
21
  wrapper: string;
22
22
  };
23
23
  };
@@ -6,80 +6,54 @@ const theme = tv({
6
6
  "touch-none [-webkit-tap-highlight-color:transparent]",
7
7
  "focus:outline-none",
8
8
  "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-75",
9
- "data-[readonly]:cursor-default"
9
+ "data-[readonly]:cursor-default",
10
+ "[--ui-switch-thumb-size:calc(var(--ui-switch-height)_-_2*var(--ui-switch-inset))]",
11
+ "[--ui-switch-travel:calc(var(--ui-switch-width)_-_var(--ui-switch-thumb-size)_-_2*var(--ui-switch-inset))]"
10
12
  ],
11
13
  container: "flex items-center",
12
14
  base: [
13
- "relative inline-flex h-[var(--ui-switch-height)] w-[var(--ui-switch-width)] shrink-0 select-none overflow-visible rounded-[100px] border-0 bg-transparent p-0 text-inherit",
14
- "[container-type:inline-size]",
15
- "[--ui-glass-edge-shadow:inset_1px_-1px_2px_rgb(255_255_255/0.55),inset_-1px_1px_2px_rgb(0_0_0/0.18),0_1px_3px_rgb(0_0_0/0.2)]",
16
- "[--ui-switch-off:color-mix(in_srgb,var(--ui-ink)_20%,transparent)] [--ui-switch-on:var(--ui-ink)]",
15
+ "relative inline-flex h-[var(--ui-switch-height)] w-[var(--ui-switch-width)] shrink-0 select-none overflow-hidden rounded-full",
16
+ "border-0 bg-[var(--ui-switch-off)] p-0 text-inherit",
17
+ "[background-image:linear-gradient(180deg,rgb(255_255_255/0.14),transparent_55%)]",
18
+ "[box-shadow:inset_0_1px_1px_rgb(255_255_255/0.3),inset_0_-1px_2px_rgb(0_0_0/0.14)]",
19
+ "transition-[background-color,box-shadow] duration-200 ease-out",
20
+ "group-data-[checked]:bg-[var(--ui-switch-on)]",
17
21
  "group-focus-visible:outline-2 group-focus-visible:outline-offset-4 group-focus-visible:outline-ui-glass-focus",
18
- "[&_*]:pointer-events-none",
19
- "motion-reduce:[transition-duration:0.01ms!important] motion-reduce:[transition-delay:0ms!important] motion-reduce:[&_*]:[transition-duration:0.01ms!important] motion-reduce:[&_*]:[transition-delay:0ms!important]",
20
- "[&_[data-layer=filter]]:pointer-events-none [&_[data-layer=filter]]:absolute [&_[data-layer=filter]]:h-0 [&_[data-layer=filter]]:w-0 [&_[data-layer=filter]]:overflow-hidden",
21
- "[&_[data-layer=mask]]:absolute [&_[data-layer=mask]]:inset-0 [&_[data-layer=mask]]:rounded-[100px]",
22
- "[&_[data-layer=mask]]:[filter:var(--ui-switch-remove-black-filter)] [&_[data-layer=mask]]:[transform:translate3d(0,0,0)]",
23
- "[&_[data-layer=track]]:absolute [&_[data-layer=track]]:left-1/2 [&_[data-layer=track]]:top-1/2 [&_[data-layer=track]]:z-[12] [&_[data-layer=track]]:h-full [&_[data-layer=track]]:w-full [&_[data-layer=track]]:rounded-[100px] [&_[data-layer=track]]:bg-[var(--ui-switch-off)]",
24
- "[&_[data-layer=track]]:[container-type:inline-size] [&_[data-layer=track]]:[translate:-50%_-50%] [&_[data-layer=track]]:[box-shadow:inset_0_1px_1px_rgb(255_255_255/0.35),inset_0_-1px_2px_rgb(0_0_0/0.12)]",
25
- "[&_[data-layer=track]]:[transition:background-color_120ms_ease-out_180ms] group-data-[checked]:[&_[data-layer=track]]:bg-[var(--ui-switch-on)]",
26
- "[&_[data-layer=track-mask]]:absolute [&_[data-layer=track-mask]]:left-[var(--ui-switch-border)] [&_[data-layer=track-mask]]:top-1/2 [&_[data-layer=track-mask]]:h-[calc(100%_-_2*var(--ui-switch-border))] [&_[data-layer=track-mask]]:w-[calc(60%_-_2*var(--ui-switch-border))] [&_[data-layer=track-mask]]:rounded-[100px] [&_[data-layer=track-mask]]:bg-black",
27
- "[&_[data-layer=track-mask]]:[translate:0_-50%] group-data-[checked]:[&_[data-layer=track-mask]]:[translate:calc(100cqi_-_100%_-_2*var(--ui-switch-border))_-50%]",
28
- "[&_[data-layer=track-mask]]:[transition:translate_120ms_ease-out_180ms,scale_300ms_ease-out]",
29
- "group-active:[&_[data-layer=track-mask]]:[scale:1.65] group-data-[animating=true]:[&_[data-layer=track-mask]]:[scale:1.65]",
30
- "[&_[data-layer=glass-rim]]:absolute [&_[data-layer=glass-rim]]:inset-0 [&_[data-layer=glass-rim]]:z-[20] [&_[data-layer=glass-rim]]:rounded-[100px] [&_[data-layer=glass-rim]]:[box-shadow:var(--ui-glass-edge-shadow)] [&_[data-layer=glass-rim]]:opacity-0",
31
- "[&_[data-layer=glass-rim]]:transition-opacity [&_[data-layer=glass-rim]]:duration-200 [&_[data-layer=glass-rim]]:ease-out",
32
- "group-active:[&_[data-layer=glass-rim]]:opacity-100 group-data-[animating=true]:[&_[data-layer=glass-rim]]:opacity-100",
33
- "[&_[data-layer=glass-lens]]:absolute [&_[data-layer=glass-lens]]:inset-0 [&_[data-layer=glass-lens]]:rounded-[100px] [&_[data-layer=glass-lens]]:[clip-path:inset(0_0_0_0_round_100px)]",
34
- "[&_[data-layer=glass-lens]]:[filter:blur(calc(var(--ui-switch-height)*0.1))] [&_[data-layer=glass-lens]]:transition-[filter] [&_[data-layer=glass-lens]]:duration-200 [&_[data-layer=glass-lens]]:ease-out",
35
- "group-active:[&_[data-layer=glass-lens]]:[filter:blur(0)] group-data-[animating=true]:[&_[data-layer=glass-lens]]:[filter:blur(0)]",
36
- "[&_[data-layer=goo]]:absolute [&_[data-layer=goo]]:inset-0 [&_[data-layer=goo]]:overflow-hidden [&_[data-layer=goo]]:rounded-[100px]",
37
- "[&_[data-layer=goo]]:[filter:var(--ui-switch-goo-filter)] [&_[data-layer=goo]]:[transform:translate3d(0,0,0)]",
38
- "[&_[data-layer=inner-glow]]:absolute [&_[data-layer=inner-glow]]:inset-0 [&_[data-layer=inner-glow]]:rounded-[100px]",
39
- "[&_[data-layer=inner-glow]]:[--ui-switch-glow-color:var(--ui-switch-off)] group-data-[checked]:[&_[data-layer=inner-glow]]:[--ui-switch-glow-color:var(--ui-switch-on)]",
40
- "[&_[data-layer=inner-glow]]:[transition:box-shadow_120ms_ease-out_180ms]",
41
- "[&_[data-layer=inner-glow]]:[box-shadow:inset_0_0_calc(var(--ui-switch-height)*0.05)_calc(var(--ui-switch-height)*0.0667)_var(--ui-switch-glow-color),inset_calc(var(--ui-switch-height)*0.133)_0_calc(var(--ui-switch-height)*0.05)_calc(var(--ui-switch-height)*0.0667)_var(--ui-switch-glow-color)]",
42
- "[&_[data-layer=fill]]:absolute [&_[data-layer=fill]]:left-0 [&_[data-layer=fill]]:top-1/2 [&_[data-layer=fill]]:h-[var(--ui-switch-height)] [&_[data-layer=fill]]:w-[var(--ui-switch-width)] [&_[data-layer=fill]]:rounded-r-[100px] [&_[data-layer=fill]]:bg-[var(--ui-switch-off)]",
43
- "[&_[data-layer=fill]]:[translate:0_-50%] group-data-[checked]:[&_[data-layer=fill]]:[translate:calc(100cqi_-_100%)_-50%]",
44
- "[&_[data-layer=fill]]:[transition:height_200ms_ease-out,translate_120ms_ease-out_180ms,background-color_120ms_ease-out_180ms]",
45
- "group-active:[&_[data-layer=fill]]:h-[calc(var(--ui-switch-height)_-_6*var(--ui-switch-border))] group-data-[animating=true]:[&_[data-layer=fill]]:h-[calc(var(--ui-switch-height)_-_6*var(--ui-switch-border))]",
46
- "group-data-[checked]:[&_[data-layer=fill]]:bg-[var(--ui-switch-on)]",
47
- "[&_[data-layer=cover]]:absolute [&_[data-layer=cover]]:inset-0 [&_[data-layer=cover]]:rounded-[100px] [&_[data-layer=cover]]:bg-white",
48
- "[&_[data-layer=cover]]:transition-opacity [&_[data-layer=cover]]:duration-200 [&_[data-layer=cover]]:ease-out",
49
- "group-active:[&_[data-layer=cover]]:opacity-0 group-data-[animating=true]:[&_[data-layer=cover]]:opacity-0"
22
+ "motion-reduce:transition-none",
23
+ "[--ui-switch-off:color-mix(in_srgb,var(--ui-ink)_20%,transparent)] [--ui-switch-on:var(--ui-ink)]"
50
24
  ],
51
25
  thumb: [
52
- "absolute left-[var(--ui-switch-border)] top-1/2 h-[calc(100%_-_2*var(--ui-switch-border))] w-[calc(60%_-_2*var(--ui-switch-border))] rounded-[100px] bg-transparent [box-shadow:var(--ui-glass-thumb-shadow)]",
53
- "[--ui-glass-thumb-shadow:0_1px_3px_rgb(0_0_0/0.2),inset_0_0_0_1px_rgb(255_255_255/0.55)]",
54
- "[container-type:inline-size]",
55
- "[translate:0_-50%] group-data-[checked]:[translate:calc(100cqi_-_100%_-_2*var(--ui-switch-border))_-50%]",
56
- "[transition:translate_120ms_ease-out_180ms,scale_300ms_ease-out]",
57
- "group-active:[scale:1.65] group-data-[animating=true]:[scale:1.65]"
26
+ "absolute left-[var(--ui-switch-inset)] top-1/2 z-10 block h-[var(--ui-switch-thumb-size)] w-[var(--ui-switch-thumb-size)] rounded-full bg-ui-surface",
27
+ "[box-shadow:0_1px_3px_rgb(0_0_0/0.22),inset_0_0_0_1px_rgb(255_255_255/0.5),inset_0_1px_1px_rgb(255_255_255/0.65),inset_0_-1px_2px_rgb(0_0_0/0.08)]",
28
+ "[translate:0_-50%] group-data-[checked]:[translate:var(--ui-switch-travel)_-50%]",
29
+ "transition-[translate,scale] duration-200 ease-out",
30
+ "group-active:scale-95",
31
+ "motion-reduce:transition-none"
58
32
  ],
59
33
  wrapper: "ms-2 min-w-0",
60
- label: "block font-medium text-ui-ink",
34
+ label: "block font-medium text-ui-ink leading-[var(--ui-switch-height)]",
61
35
  description: "block text-ui-ink opacity-60"
62
36
  },
63
37
  variants: {
64
38
  size: {
65
39
  xs: {
66
- base: "[--ui-switch-width:48px] [--ui-switch-height:24px] [--ui-switch-border:2px]",
40
+ root: "[--ui-switch-width:48px] [--ui-switch-height:24px] [--ui-switch-inset:2px]",
67
41
  wrapper: "text-xs"
68
42
  },
69
43
  sm: {
70
- base: "[--ui-switch-width:56px] [--ui-switch-height:28px] [--ui-switch-border:2px]",
44
+ root: "[--ui-switch-width:56px] [--ui-switch-height:28px] [--ui-switch-inset:2px]",
71
45
  wrapper: "text-xs"
72
46
  },
73
47
  md: {
74
- base: "[--ui-switch-width:64px] [--ui-switch-height:32px] [--ui-switch-border:3px]",
48
+ root: "[--ui-switch-width:64px] [--ui-switch-height:32px] [--ui-switch-inset:3px]",
75
49
  wrapper: "text-sm"
76
50
  },
77
51
  lg: {
78
- base: "[--ui-switch-width:72px] [--ui-switch-height:36px] [--ui-switch-border:3px]",
52
+ root: "[--ui-switch-width:72px] [--ui-switch-height:36px] [--ui-switch-inset:3px]",
79
53
  wrapper: "text-sm"
80
54
  },
81
55
  xl: {
82
- base: "[--ui-switch-width:80px] [--ui-switch-height:40px] [--ui-switch-border:3px]",
56
+ root: "[--ui-switch-width:80px] [--ui-switch-height:40px] [--ui-switch-inset:3px]",
83
57
  wrapper: "text-base"
84
58
  }
85
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xingxingmofashu/ui",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "React components with a CSS-only Liquid Glass visual style for React 19 and Tailwind CSS 4.",
5
5
  "license": "MIT",
6
6
  "author": "xingxingmofashu",