@ctrl-ui/react 0.9.15 → 0.9.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,6 +54,7 @@ These rules keep call sites honest. They come from one policy (`lint/class-polic
54
54
  - `no-component-style-classes` errors when `className` on a component imported from `@ctrl-ui/react` (or a `components/control-ui` directory) sets what the component owns: colors, type, padding, borders, radius, shadows, filters, state styles such as `hover:` and `data-[…]:`, arbitrary properties that paint (`[background:…]`), or `!important`. Every `text-*` class counts except alignment and wrapping (`text-center`, `text-pretty`, `text-ellipsis`…). The rule also reads string constants the `className` references in the same file, and the `className` of the element passed to the component's `render` prop. On any other element, it errors on classes that reach into a component's parts, such as `[&_[data-slot=trigger]]:` or `*:data-[slot=icon]:`. Layout classes (`flex`, `gap-*`, widths, heights, margins, placement) stay allowed. Restyle through the component's props or its `--cui-*` knobs instead.
55
55
  - `no-component-style-props` applies the same boundary to `style`: `--cui-*` knobs and layout keys pass, while paint keys error (`color`, `background`, `font*`, `padding*`, `border*`, `boxShadow`, `opacity`, `filter`…). It also checks same-file constant objects and the element passed to `render`. When the paint is data, such as a user-picked fill, put it on a plain element of your own and let the component inherit it.
56
56
  - `no-component-part-css` errors when a stylesheet paints a component part: a rule whose subject is `[data-slot]`, `[data-control-family]`, or another `[data-control…]` attribute, including nested `&` rules and `@apply`. Setting the part's `--cui-*` knobs and layout there stays allowed. The preset skips installed Control UI source under `components/control-ui`.
57
+ - Paint-free components own no look, so both component rules treat them like plain elements: `RevealScope` can be the painted row or card itself, through `className`, `style`, or its `render` element. Part selectors on it still error, and `Reveal` keeps the full rule because its recipe owns opacity.
57
58
  - `no-raw-type-classes` errors on text sizes in any `className`: the type rungs, Tailwind's `text-xs`…`text-9xl`, and arbitrary `text-[13px]`. It reaches beyond Control UI, so adopt it folder by folder while you migrate.
58
59
 
59
60
  The Biome rules need Biome 2.5.0 or later: the preset scopes the stylesheet rule with plugin `includes`.
@@ -1,4 +1,4 @@
1
1
  //#region src/knob-contracts/item-knobs.d.ts
2
- export declare const itemKnobs: readonly ["--cui-item-radius", "--cui-item-hover-background", "--cui-item-current-background", "--cui-item-hover-border-color", "--cui-item-border-width", "--cui-item-active-scale", "--cui-item-gap", "--cui-item-padding-block", "--cui-item-padding-inline", "--cui-item-description-font-size", "--cui-item-description-line-height", "--cui-item-description-foreground", "--cui-item-media-foreground"];
2
+ export declare const itemKnobs: readonly ["--cui-item-radius", "--cui-item-hover-background", "--cui-item-current-background", "--cui-item-foreground", "--cui-item-selected-background", "--cui-item-selected-foreground", "--cui-item-dragging-opacity", "--cui-item-drop-target-shadow", "--cui-item-hover-border-color", "--cui-item-border-width", "--cui-item-active-scale", "--cui-item-gap", "--cui-item-padding-block", "--cui-item-padding-inline", "--cui-item-description-font-size", "--cui-item-description-line-height", "--cui-item-description-foreground", "--cui-item-media-foreground"];
3
3
  export type ItemKnobStyle = Partial<Record<(typeof itemKnobs)[number], string>>;
4
4
  //#endregion
@@ -3,6 +3,11 @@ const itemKnobs = [
3
3
  "--cui-item-radius",
4
4
  "--cui-item-hover-background",
5
5
  "--cui-item-current-background",
6
+ "--cui-item-foreground",
7
+ "--cui-item-selected-background",
8
+ "--cui-item-selected-foreground",
9
+ "--cui-item-dragging-opacity",
10
+ "--cui-item-drop-target-shadow",
6
11
  "--cui-item-hover-border-color",
7
12
  "--cui-item-border-width",
8
13
  "--cui-item-active-scale",
@@ -1,4 +1,4 @@
1
1
  //#region src/knob-contracts/resize-handle-knobs.d.ts
2
- export declare const resizeHandleKnobs: readonly ["--cui-resize-handle-size", "--cui-resize-handle-hit-size", "--cui-resize-handle-background", "--cui-resize-handle-hover-background", "--cui-resize-handle-border-color", "--cui-resize-handle-border-width", "--cui-resize-handle-radius"];
2
+ export declare const resizeHandleKnobs: readonly ["--cui-resize-handle-size", "--cui-resize-handle-hit-size", "--cui-resize-handle-background", "--cui-resize-handle-hover-background", "--cui-resize-handle-border-color", "--cui-resize-handle-border-width", "--cui-resize-handle-radius", "--cui-resize-handle-shadow"];
3
3
  export type ResizeHandleKnobStyle = Partial<Record<(typeof resizeHandleKnobs)[number], string>>;
4
4
  //#endregion
@@ -6,7 +6,8 @@ const resizeHandleKnobs = [
6
6
  "--cui-resize-handle-hover-background",
7
7
  "--cui-resize-handle-border-color",
8
8
  "--cui-resize-handle-border-width",
9
- "--cui-resize-handle-radius"
9
+ "--cui-resize-handle-radius",
10
+ "--cui-resize-handle-shadow"
10
11
  ];
11
12
  //#endregion
12
13
  export { resizeHandleKnobs };
@@ -1,4 +1,4 @@
1
1
  //#region src/knob-contracts/tabs-knobs.d.ts
2
- export declare const tabsKnobs: readonly ["--cui-tabs-surface-background", "--cui-tabs-surface-radius", "--cui-tabs-trigger-padding-inline", "--cui-tabs-trigger-radius", "--cui-tabs-trigger-height", "--cui-tabs-list-padding", "--cui-tabs-list-radius", "--cui-tabs-list-background", "--cui-tabs-list-backdrop-filter", "--cui-tabs-indicator-background", "--cui-tabs-foreground", "--cui-tabs-font-size", "--cui-tabs-font-weight", "--cui-tabs-active-foreground", "--cui-tabs-hover-foreground", "--cui-tabs-indicator-shadow", "--cui-tabs-list-shadow", "--cui-tabs-border-color", "--cui-tabs-border-width", "--cui-tabs-indicator-background-image"];
2
+ export declare const tabsKnobs: readonly ["--cui-tabs-surface-background", "--cui-tabs-surface-radius", "--cui-tabs-trigger-padding-inline", "--cui-tabs-trigger-padding-inline-start", "--cui-tabs-trigger-padding-inline-end", "--cui-tabs-trigger-radius", "--cui-tabs-trigger-height", "--cui-tabs-list-padding", "--cui-tabs-list-radius", "--cui-tabs-list-background", "--cui-tabs-list-backdrop-filter", "--cui-tabs-indicator-background", "--cui-tabs-foreground", "--cui-tabs-font-size", "--cui-tabs-font-weight", "--cui-tabs-active-foreground", "--cui-tabs-hover-foreground", "--cui-tabs-indicator-shadow", "--cui-tabs-list-shadow", "--cui-tabs-border-color", "--cui-tabs-border-width", "--cui-tabs-indicator-background-image"];
3
3
  export type TabsKnobStyle = Partial<Record<(typeof tabsKnobs)[number], string>>;
4
4
  //#endregion
@@ -3,6 +3,8 @@ const tabsKnobs = [
3
3
  "--cui-tabs-surface-background",
4
4
  "--cui-tabs-surface-radius",
5
5
  "--cui-tabs-trigger-padding-inline",
6
+ "--cui-tabs-trigger-padding-inline-start",
7
+ "--cui-tabs-trigger-padding-inline-end",
6
8
  "--cui-tabs-trigger-radius",
7
9
  "--cui-tabs-trigger-height",
8
10
  "--cui-tabs-list-padding",
@@ -46,6 +46,18 @@
46
46
  }
47
47
  }
48
48
 
49
+ :where([data-control-family="badge"][data-slot="root"][data-color="primary"]) {
50
+ --_badge-color-background: var(--primary);
51
+ --_badge-color-foreground: var(--primary-foreground);
52
+ --_badge-color-border: var(--primary);
53
+ --_badge-color-background-hover: oklch(from var(--primary) calc(l - 0.05) c h);
54
+ }
55
+
56
+ :where([data-control-family="badge"][data-slot="root"][data-color="primary"][data-variant="outline"]) {
57
+ --_badge-color-background: oklch(from var(--primary) l c h / var(--badge-fill-alpha));
58
+ --_badge-color-foreground: var(--primary-text);
59
+ }
60
+
49
61
  :where([data-control-family="badge"][data-slot="root"][data-color="red"]) {
50
62
  --_badge-color-background: var(--badge-red);
51
63
  --_badge-color-foreground: var(--badge-red-foreground);
@@ -84,11 +84,11 @@
84
84
  }
85
85
 
86
86
  :where([data-control-family="card"][data-slot="root"][data-variant="sectioned"] > [data-slot="content"]) {
87
- padding-block: calc(var(--spacing) * 6);
87
+ padding-block: var(--cui-card-padding-block);
88
88
  }
89
89
 
90
90
  :where([data-control-family="card"][data-slot="root"][data-variant="sectioned"] > [data-slot="header"]) {
91
- padding-block: calc(var(--spacing) * 5);
91
+ padding-block: calc(var(--cui-card-padding-block) - var(--spacing));
92
92
  border-block-end: 1px solid oklch(from var(--border) l c h / 0.7);
93
93
  }
94
94
 
@@ -1,8 +1,13 @@
1
1
  @layer components {
2
2
  :where([data-control-family="item"][data-slot="root"]) {
3
3
  --cui-item-radius: var(--radius-lg);
4
+ --cui-item-foreground: var(--foreground);
4
5
  --cui-item-hover-background: var(--hover-fill);
5
6
  --cui-item-current-background: var(--active-fill);
7
+ --cui-item-selected-background: var(--cui-item-current-background);
8
+ --cui-item-selected-foreground: var(--cui-item-foreground);
9
+ --cui-item-dragging-opacity: 0.5;
10
+ --cui-item-drop-target-shadow: inset 0 0 0 var(--focus-ring-width) var(--primary);
6
11
  --cui-item-hover-border-color: var(--_item-border-color);
7
12
  --cui-item-border-width: 0px;
8
13
  --cui-item-active-scale: 0.99;
@@ -20,7 +25,7 @@
20
25
  border-radius: var(--cui-item-radius);
21
26
  background: var(--_item-background);
22
27
  box-shadow: none;
23
- color: var(--foreground);
28
+ color: var(--cui-item-foreground);
24
29
  transition-property: scale;
25
30
  transition-duration: var(--duration-fast);
26
31
  transition-timing-function: var(--ease-standard);
@@ -72,11 +77,16 @@
72
77
 
73
78
  @media (hover: hover) {
74
79
  :where(
75
- [data-control-family="item"][data-slot="root"]:is(a, button, [href], [role="button"]):not(
76
- :disabled,
77
- [data-disabled],
78
- [aria-disabled="true"]
79
- ):hover
80
+ [data-control-family="item"][data-slot="root"]:is(
81
+ a,
82
+ button,
83
+ [href],
84
+ [role="button"],
85
+ [role="treeitem"],
86
+ [role="option"],
87
+ [role="row"],
88
+ [role="menuitem"]
89
+ ):not(:disabled, [data-disabled], [aria-disabled="true"]):hover
80
90
  ) {
81
91
  outline-color: var(--cui-item-hover-border-color);
82
92
  background: var(--cui-item-hover-background);
@@ -84,6 +94,34 @@
84
94
  }
85
95
  }
86
96
 
97
+ :where([data-control-family="item"][data-slot="root"][aria-selected="true"]) {
98
+ background: var(--cui-item-selected-background);
99
+ color: var(--cui-item-selected-foreground);
100
+ }
101
+
102
+ :where([data-control-family="item"][data-slot="root"][data-dragging]) {
103
+ opacity: var(--cui-item-dragging-opacity);
104
+ }
105
+
106
+ :where([data-control-family="item"][data-slot="root"][data-drop-target]) {
107
+ box-shadow: var(--cui-item-drop-target-shadow);
108
+ }
109
+
110
+ @media (forced-colors: active) {
111
+ :where([data-control-family="item"][data-slot="root"][aria-selected="true"]) {
112
+ --cui-item-description-foreground: HighlightText;
113
+ --cui-item-media-foreground: HighlightText;
114
+ forced-color-adjust: none;
115
+ background: Highlight;
116
+ color: HighlightText;
117
+ }
118
+
119
+ :where([data-control-family="item"][data-slot="root"][data-drop-target]) {
120
+ outline: var(--focus-ring-width) solid Highlight;
121
+ outline-offset: calc(var(--focus-ring-width) * -1);
122
+ }
123
+ }
124
+
87
125
  :where(
88
126
  [data-control-family="item"][data-slot="root"]:is(a, button, [href], [role="button"]):not(
89
127
  :disabled,
@@ -138,6 +176,35 @@
138
176
  initial-value: transparent;
139
177
  }
140
178
 
179
+ @property --cui-item-foreground {
180
+ syntax: "<color>";
181
+ inherits: true;
182
+ initial-value: transparent;
183
+ }
184
+
185
+ @property --cui-item-selected-background {
186
+ syntax: "<color>";
187
+ inherits: true;
188
+ initial-value: transparent;
189
+ }
190
+
191
+ @property --cui-item-selected-foreground {
192
+ syntax: "<color>";
193
+ inherits: true;
194
+ initial-value: transparent;
195
+ }
196
+
197
+ @property --cui-item-dragging-opacity {
198
+ syntax: "<number>";
199
+ inherits: true;
200
+ initial-value: 1;
201
+ }
202
+
203
+ @property --cui-item-drop-target-shadow {
204
+ syntax: "*";
205
+ inherits: true;
206
+ }
207
+
141
208
  @property --cui-item-hover-border-color {
142
209
  syntax: "<color>";
143
210
  inherits: true;
@@ -25,6 +25,21 @@
25
25
  --cui-resizable-group-radius: 0px;
26
26
  }
27
27
 
28
+ /* biome-ignore-start lint/complexity/noImportantStyles: react-resizable-panels sets inline display on the group and panels. */
29
+ :where([data-control-family="resizable"][data-slot="panel-group"][data-layout="contents"]) {
30
+ display: contents !important;
31
+ }
32
+
33
+ :where([data-control-family="resizable"][data-slot="panel-group"]:not([data-layout="resizable"]) > [data-slot="panel"]),
34
+ :where([data-control-family="resizable"][data-slot="panel-group"]:not([data-layout="resizable"]) > [data-slot="panel"] > div) {
35
+ display: contents !important;
36
+ }
37
+
38
+ :where([data-control-family="resizable"][data-slot="panel-group"]:not([data-layout="resizable"]) > [data-slot="handle"]) {
39
+ display: none !important;
40
+ }
41
+ /* biome-ignore-end lint/complexity/noImportantStyles: react-resizable-panels sets inline display on the group and panels. */
42
+
28
43
  :where([data-control-family="resizable"][data-slot="handle"]) {
29
44
  --_resizable-gradient-color: var(--cui-resizable-handle-hover-background);
30
45
  background-color: var(--cui-resizable-handle-background);
@@ -7,11 +7,13 @@
7
7
  --cui-resize-handle-border-color: var(--primary);
8
8
  --cui-resize-handle-border-width: var(--control-rim-width);
9
9
  --cui-resize-handle-radius: 0px;
10
+ --cui-resize-handle-shadow: none;
10
11
  inline-size: var(--cui-resize-handle-hit-size);
11
12
  block-size: var(--cui-resize-handle-hit-size);
12
13
  translate: -50% -50%;
13
14
  padding: 0;
14
15
  border: 0;
16
+ border-radius: var(--cui-resize-handle-radius);
15
17
  background: transparent;
16
18
  }
17
19
 
@@ -25,6 +27,7 @@
25
27
  border: var(--cui-resize-handle-border-width) solid var(--cui-resize-handle-border-color);
26
28
  border-radius: var(--cui-resize-handle-radius);
27
29
  background: var(--cui-resize-handle-background);
30
+ box-shadow: var(--cui-resize-handle-shadow);
28
31
  transition: background-color var(--duration-fast) var(--ease-standard);
29
32
  }
30
33
 
@@ -124,3 +127,8 @@
124
127
  inherits: true;
125
128
  initial-value: 0px;
126
129
  }
130
+
131
+ @property --cui-resize-handle-shadow {
132
+ syntax: "*";
133
+ inherits: true;
134
+ }
@@ -34,6 +34,8 @@
34
34
 
35
35
  :where([data-control-family="tabs"][data-slot="list"]) {
36
36
  --cui-tabs-trigger-height: var(--control-h-sm);
37
+ --cui-tabs-trigger-padding-inline-start: var(--cui-tabs-trigger-padding-inline);
38
+ --cui-tabs-trigger-padding-inline-end: var(--cui-tabs-trigger-padding-inline);
37
39
  --_tabs-trigger-radius-fit: min(var(--cui-tabs-trigger-radius), calc(var(--cui-tabs-trigger-height) / 2));
38
40
  position: relative;
39
41
  display: flex;
@@ -105,7 +107,7 @@
105
107
  gap: calc(var(--spacing) * 1.5);
106
108
  white-space: nowrap;
107
109
  border-radius: var(--_tabs-trigger-radius-fit);
108
- padding-inline: var(--cui-tabs-trigger-padding-inline);
110
+ padding-inline: var(--cui-tabs-trigger-padding-inline-start) var(--cui-tabs-trigger-padding-inline-end);
109
111
  color: var(--cui-tabs-foreground);
110
112
  font-size: var(--cui-tabs-font-size);
111
113
  font-weight: var(--cui-tabs-font-weight);
@@ -285,6 +287,18 @@
285
287
  initial-value: 0px;
286
288
  }
287
289
 
290
+ @property --cui-tabs-trigger-padding-inline-start {
291
+ syntax: "<length-percentage>";
292
+ inherits: true;
293
+ initial-value: 0px;
294
+ }
295
+
296
+ @property --cui-tabs-trigger-padding-inline-end {
297
+ syntax: "<length-percentage>";
298
+ inherits: true;
299
+ initial-value: 0px;
300
+ }
301
+
288
302
  @property --cui-tabs-trigger-radius {
289
303
  syntax: "<length-percentage>";
290
304
  inherits: true;
@@ -7,7 +7,7 @@ export type BadgeVariant = (typeof badgeVariants)[number];
7
7
  export declare const badgeSizes: readonly ["sm", "md"];
8
8
  export type BadgeSize = (typeof badgeSizes)[number];
9
9
  export declare const BADGE_COLORS: readonly ["neutral", "red", "orange", "yellow", "green", "blue", "purple", "pink"];
10
- export type BadgeColor = (typeof BADGE_COLORS)[number];
10
+ export type BadgeColor = (typeof BADGE_COLORS)[number] | "primary";
11
11
  export type BadgeProps = Omit<ComponentProps<"span">, "style"> & {
12
12
  variant?: BadgeVariant;
13
13
  size?: BadgeSize;
@@ -9,10 +9,12 @@ type DropdownMenuStyledProps<PrimitiveProps> = Omit<PrimitiveProps, "className"
9
9
  className?: string;
10
10
  style?: CSSProperties & PopupKnobStyle;
11
11
  };
12
- export type DropdownMenuProps = Menu.Root.Props;
12
+ export type DropdownMenuProps<Payload = unknown> = Menu.Root.Props<Payload>;
13
+ export type DropdownMenuHandle<Payload = unknown> = Menu.Handle<Payload>;
14
+ export declare const createDropdownMenuHandle: typeof Menu.createHandle;
13
15
  export declare const dropdownMenuTriggerVariants: readonly ["surface", "ghost"];
14
16
  export type DropdownMenuTriggerVariant = (typeof dropdownMenuTriggerVariants)[number];
15
- export type DropdownMenuTriggerProps = Omit<ComponentProps<"button">, "style"> & Pick<ComponentProps<typeof Menu.Trigger>, "nativeButton" | "render"> & {
17
+ export type DropdownMenuTriggerProps<Payload = unknown> = Omit<ComponentProps<"button">, "style"> & Pick<Menu.Trigger.Props<Payload>, "nativeButton" | "render" | "handle" | "payload"> & {
16
18
  style?: CSSProperties & ButtonKnobStyle;
17
19
  } & {
18
20
  size?: ControlSize;
@@ -37,8 +39,8 @@ export type DropdownMenuSeparatorProps = Omit<ComponentProps<"div">, "style"> &
37
39
  export type DropdownMenuLabelProps = Omit<ComponentProps<"div">, "style"> & {
38
40
  style?: CSSProperties & PopupKnobStyle;
39
41
  };
40
- export declare function DropdownMenu({ children, ...props }: DropdownMenuProps): import("react").JSX.Element;
41
- export declare function DropdownMenuTrigger({ size, iconOnly, variant, className, ...props }: DropdownMenuTriggerProps): import("react").JSX.Element;
42
+ export declare function DropdownMenu<Payload>({ children, ...props }: DropdownMenuProps<Payload>): import("react").JSX.Element;
43
+ export declare function DropdownMenuTrigger<Payload>({ size, iconOnly, variant, className, ...props }: DropdownMenuTriggerProps<Payload>): import("react").JSX.Element;
42
44
  export declare function DropdownMenuContent({ className, children, side, sideOffset, align, alignOffset, collisionBoundary, collisionPadding, collisionAvoidance, ...props }: DropdownMenuContentProps): import("react").JSX.Element;
43
45
  export declare function DropdownMenuItem({ tone, className, ...props }: DropdownMenuItemProps): import("react").JSX.Element;
44
46
  export declare function DropdownMenuSeparator({ className, ...props }: DropdownMenuSeparatorProps): import("react").JSX.Element;
@@ -9,6 +9,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
9
9
  import { mergeProps } from "@base-ui/react/merge-props";
10
10
  import { Menu } from "@base-ui/react/menu";
11
11
  //#region src/ui/dropdown-menu.tsx
12
+ const createDropdownMenuHandle = Menu.createHandle;
12
13
  const dropdownMenuTriggerVariants = ["surface", "ghost"];
13
14
  function DropdownMenu({ children, ...props }) {
14
15
  return /* @__PURE__ */ jsx(Menu.Root, {
@@ -209,4 +210,4 @@ function DropdownMenuShortcut(props) {
209
210
  });
210
211
  }
211
212
  //#endregion
212
- export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, dropdownMenuTriggerVariants };
213
+ export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, createDropdownMenuHandle, dropdownMenuTriggerVariants };
@@ -5,10 +5,12 @@ export type ResizableLayout = {
5
5
  [panelId: string]: number;
6
6
  };
7
7
  export type ResizablePanelGroupVariant = "framed" | "nested";
8
+ export type ResizablePanelGroupLayout = "resizable" | "static" | "contents";
8
9
  export type ResizableHandleVariant = "solid" | "hover";
9
10
  export type ResizablePanelGroupProps = Omit<ComponentProps<"div">, "style"> & {
10
11
  orientation?: "horizontal" | "vertical";
11
12
  variant?: ResizablePanelGroupVariant;
13
+ layout?: ResizablePanelGroupLayout;
12
14
  defaultLayout?: ResizableLayout;
13
15
  disableCursor?: boolean;
14
16
  disabled?: boolean;
@@ -66,7 +68,7 @@ export type ResizableFloatingPanelProps = Omit<ComponentProps<"div">, "style"> &
66
68
  getAriaValueText?: (size: number) => string;
67
69
  style?: ResizableFloatingPanelStyle;
68
70
  };
69
- export declare function ResizablePanelGroup({ className, orientation, variant, disableCursor, children, ...props }: ResizablePanelGroupProps): import("react").JSX.Element;
71
+ export declare function ResizablePanelGroup({ className, orientation, variant, layout, disabled, disableCursor, children, ...props }: ResizablePanelGroupProps): import("react").JSX.Element;
70
72
  export declare function ResizablePanel({ className, ...props }: ResizablePanelProps): import("react").JSX.Element;
71
73
  export declare function ResizableHandle({ className, variant, withHandle, children, "aria-label": ariaLabel, ...props }: ResizableHandleProps): import("react").JSX.Element;
72
74
  export declare function ResizableFloatingPanel({ side, size: controlledSize, defaultSize, onSizeChange, minSize, maxSize, handleVariant, handleLabel, getAriaValueText, className, style, children, ...props }: ResizableFloatingPanelProps): import("react").JSX.Element;
@@ -39,7 +39,7 @@ function useResizeCursor(handleRef, disableCursor) {
39
39
  };
40
40
  }, [handleRef, disableCursor]);
41
41
  }
42
- function ResizablePanelGroup({ className, orientation = "horizontal", variant = "framed", disableCursor = false, children, ...props }) {
42
+ function ResizablePanelGroup({ className, orientation = "horizontal", variant = "framed", layout = "resizable", disabled = false, disableCursor = false, children, ...props }) {
43
43
  return /* @__PURE__ */ jsx(ResizableGroupContext.Provider, {
44
44
  value: {
45
45
  orientation,
@@ -51,7 +51,9 @@ function ResizablePanelGroup({ className, orientation = "horizontal", variant =
51
51
  "data-slot": "panel-group",
52
52
  "data-surface": "panel",
53
53
  "data-variant": variant,
54
+ "data-layout": layout,
54
55
  orientation,
56
+ disabled: disabled || layout !== "resizable",
55
57
  disableCursor: true,
56
58
  className: cn("flex h-full w-full", className),
57
59
  ...props,
@@ -25,6 +25,7 @@ declare const weightClassNames: {
25
25
  readonly normal: "font-normal";
26
26
  readonly medium: "font-medium";
27
27
  readonly semibold: "font-semibold";
28
+ readonly bold: "font-bold";
28
29
  };
29
30
  export type TypeSize = keyof typeof sizeClassNames;
30
31
  export type TypeTone = keyof typeof toneClassNames;
@@ -25,7 +25,8 @@ const toneClassNames = {
25
25
  const weightClassNames = {
26
26
  normal: "font-normal",
27
27
  medium: "font-medium",
28
- semibold: "font-semibold"
28
+ semibold: "font-semibold",
29
+ bold: "font-bold"
29
30
  };
30
31
  const headingSizeByLevel = {
31
32
  1: "heading-1",
@@ -1,5 +1,7 @@
1
1
  const controlUiImportPath = `(?:@ctrl-ui/react|[^'"]*components/control-ui)/`;
2
2
 
3
+ export const paintFreeComponents = ["RevealScope"];
4
+
3
5
  const escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4
6
 
5
7
  export function controlUiImportPattern(importSources = []) {
@@ -6,6 +6,7 @@ import {
6
6
  componentStyleMessage,
7
7
  componentStylePropMessage,
8
8
  controlUiImportPattern,
9
+ paintFreeComponents,
9
10
  rawTypeClass,
10
11
  rawTypeMessage,
11
12
  } from "./class-policy.mjs";
@@ -77,7 +78,9 @@ function controlUiLocalNames(program, controlUiImport) {
77
78
  for (const statement of program.body) {
78
79
  if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
79
80
  if (!controlUiImport.test(statement.source.value)) continue;
80
- for (const specifier of statement.specifiers) names.add(specifier.local.name);
81
+ for (const specifier of statement.specifiers) {
82
+ if (!paintFreeComponents.includes(specifier.imported?.name)) names.add(specifier.local.name);
83
+ }
81
84
  }
82
85
  return names;
83
86
  }
@@ -8,6 +8,7 @@ import {
8
8
  componentStyleMessage,
9
9
  componentStylePropMessage,
10
10
  controlUiImportPattern,
11
+ paintFreeComponents,
11
12
  paintProperty,
12
13
  rawTypeClass,
13
14
  rawTypeMessage,
@@ -30,6 +31,7 @@ const stringTexts = (name) =>
30
31
  const classLiterals = "or { JsStringLiteralExpression(), JsTemplateExpression(), JsObjectExpression() }";
31
32
  const renderedTag = "or { JsxTagExpression(tag=$tag), JsParenthesizedExpression(expression=JsxTagExpression(tag=$tag)) }";
32
33
  const componentName = `r"([A-Z]\\w*)(?:\\.[\\w.]+)?"`;
34
+ const paintFreeComponent = gritExactRegex(`(?:${paintFreeComponents.join("|")})`);
33
35
 
34
36
  function constantMatches(reference, literals, offends) {
35
37
  return `contains JsReferenceIdentifier() as $${reference} where {
@@ -97,6 +99,7 @@ function componentAttributePattern({ attribute, offends, message, importSources
97
99
  }
98
100
  } where {
99
101
  $name <: ${componentName}($component),
102
+ not $component <: ${paintFreeComponent},
100
103
  $attr <: within JsModule() as $module,
101
104
  ${offends("$attr")},
102
105
  ${importedFromControlUi("$component", importSources)},
@@ -115,6 +118,7 @@ function partClassPattern(importSources) {
115
118
  ${classNameMatches("$plainAttr", componentPartClass)},
116
119
  not and {
117
120
  $plainName <: ${componentName}($plainComponent),
121
+ not $plainComponent <: ${paintFreeComponent},
118
122
  ${importedFromControlUi("$plainComponent", importSources, "Plain")}
119
123
  },
120
124
  register_diagnostic(span=$plainAttr, message=${gritString(componentPartMessage)}, severity="error")
@@ -31,6 +31,7 @@ or {
31
31
  }
32
32
  } where {
33
33
  $name <: r"([A-Z]\w*)(?:\.[\w.]+)?"($component),
34
+ not $component <: r"(?:RevealScope)",
34
35
  $attr <: within JsModule() as $module,
35
36
  $attr <: or {
36
37
  contains or { JsxString(), JsStringLiteralExpression(), JsTemplateChunkElement(), JsLiteralMemberName() as $attrMember where { $attrMember <: r"(?s)[\"'].*" } } as $attrText where { $attrText <: r"(?s).*(?:[^\s\"'\x60]!(?:[\s\"'\x60]|$)|(?:^|[\s\"'\x60])![\w\[-]|(?:^|[^\w-])(?:p[xytrblse]?-|bg-[\w\[]|text-(?:(?:[\s\"'\x60]|$)|[^lcrjsewnbp\s\"'\x60]|l(?:(?:[\s\"'\x60]|$)|[^e\s\"'\x60]|e(?:(?:[\s\"'\x60]|$)|[^f\s\"'\x60]|f(?:(?:[\s\"'\x60]|$)|[^t\s\"'\x60]|t(?:[^\s\"'\x60]))))|c(?:(?:[\s\"'\x60]|$)|[^el\s\"'\x60]|e(?:(?:[\s\"'\x60]|$)|[^n\s\"'\x60]|n(?:(?:[\s\"'\x60]|$)|[^t\s\"'\x60]|t(?:(?:[\s\"'\x60]|$)|[^e\s\"'\x60]|e(?:(?:[\s\"'\x60]|$)|[^r\s\"'\x60]|r(?:[^\s\"'\x60])))))|l(?:(?:[\s\"'\x60]|$)|[^i\s\"'\x60]|i(?:(?:[\s\"'\x60]|$)|[^p\s\"'\x60]|p(?:[^\s\"'\x60]))))|r(?:(?:[\s\"'\x60]|$)|[^i\s\"'\x60]|i(?:(?:[\s\"'\x60]|$)|[^g\s\"'\x60]|g(?:(?:[\s\"'\x60]|$)|[^h\s\"'\x60]|h(?:(?:[\s\"'\x60]|$)|[^t\s\"'\x60]|t(?:[^\s\"'\x60])))))|j(?:(?:[\s\"'\x60]|$)|[^u\s\"'\x60]|u(?:(?:[\s\"'\x60]|$)|[^s\s\"'\x60]|s(?:(?:[\s\"'\x60]|$)|[^t\s\"'\x60]|t(?:(?:[\s\"'\x60]|$)|[^i\s\"'\x60]|i(?:(?:[\s\"'\x60]|$)|[^f\s\"'\x60]|f(?:(?:[\s\"'\x60]|$)|[^y\s\"'\x60]|y(?:[^\s\"'\x60])))))))|s(?:(?:[\s\"'\x60]|$)|[^t\s\"'\x60]|t(?:(?:[\s\"'\x60]|$)|[^a\s\"'\x60]|a(?:(?:[\s\"'\x60]|$)|[^r\s\"'\x60]|r(?:(?:[\s\"'\x60]|$)|[^t\s\"'\x60]|t(?:[^\s\"'\x60])))))|e(?:(?:[\s\"'\x60]|$)|[^nl\s\"'\x60]|n(?:(?:[\s\"'\x60]|$)|[^d\s\"'\x60]|d(?:[^\s\"'\x60]))|l(?:(?:[\s\"'\x60]|$)|[^l\s\"'\x60]|l(?:(?:[\s\"'\x60]|$)|[^i\s\"'\x60]|i(?:(?:[\s\"'\x60]|$)|[^p\s\"'\x60]|p(?:(?:[\s\"'\x60]|$)|[^s\s\"'\x60]|s(?:(?:[\s\"'\x60]|$)|[^i\s\"'\x60]|i(?:(?:[\s\"'\x60]|$)|[^s\s\"'\x60]|s(?:[^\s\"'\x60]))))))))|w(?:(?:[\s\"'\x60]|$)|[^r\s\"'\x60]|r(?:(?:[\s\"'\x60]|$)|[^a\s\"'\x60]|a(?:(?:[\s\"'\x60]|$)|[^p\s\"'\x60]|p(?:[^\s\"'\x60]))))|n(?:(?:[\s\"'\x60]|$)|[^o\s\"'\x60]|o(?:(?:[\s\"'\x60]|$)|[^w\s\"'\x60]|w(?:(?:[\s\"'\x60]|$)|[^r\s\"'\x60]|r(?:(?:[\s\"'\x60]|$)|[^a\s\"'\x60]|a(?:(?:[\s\"'\x60]|$)|[^p\s\"'\x60]|p(?:[^\s\"'\x60]))))))|b(?:(?:[\s\"'\x60]|$)|[^a\s\"'\x60]|a(?:(?:[\s\"'\x60]|$)|[^l\s\"'\x60]|l(?:(?:[\s\"'\x60]|$)|[^a\s\"'\x60]|a(?:(?:[\s\"'\x60]|$)|[^n\s\"'\x60]|n(?:(?:[\s\"'\x60]|$)|[^c\s\"'\x60]|c(?:(?:[\s\"'\x60]|$)|[^e\s\"'\x60]|e(?:[^\s\"'\x60])))))))|p(?:(?:[\s\"'\x60]|$)|[^r\s\"'\x60]|r(?:(?:[\s\"'\x60]|$)|[^e\s\"'\x60]|e(?:(?:[\s\"'\x60]|$)|[^t\s\"'\x60]|t(?:(?:[\s\"'\x60]|$)|[^t\s\"'\x60]|t(?:(?:[\s\"'\x60]|$)|[^y\s\"'\x60]|y(?:[^\s\"'\x60])))))))|font-|leading-|tracking-|border|divide-|rounded|shadow|inset-shadow|drop-shadow|ring\b|ring-|inset-ring|outline|opacity-|backdrop-|fill-|stroke-|caret-|accent-|scheme-|mask-|decoration-|underline|no-underline|overline|line-through|italic|not-italic|uppercase|lowercase|capitalize|normal-case|antialiased|subpixel-antialiased|ordinal|slashed-zero|(?:normal|lining|oldstyle|proportional|tabular)-nums|(?:diagonal|stacked)-fractions|blur(?:-|(?:[\s\"'\x60]|$))|brightness-|contrast-[\d\[]|grayscale|hue-rotate-|invert|saturate-|sepia|(?:hover|focus|focus-visible|focus-within|active|disabled|enabled|open|checked|indeterminate|visited|target|invalid|valid|user-invalid|user-valid|required|optional|read-only|autofill|placeholder-shown|in-range|out-of-range|inert|starting|dark|placeholder|selection|marker|file|first-letter|first-line|backdrop|details-content|aria-[^:\s]*|data-[^:\s]*|group-[^:\s]*|peer-[^:\s]*|has-[^:\s]*|\*|\[&[^\s]*?\]):)|(?:^|[^\w-])\[(?:padding|background|color|font|line-height|letter-spacing|border|outline|box-shadow|text-shadow|text-decoration|text-underline|text-transform|opacity|filter|backdrop-filter|fill|stroke|caret-color|accent-color|mask|-webkit-mask|-webkit-font-smoothing|-moz-osx-font-smoothing)(?:-[\w-]*)?:).*" },
@@ -71,6 +72,7 @@ or {
71
72
  },
72
73
  not and {
73
74
  $plainName <: r"([A-Z]\w*)(?:\.[\w.]+)?"($plainComponent),
75
+ not $plainComponent <: r"(?:RevealScope)",
74
76
  $module <: contains JsImport() as $importPlain where {
75
77
  $importPlain <: r"(?s).*(?:from\s+['\"](?:(?:@ctrl-ui/react|[^'\"]*components/control-ui)/)).*",
76
78
  $importPlain <: contains JsIdentifierBinding() as $bindingPlain where { $bindingPlain <: $plainComponent }
@@ -30,6 +30,7 @@ or {
30
30
  }
31
31
  } where {
32
32
  $name <: r"([A-Z]\w*)(?:\.[\w.]+)?"($component),
33
+ not $component <: r"(?:RevealScope)",
33
34
  $attr <: within JsModule() as $module,
34
35
  $attr <: or {
35
36
  contains or { JsPropertyObjectMember(name=$attrKey), JsShorthandPropertyObjectMember(name=$attrKey) } where { $attrKey <: r"[\"']?(?:(?:padding|background|color|font|lineHeight|letterSpacing|border|outline|boxShadow|textShadow|textDecoration|textUnderline|textTransform|opacity|filter|backdropFilter|fill|stroke|caretColor|accentColor|mask|WebkitMask|WebkitFontSmoothing|MozOsxFontSmoothing)(?:[A-Z]\w*)?|(?:padding|background|color|font|line-height|letter-spacing|border|outline|box-shadow|text-shadow|text-decoration|text-underline|text-transform|opacity|filter|backdrop-filter|fill|stroke|caret-color|accent-color|mask|-webkit-mask|-webkit-font-smoothing|-moz-osx-font-smoothing)(?:-[\w-]*)?)[\"']?" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctrl-ui/react",
3
- "version": "0.9.15",
3
+ "version": "0.9.17",
4
4
  "description": "100+ React components for Tailwind CSS and shadcn/ui: pickers, trees, editors, canvases, app templates, and AI chat surfaces, with neutral defaults and consumer-owned themes.",
5
5
  "keywords": [
6
6
  "react",