@ctrl-ui/react 0.8.13 → 0.8.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/audio-device-select.d.ts +38 -0
  2. package/dist/audio-device-select.js +183 -0
  3. package/dist/audio-visualizer-bar.d.ts +10 -0
  4. package/dist/audio-visualizer-bar.js +49 -0
  5. package/dist/audio-visualizer-line.d.ts +4 -0
  6. package/dist/audio-visualizer-line.js +75 -0
  7. package/dist/audio-visualizer.d.ts +1 -1
  8. package/dist/audio-visualizer.js +43 -16
  9. package/dist/hooks/use-audio-recorder.d.ts +1 -0
  10. package/dist/hooks/use-audio-recorder.js +27 -4
  11. package/dist/hooks/use-color-area.js +0 -3
  12. package/dist/hooks/use-textarea-trigger-menu.js +5 -2
  13. package/dist/knob-contracts/audio-device-select-knobs.d.ts +4 -0
  14. package/dist/knob-contracts/audio-device-select-knobs.js +4 -0
  15. package/dist/knob-contracts/audio-visualizer-knobs.d.ts +1 -1
  16. package/dist/knob-contracts/audio-visualizer-knobs.js +2 -0
  17. package/dist/lib/audio-visualizer-levels.d.ts +12 -0
  18. package/dist/lib/audio-visualizer-levels.js +37 -0
  19. package/dist/lib/code-tokens.js +4 -1
  20. package/dist/lib/popup-item-focus.d.ts +7 -0
  21. package/dist/lib/popup-item-focus.js +9 -0
  22. package/dist/model-switcher.d.ts +23 -0
  23. package/dist/model-switcher.js +70 -0
  24. package/dist/styles/code.css +1 -1
  25. package/dist/styles/index.css +3 -2
  26. package/dist/styles/recipes/audio-device-select.css +15 -0
  27. package/dist/styles/recipes/audio-visualizer.css +97 -2
  28. package/dist/styles/recipes/popup-controls.css +11 -0
  29. package/dist/styles/recipes/popup-motion.css +6 -0
  30. package/dist/styles/recipes/tabs-motion.css +38 -28
  31. package/dist/styles/recipes/tabs.css +1 -0
  32. package/dist/styles/theme.css +43 -0
  33. package/dist/ui/alert-dialog.d.ts +3 -0
  34. package/dist/ui/alert-dialog.js +11 -1
  35. package/dist/ui/autocomplete.d.ts +1 -1
  36. package/dist/ui/color-picker.js +1 -1
  37. package/dist/ui/combobox.d.ts +1 -1
  38. package/dist/ui/context-menu.d.ts +1 -1
  39. package/dist/ui/context-menu.js +7 -4
  40. package/dist/ui/dialog.d.ts +3 -0
  41. package/dist/ui/dialog.js +11 -1
  42. package/dist/ui/dropdown-menu.d.ts +1 -1
  43. package/dist/ui/dropdown-menu.js +7 -4
  44. package/dist/ui/gradient-editor.js +1 -1
  45. package/dist/ui/item.js +1 -1
  46. package/dist/ui/responsive-dialog.d.ts +2 -1
  47. package/dist/ui/responsive-dialog.js +5 -2
  48. package/dist/ui/select.d.ts +1 -1
  49. package/dist/ui/select.js +3 -1
  50. package/dist/ui/sidebar-resize-rail.js +2 -2
  51. package/dist/ui/tabs.js +25 -14
  52. package/package.json +1 -1
