panelui-web 0.10.0 → 0.11.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.
Files changed (99) hide show
  1. package/dist/components/alert/index.d.ts +15 -8
  2. package/dist/components/alert/index.js +65 -18
  3. package/dist/components/alert-dialog/index.d.ts +9 -3
  4. package/dist/components/alert-dialog/index.js +23 -7
  5. package/dist/components/avatar/index.d.ts +9 -3
  6. package/dist/components/avatar/index.js +34 -10
  7. package/dist/components/badge/index.d.ts +57 -0
  8. package/dist/components/badge/index.js +61 -0
  9. package/dist/components/bar-picker/index.d.ts +8 -1
  10. package/dist/components/bar-picker/index.js +35 -26
  11. package/dist/components/button/index.d.ts +68 -46
  12. package/dist/components/button/index.js +38 -2
  13. package/dist/components/button-group/index.d.ts +11 -5
  14. package/dist/components/button-group/index.js +31 -7
  15. package/dist/components/canvas/index.d.ts +9 -3
  16. package/dist/components/canvas/index.js +53 -25
  17. package/dist/components/card/index.d.ts +13 -7
  18. package/dist/components/card/index.js +45 -17
  19. package/dist/components/checkbox/index.d.ts +73 -0
  20. package/dist/components/checkbox/index.js +100 -0
  21. package/dist/components/chip/index.d.ts +79 -0
  22. package/dist/components/chip/index.js +112 -0
  23. package/dist/components/choice-cards/index.d.ts +11 -5
  24. package/dist/components/choice-cards/index.js +25 -6
  25. package/dist/components/command-dock/index.d.ts +16 -9
  26. package/dist/components/command-dock/index.js +76 -19
  27. package/dist/components/day-grid/index.d.ts +12 -6
  28. package/dist/components/day-grid/index.js +71 -27
  29. package/dist/components/dialog/index.d.ts +19 -12
  30. package/dist/components/dialog/index.js +84 -25
  31. package/dist/components/feedback/index.d.ts +20 -13
  32. package/dist/components/feedback/index.js +83 -27
  33. package/dist/components/field/index.d.ts +85 -0
  34. package/dist/components/field/index.js +90 -0
  35. package/dist/components/flow/index.d.ts +21 -14
  36. package/dist/components/flow/index.js +56 -20
  37. package/dist/components/frame/index.d.ts +17 -11
  38. package/dist/components/frame/index.js +73 -22
  39. package/dist/components/input/index.d.ts +9 -24
  40. package/dist/components/input/index.js +53 -6
  41. package/dist/components/inspector/index.d.ts +69 -63
  42. package/dist/components/inspector/index.js +31 -9
  43. package/dist/components/kpi/index.d.ts +20 -14
  44. package/dist/components/kpi/index.js +111 -36
  45. package/dist/components/label/index.d.ts +10 -4
  46. package/dist/components/label/index.js +39 -9
  47. package/dist/components/lane-board/index.d.ts +15 -9
  48. package/dist/components/lane-board/index.js +97 -57
  49. package/dist/components/menu/index.d.ts +10 -4
  50. package/dist/components/menu/index.js +28 -10
  51. package/dist/components/message/index.d.ts +15 -9
  52. package/dist/components/message/index.js +71 -19
  53. package/dist/components/not-found/index.d.ts +10 -4
  54. package/dist/components/not-found/index.js +48 -12
  55. package/dist/components/planet/index.d.ts +11 -5
  56. package/dist/components/planet/index.js +73 -20
  57. package/dist/components/prompt/index.d.ts +30 -23
  58. package/dist/components/prompt/index.js +148 -52
  59. package/dist/components/radio-group/index.d.ts +124 -0
  60. package/dist/components/radio-group/index.js +216 -0
  61. package/dist/components/range-meter/index.d.ts +9 -3
  62. package/dist/components/range-meter/index.js +61 -27
  63. package/dist/components/rolling-number/index.d.ts +8 -1
  64. package/dist/components/rolling-number/index.js +15 -6
  65. package/dist/components/select/index.d.ts +7 -1
  66. package/dist/components/select/index.js +13 -4
  67. package/dist/components/sheet/index.d.ts +8 -2
  68. package/dist/components/sheet/index.js +27 -15
  69. package/dist/components/slider/index.d.ts +11 -4
  70. package/dist/components/slider/index.js +30 -10
  71. package/dist/components/soundwave/index.d.ts +8 -2
  72. package/dist/components/soundwave/index.js +26 -6
  73. package/dist/components/stat/index.d.ts +13 -7
  74. package/dist/components/stat/index.js +45 -15
  75. package/dist/components/table/index.d.ts +128 -0
  76. package/dist/components/table/index.js +145 -0
  77. package/dist/components/tabs/index.d.ts +7 -1
  78. package/dist/components/tabs/index.js +8 -2
  79. package/dist/components/textarea/index.d.ts +0 -23
  80. package/dist/components/textarea/index.js +22 -5
  81. package/dist/components/tilt-card/index.d.ts +9 -3
  82. package/dist/components/tilt-card/index.js +28 -8
  83. package/dist/components/timeline/index.d.ts +20 -13
  84. package/dist/components/timeline/index.js +166 -59
  85. package/dist/components/toast/dither-text.d.ts +13 -0
  86. package/dist/components/toast/dither-text.js +143 -0
  87. package/dist/components/toast/dither.d.ts +29 -0
  88. package/dist/components/toast/dither.js +153 -0
  89. package/dist/components/toast/index.d.ts +108 -0
  90. package/dist/components/toast/index.js +225 -0
  91. package/dist/components/toolbar/index.d.ts +9 -3
  92. package/dist/components/toolbar/index.js +23 -6
  93. package/dist/components/workbench/index.d.ts +50 -43
  94. package/dist/components/workbench/index.js +77 -20
  95. package/dist/components/xy-pad/index.d.ts +9 -3
  96. package/dist/components/xy-pad/index.js +13 -2
  97. package/dist/index.d.ts +7 -0
  98. package/dist/index.js +7 -0
  99. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  /**
4
4
  * BarPicker — a short run of values as columns of lights, one column picked.
5
5
  *
@@ -27,8 +27,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
27
27
  * yours to show beside it, large: a column of eight lights cannot be read to
28
28
  * the unit, but one number can.
29
29
  */