@@ -0,0 +1,37 @@
1
+ //#region src/lib/audio-visualizer-levels.ts
2
+ function audioVisualizerPointCount(points = 28) {
3
+ return Math.min(128, Math.max(1, Number.isFinite(points) ? Math.floor(points) : 28));
4
+ }
5
+ function normalizeAudioLevel(level) {
6
+ return Number.isFinite(level) ? Math.min(1, Math.max(0, level)) : 0;
7
+ }
8
+ function audioVisualizerHistory(levels, points, active = true) {
9
+ const count = audioVisualizerPointCount(points);
10
+ const start = Math.max(0, levels.length - count);
11
+ const padding = Math.max(0, count - levels.length);
12
+ return Array.from({ length: count }, (_, index) => ({
13
+ key: `bar-${index}`,
14
+ level: active && index >= padding ? normalizeAudioLevel(levels[start + index - padding] ?? 0) : 0
15
+ }));
16
+ }
17
+ function audioVisualizerBands(levels, points, active = true, mirrored = false) {
18
+ const count = audioVisualizerPointCount(points);
19
+ const bandCount = mirrored ? Math.ceil(count / 2) : count;
20
+ const bands = Array.from({ length: bandCount }, (_, index) => {
21
+ if (!active || levels.length === 0) return 0;
22
+ const start = index * levels.length / bandCount;
23
+ const end = (index + 1) * levels.length / bandCount;
24
+ let total = 0;
25
+ for (let sample = Math.floor(start); sample < Math.ceil(end); sample += 1) {
26
+ const weight = Math.min(end, sample + 1) - Math.max(start, sample);
27
+ total += normalizeAudioLevel(levels[sample] ?? 0) * weight;
28
+ }
29
+ return total / (end - start);
30
+ });
31
+ return Array.from({ length: count }, (_, index) => ({
32
+ key: `band-${index}`,
33
+ level: bands[mirrored ? Math.floor(Math.abs(index - (count - 1) / 2)) : index] ?? 0
34
+ }));
35
+ }
36
+ //#endregion
37
+ export { audioVisualizerBands, audioVisualizerHistory, audioVisualizerPointCount, normalizeAudioLevel };
@@ -145,7 +145,10 @@ async function highlightToTokens(code, language) {
145
145
  const key = `${lang}\n${code}`;
146
146
  const cached = cacheGet(key);
147
147
  if (cached !== void 0) return cached;
148
- const { tokens } = (await getHighlighter()).codeToTokens(code, {
148
+ const highlighter = await getHighlighter();
149
+ const shared = cacheGet(key);
150
+ if (shared !== void 0) return shared;
151
+ const { tokens } = highlighter.codeToTokens(code, {
149
152
  lang,
150
153
  theme: CODE_THEME_NAME
151
154
  });
@@ -0,0 +1,7 @@
1
+ import { PointerEvent } from "react";
2
+ import { BaseUIEvent } from "@base-ui/react/types";
3
+ //#region src/lib/popup-item-focus.d.ts
4
+ export declare const popupItemFocusProps: {
5
+ onPointerLeave(event: BaseUIEvent<PointerEvent<HTMLElement>>): void;
6
+ };
7
+ //#endregion
@@ -0,0 +1,9 @@
1
+ //#region src/lib/popup-item-focus.ts
2
+ const popupItemFocusProps = { onPointerLeave(event) {
3
+ if (event.pointerType === "touch" || !(event.relatedTarget instanceof Element)) return;
4
+ const nextItem = event.relatedTarget.closest("[role=\"option\"], [role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]");
5
+ const list = event.currentTarget.closest("[role=\"listbox\"], [role=\"menu\"]");
6
+ if (nextItem && list && nextItem.closest("[role=\"listbox\"], [role=\"menu\"]") === list) event.preventBaseUIHandler();
7
+ } };
8
+ //#endregion
9
+ export { popupItemFocusProps };
@@ -0,0 +1,23 @@
1
+ import { SelectTriggerProps, SelectTriggerVariant } from "./ui/select.js";
2
+ import { ReactNode } from "react";
3
+ //#region src/model-switcher.d.ts
4
+ export type ModelOption = {
5
+ value: string;
6
+ label: string;
7
+ hint?: ReactNode;
8
+ icon?: ReactNode;
9
+ };
10
+ export type ModelSwitcherProps = {
11
+ models: ModelOption[];
12
+ value?: string;
13
+ defaultValue?: string;
14
+ onValueChange?: (value: string) => void;
15
+ disabled?: boolean;
16
+ size?: "xs" | "sm";
17
+ variant?: SelectTriggerVariant;
18
+ className?: string;
19
+ label?: string;
20
+ style?: SelectTriggerProps["style"];
21
+ };
22
+ export declare function ModelSwitcher({ models, value, defaultValue, onValueChange, disabled, size, variant, className, label, style }: ModelSwitcherProps): import("react").JSX.Element;
23
+ //#endregion
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import { cn } from "./lib/cn.js";
3
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/model-switcher.tsx
6
+ function ModelSwitcher({ models, value, defaultValue, onValueChange, disabled, size = "sm", variant = "surface", className, label = "Model", style }) {
7
+ const fallback = defaultValue ?? models[0]?.value;
8
+ return /* @__PURE__ */ jsxs(Select, {
9
+ value,
10
+ defaultValue: fallback,
11
+ onValueChange,
12
+ disabled: disabled || models.length === 0,
13
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
14
+ size,
15
+ variant,
16
+ className: cn("max-w-52", className),
17
+ style,
18
+ "aria-label": label,
19
+ children: /* @__PURE__ */ jsx("span", {
20
+ "data-control-ui": "model-switcher",
21
+ "data-control-family": "button",
22
+ "data-button-kind": "model-switcher",
23
+ "data-slot": "value",
24
+ className: "flex min-w-0 items-center",
25
+ children: /* @__PURE__ */ jsx(SelectValue, {
26
+ placeholder: label,
27
+ children: (current) => {
28
+ const model = models.find((option) => option.value === current);
29
+ return /* @__PURE__ */ jsxs("span", {
30
+ className: "flex min-w-0 items-center gap-1.5",
31
+ children: [model?.icon ? /* @__PURE__ */ jsx("span", {
32
+ "aria-hidden": "true",
33
+ className: "flex shrink-0 [&_svg]:size-4",
34
+ children: model.icon
35
+ }) : null, /* @__PURE__ */ jsx("span", {
36
+ className: "truncate",
37
+ title: model?.label ?? label,
38
+ children: model?.label ?? label
39
+ })]
40
+ });
41
+ }
42
+ })
43
+ })
44
+ }), /* @__PURE__ */ jsx(SelectContent, { children: models.map((model) => /* @__PURE__ */ jsxs(SelectItem, {
45
+ value: model.value,
46
+ label: model.label,
47
+ children: [
48
+ model.icon ? /* @__PURE__ */ jsx("span", {
49
+ "aria-hidden": "true",
50
+ className: "flex shrink-0 [&_svg]:size-4",
51
+ children: model.icon
52
+ }) : null,
53
+ /* @__PURE__ */ jsx("span", {
54
+ className: "truncate",
55
+ title: model.label,
56
+ children: model.label
57
+ }),
58
+ model.hint ? /* @__PURE__ */ jsx("span", {
59
+ "data-control-ui": "model-switcher",
60
+ "data-control-family": "popup",
61
+ "data-popup-kind": "model-switcher",
62
+ "data-slot": "hint",
63
+ children: model.hint
64
+ }) : null
65
+ ]
66
+ }, model.value)) })]
67
+ });
68
+ }
69
+ //#endregion
70
+ export { ModelSwitcher };
@@ -13,7 +13,7 @@
13
13
  --code-token-comment: oklch(0.48 0.015 285);
14
14
  --code-token-keyword: oklch(0.53 0.2 18);
15
15
  --code-token-parameter: oklch(0.52 0.09 55);
16
- --code-token-function: oklch(0.54 0.17 300);
16
+ --code-token-function: oklch(0.53 0.17 300);
17
17
  --code-token-punctuation: oklch(0.5 0.02 285);
18
18
  --code-token-link: oklch(0.5 0.15 250);
19
19
 
@@ -19,10 +19,11 @@
19
19
  @import "./recipes/sidebar-menu.css";
20
20
  @import "./recipes/sidebar-parts.css";
21
21
  @import "./recipes/app-shell-header.css";
22
- @import "./recipes/audio-visualizer.css";
23
- @import "./recipes/audio-recorder.css";
24
22
  @import "./recipes/field.css";
25
23
  @import "./recipes/field-parts.css";
24
+ @import "./recipes/audio-device-select.css";
25
+ @import "./recipes/audio-visualizer.css";
26
+ @import "./recipes/audio-recorder.css";
26
27
  @import "./recipes/avatar.css";
27
28
  @import "./recipes/badge.css";
28
29
  @import "./recipes/breadcrumb.css";
@@ -0,0 +1,15 @@
1
+ @layer components {
2
+ :where([data-control-family="audio-device-select"][data-slot="root"]) {
3
+ --cui-audio-device-select-status-foreground: var(--muted-foreground);
4
+ }
5
+
6
+ :where([data-control-family="audio-device-select"][data-slot="status"]) {
7
+ color: var(--cui-audio-device-select-status-foreground);
8
+ }
9
+ }
10
+
11
+ @property --cui-audio-device-select-status-foreground {
12
+ syntax: "<color>";
13
+ inherits: true;
14
+ initial-value: transparent;
15
+ }
@@ -3,6 +3,8 @@
3
3
  --cui-audio-visualizer-radius: var(--radius-control);
4
4
  --cui-audio-visualizer-bar-background: oklch(from var(--primary) l c h / 0.8);