30
- import { useEffect, useRef, useState, } from 'react';
30
+ import { useEffect, useRef, useState } from 'react';
31
31
  import { tv } from 'tailwind-variants';
32
+ import { mergeProps } from '@base-ui/react/merge-props';
33
+ import { useRender } from '@base-ui/react/use-render';
32
34
  const barPickerVariants = tv({
33
35
  slots: {
34
36
  root: 'flex w-full items-end gap-1.5',
@@ -68,7 +70,7 @@ const barPickerVariants = tv({
68
70
  });
69
71
  /** Milliseconds between one light of a column coming on and the next. */
70
72
  const STAGGER = 32;
71
- export function BarPicker({ data, max, value, defaultValue = 0, onValueChange, segments = 8, color = 'primary', formatValue = (bar) => String(bar), label = 'Values', className, ...props }) {
73
+ export function BarPicker({ data, max, value, defaultValue = 0, onValueChange, segments = 8, color = 'primary', formatValue = (bar) => String(bar), label = 'Values', className, render, ...props }) {
72
74
  const slots = barPickerVariants({ color });
73
75
  const [internal, setInternal] = useState(defaultValue);
74
76
  const picked = clampIndex(value ?? internal, data.length);
@@ -107,29 +109,36 @@ export function BarPicker({ data, max, value, defaultValue = 0, onValueChange, s
107
109
  refs.current[next]?.focus();
108
110
  pick(next);
109
111
  };
110
- return (_jsx("div", { role: "radiogroup", "aria-label": label, className: slots.root({ className }), ...props, children: data.map((bar, index) => {
111
- const selected = index === picked;
112
- const part = barPickerVariants({ selected });
113
- const name = bar.name ?? (typeof bar.label === 'string' ? bar.label : `Bar ${index + 1}`);
114
- // How many lights the value reaches. Any value above zero lights at
115
- // least one, so a small value is a mark and not an empty column.
116
- const share = Math.min(1, Math.max(0, bar.value / ceiling));
117
- const reached = !lit || bar.value <= 0 ? 0 : Math.max(1, Math.round(share * count));
118
- return (_jsxs("button", { ref: (node) => {
119
- refs.current[index] = node;
120
- }, type: "button", role: "radio", "aria-checked": selected, "aria-label": `${name}: ${formatValue(bar.value)}`, tabIndex: selected ? 0 : -1, onClick: () => pick(index), onKeyDown: (event) => onKeyDown(event, index), className: part.column(), children: [_jsx("span", { className: slots.stack(), children: Array.from({ length: count }, (_, level) => {
121
- const on = level < reached;
122
- const light = !on ? 'off' : !selected ? 'dim' : level === reached - 1 ? 'peak' : 'on';
123
- return (_jsx("span", { className: barPickerVariants({ light }).segment(), style: {
124
- // Bottom first, so a column is seen filling upwards.
125
- transitionDelay: on ? `${level * STAGGER}ms` : undefined,
126
- // The picked column brightens towards its top.
127
- opacity: light === 'on'
128
- ? Number((0.5 + 0.5 * ((level + 1) / reached)).toFixed(3))
129
- : undefined,
130
- } }, level));
131
- }) }), _jsx("span", { "aria-hidden": "true", className: part.label(), children: bar.label })] }, index));
132
- }) }));
112
+ return useRender({
113
+ defaultTagName: 'div',
114
+ render,
115
+ props: mergeProps({
116
+ role: 'radiogroup',
117
+ 'aria-label': label,
118
+ className: slots.root({ className }),
119
+ children: (_jsx(_Fragment, { children: data.map((bar, index) => {
120
+ const selected = index === picked;
121
+ const part = barPickerVariants({ selected });
122
+ const name = bar.name ?? (typeof bar.label === 'string' ? bar.label : `Bar ${index + 1}`);
123
+ // How many lights the value reaches. Any value above zero lights at
124
+ // least one, so a small value is a mark and not an empty column.
125
+ const share = Math.min(1, Math.max(0, bar.value / ceiling));
126
+ const reached = !lit || bar.value <= 0 ? 0 : Math.max(1, Math.round(share * count));
127
+ return (_jsxs("button", { ref: (node) => {
128
+ refs.current[index] = node;
129
+ }, type: "button", role: "radio", "aria-checked": selected, "aria-label": `${name}: ${formatValue(bar.value)}`, tabIndex: selected ? 0 : -1, onClick: () => pick(index), onKeyDown: (event) => onKeyDown(event, index), className: part.column(), children: [_jsx("span", { className: slots.stack(), children: Array.from({ length: count }, (_, level) => {
130
+ const on = level < reached;
131
+ const light = !on ? 'off' : !selected ? 'dim' : level === reached - 1 ? 'peak' : 'on';
132
+ return (_jsx("span", { className: barPickerVariants({ light }).segment(), style: {
133
+ // Bottom first, so a column is seen filling upwards.
134
+ transitionDelay: on ? `${level * STAGGER}ms` : undefined,
135
+ // The picked column brightens towards its top.
136
+ opacity: light === 'on' ? Number((0.5 + 0.5 * ((level + 1) / reached)).toFixed(3)) : undefined,
137
+ } }, level));
138
+ }) }), _jsx("span", { "aria-hidden": "true", className: part.label(), children: bar.label })] }, index));
139
+ }) })),
140
+ }, props),
141
+ });
133
142
  }
134
143
  BarPicker.displayName = 'BarPicker';
135
144
  function clampIndex(index, length) {
@@ -20,76 +20,86 @@
20
20
  * `loading` sets a spinner over the label and stops presses. The label stays
21
21
  * in place, hidden, so the width does not change: a button that resizes as it
22
22
  * is pressed moves whatever sits beside it.
23
+ *
24
+ * ## A link that looks like a button
25
+ *
26
+ * An action that goes to another page is a link, whatever it looks like, so
27
+ * that it opens in a new tab and reads as a link to a screen reader. Pass the
28
+ * link as `render` and the button's look and behaviour are put on it:
29
+ * `<Button render={<a href="/" />}>Home</Button>`. The button is the headless
30
+ * button primitive underneath, so keyboard handling, `disabled` and focus work
31
+ * the same whatever it is drawn as.
23
32
  */
24
- import type { ComponentProps } from 'react';
33
+ import { Button as BaseButton } from '@base-ui/react/button';
34
+ import { type ComponentProps } from 'react';
25
35
  import { type VariantProps } from 'tailwind-variants';
26
36
  export declare const buttonVariants: import("tailwind-variants").TVReturnType<{
27
37
  variant: {
28
- default: string;
29
- secondary: string;
30
- outline: string;
31
- ghost: string;
32
- destructive: string;
33
- link: string;
38
+ default: "bg-primary text-primary-foreground shadow-xs inset-shadow-[0_1px_0_rgb(255_255_255/0.12)] hover:not-disabled:bg-primary/90";
39
+ secondary: "bg-secondary text-secondary-foreground hover:not-disabled:bg-secondary/80";
40
+ outline: "border bg-background text-foreground shadow-xs hover:not-disabled:bg-accent hover:not-disabled:text-accent-foreground dark:bg-input/30";
41
+ ghost: "text-foreground hover:not-disabled:bg-accent hover:not-disabled:text-accent-foreground";
42
+ destructive: "bg-destructive text-white shadow-xs hover:not-disabled:bg-destructive/90 focus-visible:ring-destructive/40";
43
+ link: "text-foreground underline-offset-4 hover:underline";
34
44
  };
35
45
  size: {
36
- sm: string;
37
- md: string;
38
- lg: string;
39
- 'icon-sm': string;
40
- icon: string;
41
- 'icon-lg': string;
46
+ sm: "h-8 rounded-md px-3 text-[0.8125rem]";
47
+ md: "h-9 rounded-lg px-4 text-sm";
48
+ lg: "h-11 rounded-xl px-5 text-[0.9375rem]";
49
+ 'icon-sm': "size-8 rounded-md";
50
+ icon: "size-9 rounded-lg";
51
+ 'icon-lg': "size-11 rounded-xl [&_svg:not([class*=size-])]:size-5";
42
52
  };
43
53
  /** `pill` rounds the ends fully, whatever the size. */
44
54
  shape: {
45
- default: string;
46
- pill: string;
55
+ default: "";
56
+ pill: "rounded-full";
47
57
  };
48
58
  }, undefined, string[], {
49
59
  variant: {
50
- default: string;
51
- secondary: string;
52
- outline: string;
53
- ghost: string;
54
- destructive: string;
55
- link: string;
60
+ default: "bg-primary text-primary-foreground shadow-xs inset-shadow-[0_1px_0_rgb(255_255_255/0.12)] hover:not-disabled:bg-primary/90";
61
+ secondary: "bg-secondary text-secondary-foreground hover:not-disabled:bg-secondary/80";
62
+ outline: "border bg-background text-foreground shadow-xs hover:not-disabled:bg-accent hover:not-disabled:text-accent-foreground dark:bg-input/30";
63
+ ghost: "text-foreground hover:not-disabled:bg-accent hover:not-disabled:text-accent-foreground";
64
+ destructive: "bg-destructive text-white shadow-xs hover:not-disabled:bg-destructive/90 focus-visible:ring-destructive/40";
65
+ link: "text-foreground underline-offset-4 hover:underline";
56
66
  };
57
67
  size: {
58
- sm: string;
59
- md: string;
60
- lg: string;
61
- 'icon-sm': string;
62
- icon: string;
63
- 'icon-lg': string;
68
+ sm: "h-8 rounded-md px-3 text-[0.8125rem]";
69
+ md: "h-9 rounded-lg px-4 text-sm";
70
+ lg: "h-11 rounded-xl px-5 text-[0.9375rem]";
71
+ 'icon-sm': "size-8 rounded-md";
72
+ icon: "size-9 rounded-lg";
73
+ 'icon-lg': "size-11 rounded-xl [&_svg:not([class*=size-])]:size-5";
64
74
  };
65
75
  /** `pill` rounds the ends fully, whatever the size. */
66
76
  shape: {
67
- default: string;
68
- pill: string;
77
+ default: "";
78
+ pill: "rounded-full";
69
79
  };
70
- }, undefined, import("tailwind-variants").TVReturnType<{
80
+ }, undefined, import("tailwind-variants").TVReturnTypeLike<{
71
81
  variant: {
72
- default: string;
73
- secondary: string;
74
- outline: string;
75
- ghost: string;
76
- destructive: string;
77
- link: string;
82
+ default: "bg-primary text-primary-foreground shadow-xs inset-shadow-[0_1px_0_rgb(255_255_255/0.12)] hover:not-disabled:bg-primary/90";
83
+ secondary: "bg-secondary text-secondary-foreground hover:not-disabled:bg-secondary/80";
84
+ outline: "border bg-background text-foreground shadow-xs hover:not-disabled:bg-accent hover:not-disabled:text-accent-foreground dark:bg-input/30";
85
+ ghost: "text-foreground hover:not-disabled:bg-accent hover:not-disabled:text-accent-foreground";
86
+ destructive: "bg-destructive text-white shadow-xs hover:not-disabled:bg-destructive/90 focus-visible:ring-destructive/40";
87
+ link: "text-foreground underline-offset-4 hover:underline";
78
88
  };
79
89
  size: {
80
- sm: string;
81
- md: string;
82
- lg: string;
83
- 'icon-sm': string;
84
- icon: string;
85
- 'icon-lg': string;
90
+ sm: "h-8 rounded-md px-3 text-[0.8125rem]";
91
+ md: "h-9 rounded-lg px-4 text-sm";
92
+ lg: "h-11 rounded-xl px-5 text-[0.9375rem]";
93
+ 'icon-sm': "size-8 rounded-md";
94
+ icon: "size-9 rounded-lg";
95
+ 'icon-lg': "size-11 rounded-xl [&_svg:not([class*=size-])]:size-5";
86
96
  };
87
97
  /** `pill` rounds the ends fully, whatever the size. */
88
98
  shape: {
89
- default: string;
90
- pill: string;
99
+ default: "";
100
+ pill: "rounded-full";
91
101
  };
92
- }, undefined, string[], unknown, unknown, undefined>>;
102
+ }, undefined>>;
93
103
  export type ButtonVariant = NonNullable<VariantProps<typeof buttonVariants>['variant']>;
94
104
  export type ButtonSize = NonNullable<VariantProps<typeof buttonVariants>['size']>;
95
105
  export interface ButtonProps extends ComponentProps<'button'> {
@@ -101,9 +111,21 @@ export interface ButtonProps extends ComponentProps<'button'> {
101
111
  shape?: 'default' | 'pill';
102
112
  /** Shows a spinner over the label, sets `aria-busy` and stops presses, keeping the width. */
103
113
  loading?: boolean;
114
+ /**
115
+ * An element to draw the button as, such as `<a href="/" />` or a router's
116
+ * link, or a function of the props and state that returns one. It takes the
117
+ * button's classes, children, ref and event handlers. A link stays a link: it
118
+ * is not given `type`, and Space does not press it.
119
+ */
120
+ render?: BaseButton.Props['render'];
121
+ /**
122
+ * Whether `render` draws a native `<button>`. Set automatically for an
123
+ * element; pass it when `render` is a function that returns something else.
124
+ */
125
+ nativeButton?: boolean;
104
126
  }
105
127
  /** A button. Every attribute of `<button>` passes through. */
106
- declare function ButtonRoot({ variant, size, shape, loading, disabled, type, className, children, ...props }: ButtonProps): import("react").JSX.Element;
128
+ declare function ButtonRoot({ variant, size, shape, loading, disabled, type, className, children, render, nativeButton, ...props }: ButtonProps): import("react").JSX.Element;
107
129
  declare namespace ButtonRoot {
108
130
  var displayName: string;
109
131
  }
@@ -1,4 +1,38 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Button — something to press, in the weight its action deserves.
4
+ *
5
+ * ```tsx
6
+ * <Button>Save</Button>
7
+ * <Button variant="outline" size="sm">Cancel</Button>
8
+ * <Button variant="ghost" size="icon" aria-label="Close"><XIcon /></Button>
9
+ * <Button loading>Saving</Button>
10
+ * ```
11
+ *
12
+ * ## Weight
13
+ *
14
+ * `default` is the one action a view is for, and there should be one of it.
15
+ * `secondary` and `outline` are the actions beside it, `ghost` the ones that
16
+ * should not draw the eye until they are looked for, and `destructive` the
17
+ * one that cannot be taken back.
18
+ *
19
+ * ## Loading
20
+ *
21
+ * `loading` sets a spinner over the label and stops presses. The label stays
22
+ * in place, hidden, so the width does not change: a button that resizes as it
23
+ * is pressed moves whatever sits beside it.
24
+ *
25
+ * ## A link that looks like a button
26
+ *
27
+ * An action that goes to another page is a link, whatever it looks like, so
28
+ * that it opens in a new tab and reads as a link to a screen reader. Pass the
29
+ * link as `render` and the button's look and behaviour are put on it:
30
+ * `<Button render={<a href="/" />}>Home</Button>`. The button is the headless
31
+ * button primitive underneath, so keyboard handling, `disabled` and focus work
32
+ * the same whatever it is drawn as.
33
+ */
34
+ import { Button as BaseButton } from '@base-ui/react/button';
35
+ import { isValidElement } from 'react';
2
36
  import { tv } from 'tailwind-variants';
3
37
  export const buttonVariants = tv({
4
38
  base: [
@@ -38,8 +72,10 @@ function Spinner() {
38
72
  return (_jsx("svg", { "aria-hidden": "true", "data-slot": "button-spinner", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", className: "absolute inset-0 m-auto animate-spin motion-reduce:animate-none", children: _jsx("path", { d: "M21 12a9 9 0 1 1-6.22-8.56" }) }));
39
73
  }
40
74
  /** A button. Every attribute of `<button>` passes through. */
41
- function ButtonRoot({ variant, size, shape, loading = false, disabled, type = 'button', className, children, ...props }) {
42
- return (_jsxs("button", { type: type, "data-slot": "button", "data-variant": variant ?? 'default', "aria-busy": loading || undefined, disabled: disabled || loading, className: buttonVariants({ variant, size, shape, className }), ...props, children: [loading ? _jsx(Spinner, {}) : null, _jsx("span", { className: loading ? 'invisible contents' : 'contents', children: children })] }));
75
+ function ButtonRoot({ variant, size, shape, loading = false, disabled, type = 'button', className, children, render, nativeButton, ...props }) {
76
+ // An element to render tells whether it is a native button; a function cannot, so it says.
77
+ const native = nativeButton ?? (render === undefined || (isValidElement(render) && render.type === 'button'));
78
+ return (_jsxs(BaseButton, { type: native ? type : undefined, "data-slot": "button", "data-variant": variant ?? 'default', "aria-busy": loading || undefined, disabled: disabled || loading, focusableWhenDisabled: loading, nativeButton: native, render: render, className: buttonVariants({ variant, size, shape, className }), ...props, children: [loading ? _jsx(Spinner, {}) : null, _jsx("span", { className: loading ? 'invisible contents' : 'contents', children: children })] }));
43
79
  }
44
80
  ButtonRoot.displayName = 'Button';
45
81
  export const Button = ButtonRoot;
@@ -35,6 +35,12 @@
35
35
  */
36
36
  import type { ComponentProps } from 'react';
37
37
  import { type VariantProps } from 'tailwind-variants';
38
+ import { useRender } from '@base-ui/react/use-render';
39
+ import type { ElementType as RenderElementType } from 'react';
40
+ /** Draws a part as another element, or composes it with a component: `render={<a href="/" />}`. */
41
+ type RenderProp<Tag extends RenderElementType> = {
42
+ render?: useRender.ComponentProps<Tag>['render'];
43
+ };
38
44
  export declare const buttonGroupVariants: import("tailwind-variants").TVReturnType<{
39
45
  orientation: {
40
46
  horizontal: {
@@ -89,7 +95,7 @@ export declare const buttonGroupVariants: import("tailwind-variants").TVReturnTy
89
95
  root: string[];
90
96
  separator: string[];
91
97
  text: string[];
92
- }, import("tailwind-variants").TVReturnType<{
98
+ }, import("tailwind-variants").TVReturnTypeLike<{
93
99
  orientation: {
94
100
  horizontal: {
95
101
  root: string[];
@@ -116,7 +122,7 @@ export declare const buttonGroupVariants: import("tailwind-variants").TVReturnTy
116
122
  root: string[];
117
123
  separator: string[];
118
124
  text: string[];
119
- }, undefined, unknown, unknown, undefined>>;
125
+ }>>;
120
126
  export type ButtonGroupOrientation = NonNullable<VariantProps<typeof buttonGroupVariants>['orientation']>;
121
127
  export type ButtonGroupTextSize = NonNullable<VariantProps<typeof buttonGroupVariants>['size']>;
122
128
  export interface ButtonGroupProps extends ComponentProps<'div'> {
@@ -127,7 +133,7 @@ export interface ButtonGroupProps extends ComponentProps<'div'> {
127
133
  * The group. It is a `role="group"`, so give it an `aria-label` that names
128
134
  * what its actions have in common.
129
135
  */
130
- declare function ButtonGroupRoot({ orientation, className, ...props }: ButtonGroupProps): import("react").JSX.Element;
136
+ declare function ButtonGroupRoot({ orientation, className, render, ...props }: ButtonGroupProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
131
137
  declare namespace ButtonGroupRoot {
132
138
  var displayName: string;
133
139
  }
@@ -145,7 +151,7 @@ export interface ButtonGroupSeparatorProps extends ComponentProps<'span'> {
145
151
  * one, and draws a short line in the button's text colour. It is decorative
146
152
  * and hidden from assistive technology.
147
153
  */
148
- declare function ButtonGroupSeparator({ orientation, className, ...props }: ButtonGroupSeparatorProps): import("react").JSX.Element;
154
+ declare function ButtonGroupSeparator({ orientation, className, render, ...props }: ButtonGroupSeparatorProps & RenderProp<'span'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
149
155
  declare namespace ButtonGroupSeparator {
150
156
  var displayName: string;
151
157
  }
@@ -157,7 +163,7 @@ export interface ButtonGroupTextProps extends ComponentProps<'div'> {
157
163
  * A cell that is read rather than pressed: a prefix such as `https://`, a unit,
158
164
  * or the value a pair of buttons changes.
159
165
  */
160
- declare function ButtonGroupText({ size, className, ...props }: ButtonGroupTextProps): import("react").JSX.Element;
166
+ declare function ButtonGroupText({ size, className, render, ...props }: ButtonGroupTextProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
161
167
  declare namespace ButtonGroupText {
162
168
  var displayName: string;
163
169
  }
@@ -1,5 +1,7 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ 'use client';
2
2
  import { tv } from 'tailwind-variants';
3
+ import { mergeProps } from '@base-ui/react/merge-props';
4
+ import { useRender } from '@base-ui/react/use-render';
3
5
  /*
4
6
  * Which of the three a group is depends on what it holds, so the rules below
5
7
  * are scoped by selector rather than by prop:
@@ -83,8 +85,17 @@ export const buttonGroupVariants = tv({
83
85
  * The group. It is a `role="group"`, so give it an `aria-label` that names
84
86
  * what its actions have in common.
85
87
  */
86
- function ButtonGroupRoot({ orientation = 'horizontal', className, ...props }) {
87
- return (_jsx("div", { role: "group", "data-slot": "button-group", "data-orientation": orientation, className: buttonGroupVariants({ orientation }).root({ className }), ...props }));
88
+ function ButtonGroupRoot({ orientation = 'horizontal', className, render, ...props }) {
89
+ return useRender({
90
+ defaultTagName: 'div',
91
+ render,
92
+ props: mergeProps({
93
+ role: 'group',
94
+ 'data-slot': 'button-group',
95
+ 'data-orientation': orientation,
96
+ className: buttonGroupVariants({ orientation }).root({ className }),
97
+ }, props),
98
+ });
88
99
  }
89
100
  ButtonGroupRoot.displayName = 'ButtonGroup';
90
101
  /**
@@ -94,19 +105,32 @@ ButtonGroupRoot.displayName = 'ButtonGroup';
94
105
  * one, and draws a short line in the button's text colour. It is decorative
95
106
  * and hidden from assistive technology.
96
107
  */
97
- function ButtonGroupSeparator({ orientation = 'vertical', className, ...props }) {
108
+ function ButtonGroupSeparator({ orientation = 'vertical', className, render, ...props }) {
98
109
  const slots = buttonGroupVariants({
99
110
  orientation: orientation === 'vertical' ? 'horizontal' : 'vertical',
100
111
  });
101
- return (_jsx("span", { "aria-hidden": "true", "data-slot": "button-group-separator", "data-orientation": orientation, className: slots.separator({ className }), ...props }));
112
+ return useRender({
113
+ defaultTagName: 'span',
114
+ render,
115
+ props: mergeProps({
116
+ 'aria-hidden': 'true',
117
+ 'data-slot': 'button-group-separator',
118
+ 'data-orientation': orientation,
119
+ className: slots.separator({ className }),
120
+ }, props),
121
+ });
102
122
  }
103
123
  ButtonGroupSeparator.displayName = 'ButtonGroup.Separator';
104
124
  /**
105
125
  * A cell that is read rather than pressed: a prefix such as `https://`, a unit,
106
126
  * or the value a pair of buttons changes.
107
127
  */
108
- function ButtonGroupText({ size, className, ...props }) {
109
- return (_jsx("div", { "data-slot": "button-group-text", className: buttonGroupVariants({ size }).text({ className }), ...props }));
128
+ function ButtonGroupText({ size, className, render, ...props }) {
129
+ return useRender({
130
+ defaultTagName: 'div',
131
+ render,
132
+ props: mergeProps({ 'data-slot': 'button-group-text', className: buttonGroupVariants({ size }).text({ className }) }, props),
133
+ });
110
134
  }
111
135
  ButtonGroupText.displayName = 'ButtonGroup.Text';
112
136
  export const ButtonGroup = Object.assign(ButtonGroupRoot, {
@@ -35,6 +35,12 @@
35
35
  * finger is down.
36
36
  */
37
37
  import { type ComponentProps } from 'react';
38
+ import { useRender } from '@base-ui/react/use-render';
39
+ import type { ElementType as RenderElementType } from 'react';
40
+ /** Draws a part as another element, or composes it with a component: `render={<a href="/" />}`. */
41
+ type RenderProp<Tag extends RenderElementType> = {
42
+ render?: useRender.ComponentProps<Tag>['render'];
43
+ };
38
44
  /** Where the canvas is looking: the same shape a Flow reports. */
39
45
  export interface CanvasViewport {
40
46
  x: number;
@@ -75,7 +81,7 @@ export interface CanvasPatternProps extends Omit<ComponentProps<'svg'>, 'childre
75
81
  strength?: string;
76
82
  }
77
83
  /** A pattern across the whole canvas, which pans and scales with its viewport. */
78
- declare function CanvasPattern({ variant, gap, size, major, fade, strength, className, style, ...props }: CanvasPatternProps): import("react").JSX.Element;
84
+ declare function CanvasPattern({ variant, gap, size, major, fade, strength, className, style, render, ...props }: CanvasPatternProps & RenderProp<'svg'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
79
85
  declare namespace CanvasPattern {
80
86
  var displayName: string;
81
87
  }
@@ -95,14 +101,14 @@ export interface CanvasSpotlightProps extends Omit<ComponentProps<'div'>, 'child
95
101
  * Lights the patterns near the pointer: the marks inside the circle brighten
96
102
  * towards the foreground colour, most at the centre.
97
103
  */
98
- declare function CanvasSpotlight({ radius, position, className, style, ...props }: CanvasSpotlightProps): import("react").JSX.Element;
104
+ declare function CanvasSpotlight({ radius, position, className, style, render, ...props }: CanvasSpotlightProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
99
105
  declare namespace CanvasSpotlight {
100
106
  var displayName: string;
101
107
  }
102
108
  export interface CanvasContentProps extends ComponentProps<'div'> {
103
109
  }
104
110
  /** What sits on the canvas, above its patterns and its light. */
105
- declare function CanvasContent({ className, ...props }: CanvasContentProps): import("react").JSX.Element;
111
+ declare function CanvasContent({ className, render, ...props }: CanvasContentProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
106
112
  declare namespace CanvasContent {
107
113
  var displayName: string;
108
114
  }
@@ -38,6 +38,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
38
38
  */
39
39
  import { createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, } from 'react';
40
40
  import { tv } from 'tailwind-variants';
41
+ import { mergeProps } from '@base-ui/react/merge-props';
42
+ import { useRender } from '@base-ui/react/use-render';
41
43
  const canvasVariants = tv({
42
44
  slots: {
43
45
  root: 'relative isolate overflow-hidden bg-background text-foreground',
@@ -94,7 +96,7 @@ function Marks({ id, spec, viewport }) {
94
96
  return (_jsxs(_Fragment, { children: [_jsxs("defs", { children: [_jsx("pattern", { id: `${id}-minor`, width: gap, height: gap, patternUnits: "userSpaceOnUse", patternTransform: transform, children: variant === 'grid' ? (_jsx("path", { d: `M${half} 0V${gap}M0 ${half}H${gap}`, fill: "none", stroke: "currentColor", strokeWidth: size, opacity: major ? 0.6 : 1 })) : (mark(1, half)) }), major ? (_jsx("pattern", { id: `${id}-major`, width: tile, height: tile, patternUnits: "userSpaceOnUse", patternTransform: transform, children: variant === 'grid' ? (_jsx("path", { d: `M${half} 0V${tile}M0 ${half}H${tile}`, fill: "none", stroke: "currentColor", strokeWidth: size * 1.5 })) : (mark(variant === 'dots' ? 1.8 : 1.6, half)) })) : null] }), _jsx("rect", { width: "100%", height: "100%", fill: `url(#${id}-minor)` }), major ? _jsx("rect", { width: "100%", height: "100%", fill: `url(#${id}-major)` }) : null] }));
95
97
  }
96
98
  /** A pattern across the whole canvas, which pans and scales with its viewport. */
97
- function CanvasPattern({ variant = 'dots', gap, size, major, fade = 'none', strength, className, style, ...props }) {
99
+ function CanvasPattern({ variant = 'dots', gap, size, major, fade = 'none', strength, className, style, render, ...props }) {
98
100
  const canvas = useCanvas('Canvas.Pattern');
99
101
  const id = `canvas${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
100
102
  const spec = useMemo(() => ({
@@ -108,19 +110,29 @@ function CanvasPattern({ variant = 'dots', gap, size, major, fade = 'none', stre
108
110
  // Known to the spotlight, which draws a lit copy of every pattern.
109
111
  useLayoutEffect(() => register(id, spec), [register, id, spec]);
110
112
  const mask = FADES[fade];
111
- return (_jsx("svg", { "aria-hidden": "true", "data-slot": "canvas-pattern", className: canvasVariants().layer({ className: [canvasVariants().pattern(), className] }), style: {
112
- opacity: strength ?? (variant === 'grid' ? '10%' : '28%'),
113
- maskImage: mask,
114
- WebkitMaskImage: mask,
115
- ...style,
116
- }, ...props, children: _jsx(Marks, { id: id, spec: spec, viewport: canvas.viewport }) }));
113
+ return useRender({
114
+ defaultTagName: 'svg',
115
+ render,
116
+ props: mergeProps({
117
+ 'aria-hidden': 'true',
118
+ 'data-slot': 'canvas-pattern',
119
+ className: canvasVariants().layer({ className: [canvasVariants().pattern(), className] }),
120
+ style: {
121
+ opacity: strength ?? (variant === 'grid' ? '10%' : '28%'),
122
+ maskImage: mask,
123
+ WebkitMaskImage: mask,
124
+ ...style,
125
+ },
126
+ children: (_jsx(_Fragment, { children: _jsx(Marks, { id: id, spec: spec, viewport: canvas.viewport }) })),
127
+ }, props),
128
+ });
117
129
  }
118
130
  CanvasPattern.displayName = 'Canvas.Pattern';
119
131
  /**
120
132
  * Lights the patterns near the pointer: the marks inside the circle brighten
121
133
  * towards the foreground colour, most at the centre.
122
134
  */
123
- function CanvasSpotlight({ radius = 100, position, className, style, ...props }) {
135
+ function CanvasSpotlight({ radius = 100, position, className, style, render, ...props }) {
124
136
  const canvas = useCanvas('Canvas.Spotlight');
125
137
  const light = useRef(null);
126
138
  const prefix = `spot${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
@@ -180,26 +192,42 @@ function CanvasSpotlight({ radius = 100, position, className, style, ...props })
180
192
  }, [canvas.root, position]);
181
193
  // A round window on the lit copy: opaque at the pointer, clear at the radius.
182
194
  const circle = `radial-gradient(circle var(--canvas-r) at var(--canvas-x, -999px) var(--canvas-y, -999px), #000, transparent)`;
183
- return (_jsx("div", { ref: light, "aria-hidden": "true", "data-slot": "canvas-spotlight", "data-on": position ? '' : undefined, className: canvasVariants().layer({ className: [canvasVariants().spotlight(), className] }), style: {
184
- '--canvas-r': `${radius}px`,
185
- ...(position ? { '--canvas-x': `${position.x}px`, '--canvas-y': `${position.y}px` } : null),
186
- ...style,
187
- }, ...props, children: canvas.patterns.map((pattern) => {
188
- const fade = FADES[pattern.fade];
189
- // The pattern's own fade still applies inside the light.
190
- const mask = fade ? `${circle}, ${fade}` : circle;
191
- return (_jsx("svg", { className: "absolute inset-0 size-full", style: {
192
- maskImage: mask,
193
- WebkitMaskImage: mask,
194
- maskComposite: fade ? 'intersect' : undefined,
195
- WebkitMaskComposite: fade ? 'source-in' : undefined,
196
- }, children: _jsx(Marks, { id: `${prefix}${pattern.key}`, spec: pattern, viewport: canvas.viewport }) }, pattern.key));
197
- }) }));
195
+ return useRender({
196
+ defaultTagName: 'div',
197
+ render,
198
+ props: mergeProps({
199
+ ref: light,
200
+ 'aria-hidden': 'true',
201
+ 'data-slot': 'canvas-spotlight',
202
+ 'data-on': position ? '' : undefined,
203
+ className: canvasVariants().layer({ className: [canvasVariants().spotlight(), className] }),
204
+ style: {
205
+ '--canvas-r': `${radius}px`,
206
+ ...(position ? { '--canvas-x': `${position.x}px`, '--canvas-y': `${position.y}px` } : null),
207
+ ...style,
208
+ },
209
+ children: (_jsx(_Fragment, { children: canvas.patterns.map((pattern) => {
210
+ const fade = FADES[pattern.fade];
211
+ // The pattern's own fade still applies inside the light.
212
+ const mask = fade ? `${circle}, ${fade}` : circle;
213
+ return (_jsx("svg", { className: "absolute inset-0 size-full", style: {
214
+ maskImage: mask,
215
+ WebkitMaskImage: mask,
216
+ maskComposite: fade ? 'intersect' : undefined,
217
+ WebkitMaskComposite: fade ? 'source-in' : undefined,
218
+ }, children: _jsx(Marks, { id: `${prefix}${pattern.key}`, spec: pattern, viewport: canvas.viewport }) }, pattern.key));
219
+ }) })),
220
+ }, props),
221
+ });
198
222
  }
199
223
  CanvasSpotlight.displayName = 'Canvas.Spotlight';
200
224
  /** What sits on the canvas, above its patterns and its light. */
201
- function CanvasContent({ className, ...props }) {
202
- return _jsx("div", { "data-slot": "canvas-content", className: canvasVariants().content({ className }), ...props });
225
+ function CanvasContent({ className, render, ...props }) {
226
+ return useRender({
227
+ defaultTagName: 'div',
228
+ render,
229
+ props: mergeProps({ 'data-slot': 'canvas-content', className: canvasVariants().content({ className }) }, props),
230
+ });
203
231
  }
204
232
  CanvasContent.displayName = 'Canvas.Content';
205
233
  export const Canvas = Object.assign(CanvasRoot, {