5
5
  --cui-audio-visualizer-bar-radius: var(--radius-control);
6
+ --cui-audio-visualizer-bar-size: calc(var(--spacing) * 1);
7
+ --cui-audio-visualizer-bar-gap: 1px;
6
8
  --cui-audio-visualizer-line-fill: oklch(from var(--primary) l c h / 0.18);
7
9
  --cui-audio-visualizer-line-stroke: oklch(from var(--primary) l c h / 0.8);
8
10
  --cui-audio-visualizer-baseline-stroke: oklch(from var(--border) l c h / 0.55);
@@ -24,7 +26,7 @@
24
26
  }
25
27
 
26
28
  :where([data-control-family="audio-visualizer"][data-slot="track"]) {
27
- gap: 1px;
29
+ gap: var(--cui-audio-visualizer-bar-gap);
28
30
  opacity: 0.55;
29
31
  transition: opacity var(--duration-base) var(--ease-standard);
30
32
  }
@@ -34,7 +36,9 @@
34
36
  }
35
37
 
36
38
  :where([data-control-family="audio-visualizer"][data-slot="bar-track"]) {
37
- inline-size: calc(var(--spacing) * 1);
39
+ inline-size: var(--cui-audio-visualizer-bar-size);
40
+ min-inline-size: 0;
41
+ flex-shrink: 1;
38
42
  transition:
39
43
  opacity var(--duration-base) var(--ease-emphasized),
40
44
  filter var(--duration-base) var(--ease-emphasized),
@@ -69,6 +73,97 @@
69
73
  stroke: var(--cui-audio-visualizer-line-stroke);
70
74
  transition: d var(--duration-base) var(--ease-standard);
71
75
  }
76
+
77
+ :where([data-control-family="audio-visualizer"][data-variant="bars"][data-active="true"] [data-slot="bar"]) {
78
+ transition: none;
79
+ }
80
+
81
+ :where([data-control-family="audio-visualizer"][data-variant="line"]:not([data-active="true"]) [data-slot="waveform"]) {
82
+ opacity: 0;
83
+ }
84
+
85
+ :where([data-control-family="audio-visualizer"][data-variant="line"]:not([data-active="true"]) [data-slot="baseline"]) {
86
+ stroke-dasharray: 1 4;
87
+ stroke-linecap: round;
88
+ }
89
+
90
+ :where([data-control-family="audio-visualizer"][data-slot="root"][data-variant="bar"]) {
91
+ --cui-audio-visualizer-bar-size: calc(var(--spacing) * 1.5);
92
+ --cui-audio-visualizer-bar-gap: calc(var(--spacing) * 0.75);
93
+ padding-block: calc(var(--spacing) * 1);
94
+ }
95
+
96
+ :where([data-control-family="audio-visualizer"][data-variant="bar"] [data-slot="track"]) {
97
+ display: flex;
98
+ block-size: 100%;
99
+ align-items: center;
100
+ justify-content: center;
101
+ }
102
+
103
+ :where([data-control-family="audio-visualizer"][data-variant="bar"][data-align="start"] [data-slot="track"]) {
104
+ align-items: flex-start;
105
+ }
106
+
107
+ :where([data-control-family="audio-visualizer"][data-variant="bar"][data-align="end"] [data-slot="track"]) {
108
+ align-items: flex-end;
109
+ }
110
+
111
+ :where([data-control-family="audio-visualizer"][data-variant="bar"] [data-slot="bar"]) {
112
+ min-inline-size: 0;
113
+ max-inline-size: var(--cui-audio-visualizer-bar-size);
114
+ flex: 1;
115
+ transform-origin: center;
116
+ transition:
117
+ height var(--duration-fast) var(--ease-standard),
118
+ opacity var(--duration-fast) var(--ease-standard);
119
+ }
120
+
121
+ :where([data-control-family="audio-visualizer"][data-variant="bar"][data-align="start"] [data-slot="bar"]) {
122
+ transform-origin: top;
123
+ }
124
+
125
+ :where([data-control-family="audio-visualizer"][data-variant="bar"][data-align="end"] [data-slot="bar"]) {
126
+ transform-origin: bottom;
127
+ }
128
+
129
+ :where([data-control-family="audio-visualizer"][data-variant="bar"][data-idle="pulse"]:not([data-active="true"]) [data-slot="bar"]) {
130
+ animation: cui-audio-bar-pulse calc(var(--duration-slow) * 4) var(--ease-standard) infinite;
131
+ }
132
+
133
+ :where([data-control-family="audio-visualizer"][data-variant="bar"][data-idle="wave"]:not([data-active="true"]) [data-slot="bar"]),
134
+ :where([data-control-family="audio-visualizer"][data-variant="bar"][data-loading="true"] [data-slot="bar"]) {
135
+ animation: cui-audio-bar-pulse calc(var(--duration-slow) * 4) var(--ease-standard) infinite;
136
+ animation-delay: var(--_audio-visualizer-delay);
137
+ }
138
+
139
+ @keyframes cui-audio-bar-pulse {
140
+ 0%,
141
+ 100% {
142
+ transform: scaleY(1);
143
+ }
144
+ 50% {
145
+ transform: scaleY(4);
146
+ }
147
+ }
148
+
149
+ @media (prefers-reduced-motion: reduce) {
150
+ :where([data-control-family="audio-visualizer"] [data-slot]) {
151
+ animation: none;
152
+ transition: none;
153
+ }
154
+ }
155
+ }
156
+
157
+ @property --cui-audio-visualizer-bar-size {
158
+ syntax: "<length>";
159
+ inherits: true;
160
+ initial-value: 4px;
161
+ }
162
+
163
+ @property --cui-audio-visualizer-bar-gap {
164
+ syntax: "<length>";
165
+ inherits: true;
166
+ initial-value: 1px;
72
167
  }
73
168
 
74
169
  @property --cui-audio-visualizer-radius {
@@ -81,6 +81,17 @@
81
81
  padding: calc(var(--spacing) * 4) calc(var(--spacing) * 4) 0;
82
82
  }
83
83
 
84
+ :where([data-control-family="popup"][data-popup-kind="dialog"][data-slot="body"]),
85
+ :where([data-control-family="popup"][data-popup-kind="alert-dialog"][data-slot="body"]) {
86
+ gap: calc(var(--spacing) * 4);
87
+ padding-inline: calc(var(--spacing) * 4);
88
+ }
89
+
90
+ :where([data-control-family="popup"][data-popup-kind="dialog"][data-slot="body"]:not(:has(~ [data-slot="footer"]))),
91
+ :where([data-control-family="popup"][data-popup-kind="alert-dialog"][data-slot="body"]:not(:has(~ [data-slot="footer"]))) {
92
+ padding-block-end: calc(var(--spacing) * 4);
93
+ }
94
+
84
95
  :where([data-control-family="popup"][data-popup-kind="dialog"][data-slot="footer"]),
85
96
  :where([data-control-family="popup"][data-popup-kind="alert-dialog"][data-slot="footer"]) {
86
97
  gap: calc(var(--spacing) * 2);
@@ -1,3 +1,9 @@
1
+ @property --_popover-viewport-height {
2
+ syntax: "<length> | auto";
3
+ inherits: false;
4
+ initial-value: auto;
5
+ }
6
+
1
7
  @layer components {
2
8
  :where([data-control-family="popup"][data-popup-kind="popover"][data-slot="viewport"]) {
3
9
  block-size: var(--_popover-viewport-height, auto);
@@ -42,58 +42,68 @@
42
42
  translate: 0 100%;
43
43
  }
44
44
 
45
- :where(
46
- [data-control-family="tabs"][data-slot="root"]:has(> [data-slot="panel"][data-starting-style])
47
- > [data-slot="panel"][inert][data-ending-style]
48
- ) {
49
- translate: 0 0;
50
- transition: none;
51
- }
52
-
53
45
  :where([data-control-family="tabs"][data-slot="panel"][data-slide="panel"][data-starting-style]) {
54
46
  transition: none;
55
47
  }
56
48
 
57
- :where([data-control-family="tabs"][data-slot="root"][data-slide-starting] > [data-slot="panel"][data-slide="panel"]) {
49
+ :where([data-control-family="tabs"][data-slot="panel"][data-slide-starting][data-slide="panel"]) {
58
50
  transition: none;
59
51
  }
60
52
 
61
- :where([data-control-family="tabs"][data-slot="root"][data-slide-starting] > [data-slot="panel"][inert]) {
53
+ :where([data-control-family="tabs"][data-slot="panel"][data-slide-starting][inert]) {
62
54
  translate: 0 0;
63
55
  }
64
56
 
65
- :where(
66
- [data-control-family="tabs"][data-slot="root"][data-slide-starting]
67
- > [data-slot="panel"]:not([inert])[data-activation-direction="right"]
68
- ) {
57
+ :where([data-control-family="tabs"][data-slot="panel"][data-slide-starting]:not([inert])[data-activation-direction="right"]) {
69
58
  translate: 100% 0;
70
59
  }
71
60
 
72
- :where(
73
- [data-control-family="tabs"][data-slot="root"][data-slide-starting]
74
- > [data-slot="panel"]:not([inert])[data-activation-direction="left"]
75
- ) {
61
+ :where([data-control-family="tabs"][data-slot="panel"][data-slide-starting]:not([inert])[data-activation-direction="left"]) {
76
62
  translate: -100% 0;
77
63
  }
78
64
 
79
- :where(
80
- [data-control-family="tabs"][data-slot="root"][data-slide-starting]
81
- > [data-slot="panel"]:not([inert])[data-activation-direction="down"]
82
- ) {
65
+ :where([data-control-family="tabs"][data-slot="panel"][data-slide-starting]:not([inert])[data-activation-direction="down"]) {
83
66
  translate: 0 100%;
84
67
  }
85
68
 
86
- :where(
87
- [data-control-family="tabs"][data-slot="root"][data-slide-starting]
88
- > [data-slot="panel"]:not([inert])[data-activation-direction="up"]
89
- ) {
69
+ :where([data-control-family="tabs"][data-slot="panel"][data-slide-starting]:not([inert])[data-activation-direction="up"]) {
90
70
  translate: 0 -100%;
91
71
  }
92
72
 
93
73
  @supports (height: calc-size(auto, size)) {
94
- :where([data-control-family="tabs"][data-slot="root"][data-slide-starting] > [data-slot="panel"][data-slide="panel"]:not([inert])) {
95
- height: var(--aui-slide-prev-height, calc-size(auto, size));
74
+ :where([data-control-family="tabs"][data-slot="panel"][data-slide-starting][data-slide="panel"]:not([inert])) {
75
+ height: var(--_tabs-prev-height);
96
76
  }
97
77
  }
98
78
  }
99
79
  }
80
+
81
+ @property --_tabs-prev-height {
82
+ syntax: "<length>";
83
+ inherits: false;
84
+ initial-value: 0px;
85
+ }
86
+
87
+ @property --_tabs-panel-top {
88
+ syntax: "<length>";
89
+ inherits: false;
90
+ initial-value: 0px;
91
+ }
92
+
93
+ @property --_tabs-panel-left {
94
+ syntax: "<length>";
95
+ inherits: false;
96
+ initial-value: 0px;
97
+ }
98
+
99
+ @property --_tabs-panel-width {
100
+ syntax: "<length>";
101
+ inherits: false;
102
+ initial-value: 0px;
103
+ }
104
+
105
+ @property --_tabs-panel-height {
106
+ syntax: "<length>";
107
+ inherits: false;
108
+ initial-value: 0px;
109
+ }
@@ -183,6 +183,7 @@
183
183
  }
184
184
 
185
185
  :where([data-control-family="tabs"][data-slot="panel"]) {
186
+ min-height: 0;
186
187
  outline-style: none;
187
188
  }
188
189
 
@@ -638,6 +638,18 @@
638
638
  -moz-osx-font-smoothing: grayscale;
639
639
  }
640
640
 
641
+ html {
642
+ background-color: var(--background);
643
+ }
644
+
645
+ :root:has([data-app-shell]:not([data-scroll="page"])) {
646
+ overscroll-behavior: none;
647
+ }
648
+
649
+ :root:has([data-control-family="sidebar"][data-slot="wrapper"] [data-control-family="sidebar"][data-variant="inset"][data-state]) {
650
+ background-color: var(--sidebar);
651
+ }
652
+
641
653
  :where(
642
654
  [data-control-family]:is(
643
655
  a[href],
@@ -832,3 +844,34 @@
832
844
  }
833
845
  }
834
846
  }
847
+
848
+ /* Measured panel and list dimensions are consumed locally. */
849
+ @property --collapsible-panel-height {
850
+ syntax: "<length> | auto";
851
+ inherits: false;
852
+ initial-value: auto;
853
+ }
854
+
855
+ @property --collapsible-panel-width {
856
+ syntax: "<length> | auto";
857
+ inherits: false;
858
+ initial-value: auto;
859
+ }
860
+
861
+ @property --accordion-panel-height {
862
+ syntax: "<length> | auto";
863
+ inherits: false;
864
+ initial-value: auto;
865
+ }
866
+
867
+ @property --accordion-panel-width {
868
+ syntax: "<length> | auto";
869
+ inherits: false;
870
+ initial-value: auto;
871
+ }
872
+
873
+ @property --cmdk-list-height {
874
+ syntax: "<length>";
875
+ inherits: false;
876
+ initial-value: 0px;
877
+ }
@@ -23,6 +23,9 @@ export declare function AlertDialogContent({ className, children, ...props }: Al
23
23
  export declare function AlertDialogHeader({ className, ...props }: ComponentProps<"div"> & {
24
24
  style?: CSSProperties & PopupKnobStyle;
25
25
  }): import("react").JSX.Element;
26
+ export declare function AlertDialogBody({ className, ...props }: ComponentProps<"div"> & {
27
+ style?: CSSProperties & PopupKnobStyle;
28
+ }): import("react").JSX.Element;
26
29
  export declare function AlertDialogFooter({ className, ...props }: ComponentProps<"div"> & {
27
30
  style?: CSSProperties & PopupKnobStyle;
28
31
  }): import("react").JSX.Element;
@@ -68,6 +68,16 @@ function AlertDialogHeader({ className, ...props }) {
68
68
  ...props
69
69
  });
70
70
  }
71
+ function AlertDialogBody({ className, ...props }) {
72
+ return /* @__PURE__ */ jsx("div", {
73
+ "data-control-ui": "alert-dialog",
74
+ "data-control-family": "popup",
75
+ "data-popup-kind": "alert-dialog",
76
+ "data-slot": "body",
77
+ className: cn("grid", className),
78
+ ...props
79
+ });
80
+ }
71
81
  function AlertDialogFooter({ className, ...props }) {
72
82
  return /* @__PURE__ */ jsx("div", {
73
83
  "data-control-ui": "alert-dialog",
@@ -99,4 +109,4 @@ function AlertDialogDescription({ className, ...props }) {
99
109
  });
100
110
  }
101
111
  //#endregion
102
- export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger };
112
+ export { AlertDialog, AlertDialogBody, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger };
@@ -1,7 +1,7 @@
1
1
  import { OpenChangeEventDetails } from "../control-props.js";
2
2
  import { ControlSize } from "../control-variants.js";
3
- import { PopupKnobStyle } from "../knob-contracts/popup-knobs.js";
4
3
  import { FieldKnobStyle } from "../knob-contracts/field-knobs.js";
4
+ import { PopupKnobStyle } from "../knob-contracts/popup-knobs.js";
5
5
  import { CSSProperties, ComponentProps, ReactNode } from "react";
6
6
  //#region src/ui/autocomplete.d.ts
7
7
  export type AutocompleteProps<Value = string> = {
@@ -3,12 +3,12 @@ import { cn } from "../lib/cn.js";
3
3
  import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { Button } from "./button.js";
6
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
6
7
  import { Input } from "./input.js";
7
8
  import { useColorArea } from "../hooks/use-color-area.js";
8
9
  import { formatColor, getChannels, hsvaToRgba, parseColor, pointToHueSat, pointToSaturationValue, setChannel } from "../lib/color.js";
9
10
  import { TARGET_RATIO, contrastOf, fixColorForContrast, formatContrastRatio, nextFixLevel, wcagLevels } from "../lib/contrast.js";
10
11
  import { NumberField, NumberFieldGroup, NumberFieldInput } from "./number-field.js";
11
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
12
12
  import { createContext, useContext, useEffect, useId, useRef, useState, useSyncExternalStore } from "react";
13
13
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
14
14
  import { Popover } from "@base-ui/react/popover";
@@ -1,6 +1,6 @@
1
1
  import { ControlSize } from "../control-variants.js";
2
- import { PopupKnobStyle } from "../knob-contracts/popup-knobs.js";
3
2
  import { FieldKnobStyle } from "../knob-contracts/field-knobs.js";
3
+ import { PopupKnobStyle } from "../knob-contracts/popup-knobs.js";
4
4
  import { CSSProperties, ComponentProps, ReactNode } from "react";
5
5
  import { Combobox as Combobox$1 } from "@base-ui/react/combobox";
6
6
  //#region src/ui/combobox.d.ts
@@ -75,7 +75,7 @@ export declare function ContextMenuContent({ className, children, ...props }: Co
75
75
  export declare function ContextMenuItem({ className, inset, ...props }: ContextMenuItemProps): import("react").JSX.Element;
76
76
  export declare function ContextMenuCheckboxItem({ className, children, ...props }: ContextMenuCheckboxItemProps): import("react").JSX.Element;
77
77
  export declare function ContextMenuRadioGroup<TValue extends string = string>({ className, ...props }: ContextMenuRadioGroupProps<TValue>): import("react").JSX.Element;
78
- export declare function ContextMenuRadioItem({ className, children, ...props }: ContextMenuRadioItemProps): import("react").JSX.Element;
78
+ export declare function ContextMenuRadioItem({ className, children, value, ...props }: ContextMenuRadioItemProps): import("react").JSX.Element;
79
79
  export declare function ContextMenuLabel({ className, inset, ...props }: ContextMenuLabelProps): import("react").JSX.Element;
80
80
  export declare function ContextMenuSeparator({ className, ...props }: ContextMenuSeparatorProps): import("react").JSX.Element;
81
81
  export declare function ContextMenuShortcut({ className, ...props }: ContextMenuShortcutProps): import("react").JSX.Element;
@@ -2,8 +2,10 @@
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
+ import { popupItemFocusProps } from "../lib/popup-item-focus.js";
5
6
  import { popupItemStructureClasses } from "../surface-variants.js";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { mergeProps } from "@base-ui/react/merge-props";
7
9
  import { ContextMenu as ContextMenu$1 } from "@base-ui/react/context-menu";
8
10
  //#region src/ui/context-menu.tsx
9
11
  const popupClasses = "min-w-[10rem]";
@@ -63,7 +65,7 @@ function ContextMenuItem({ className, inset = false, ...props }) {
63
65
  "data-popup-part": "item",
64
66
  "data-inset": inset || void 0,
65
67
  className: cn(itemClasses, className),
66
- ...props
68
+ ...mergeProps(popupItemFocusProps, props)
67
69
  });
68
70
  }
69
71
  function ContextMenuCheckboxItem({ className, children, ...props }) {
@@ -74,7 +76,7 @@ function ContextMenuCheckboxItem({ className, children, ...props }) {
74
76
  "data-control-family": "popup",
75
77
  "data-popup-part": "item",
76
78
  className: cn(itemClasses, className),
77
- ...props,
79
+ ...mergeProps(popupItemFocusProps, props),
78
80
  children: [/* @__PURE__ */ jsx("span", {
79
81
  className: "pointer-events-none absolute start-2 flex size-4 items-center justify-center",
80
82
  children: /* @__PURE__ */ jsx(ContextMenu$1.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx("svg", {
@@ -103,15 +105,16 @@ function ContextMenuRadioGroup({ className, ...props }) {
103
105
  ...props
104
106
  });
105
107
  }
106
- function ContextMenuRadioItem({ className, children, ...props }) {
108
+ function ContextMenuRadioItem({ className, children, value, ...props }) {
107
109
  return /* @__PURE__ */ jsxs(ContextMenu$1.RadioItem, {
110
+ value,
108
111
  "data-control-ui": "context-menu",
109
112
  "data-popup-kind": "context-menu",
110
113
  "data-slot": "radio-item",
111
114
  "data-control-family": "popup",
112
115
  "data-popup-part": "item",
113
116
  className: cn(itemClasses, className),
114
- ...props,
117
+ ...mergeProps(popupItemFocusProps, props),
115
118
  children: [/* @__PURE__ */ jsx("span", {
116
119
  className: "pointer-events-none absolute start-2 flex size-4 items-center justify-center",
117
120
  children: /* @__PURE__ */ jsx(ContextMenu$1.RadioItemIndicator, { children: /* @__PURE__ */ jsx("span", {
@@ -27,6 +27,9 @@ export declare function DialogContent({ className, children, showCloseButton, cl
27
27
  export declare function DialogHeader({ className, ...props }: ComponentProps<"div"> & {
28
28
  style?: CSSProperties & PopupKnobStyle;
29
29
  }): import("react").JSX.Element;
30
+ export declare function DialogBody({ className, ...props }: ComponentProps<"div"> & {
31
+ style?: CSSProperties & PopupKnobStyle;
32
+ }): import("react").JSX.Element;
30
33
  export declare function DialogFooter({ className, ...props }: ComponentProps<"div"> & {
31
34
  style?: CSSProperties & PopupKnobStyle;
32
35
  }): import("react").JSX.Element;