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.
- package/dist/components/alert/index.d.ts +15 -8
- package/dist/components/alert/index.js +65 -18
- package/dist/components/alert-dialog/index.d.ts +9 -3
- package/dist/components/alert-dialog/index.js +23 -7
- package/dist/components/avatar/index.d.ts +9 -3
- package/dist/components/avatar/index.js +34 -10
- package/dist/components/badge/index.d.ts +57 -0
- package/dist/components/badge/index.js +61 -0
- package/dist/components/bar-picker/index.d.ts +8 -1
- package/dist/components/bar-picker/index.js +35 -26
- package/dist/components/button/index.d.ts +68 -46
- package/dist/components/button/index.js +38 -2
- package/dist/components/button-group/index.d.ts +11 -5
- package/dist/components/button-group/index.js +31 -7
- package/dist/components/canvas/index.d.ts +9 -3
- package/dist/components/canvas/index.js +53 -25
- package/dist/components/card/index.d.ts +13 -7
- package/dist/components/card/index.js +45 -17
- package/dist/components/checkbox/index.d.ts +73 -0
- package/dist/components/checkbox/index.js +100 -0
- package/dist/components/chip/index.d.ts +79 -0
- package/dist/components/chip/index.js +112 -0
- package/dist/components/choice-cards/index.d.ts +11 -5
- package/dist/components/choice-cards/index.js +25 -6
- package/dist/components/command-dock/index.d.ts +16 -9
- package/dist/components/command-dock/index.js +76 -19
- package/dist/components/day-grid/index.d.ts +12 -6
- package/dist/components/day-grid/index.js +71 -27
- package/dist/components/dialog/index.d.ts +19 -12
- package/dist/components/dialog/index.js +84 -25
- package/dist/components/feedback/index.d.ts +20 -13
- package/dist/components/feedback/index.js +83 -27
- package/dist/components/field/index.d.ts +85 -0
- package/dist/components/field/index.js +90 -0
- package/dist/components/flow/index.d.ts +21 -14
- package/dist/components/flow/index.js +56 -20
- package/dist/components/frame/index.d.ts +17 -11
- package/dist/components/frame/index.js +73 -22
- package/dist/components/input/index.d.ts +9 -24
- package/dist/components/input/index.js +53 -6
- package/dist/components/inspector/index.d.ts +69 -63
- package/dist/components/inspector/index.js +31 -9
- package/dist/components/kpi/index.d.ts +20 -14
- package/dist/components/kpi/index.js +111 -36
- package/dist/components/label/index.d.ts +10 -4
- package/dist/components/label/index.js +39 -9
- package/dist/components/lane-board/index.d.ts +15 -9
- package/dist/components/lane-board/index.js +97 -57
- package/dist/components/menu/index.d.ts +10 -4
- package/dist/components/menu/index.js +28 -10
- package/dist/components/message/index.d.ts +15 -9
- package/dist/components/message/index.js +71 -19
- package/dist/components/not-found/index.d.ts +10 -4
- package/dist/components/not-found/index.js +48 -12
- package/dist/components/planet/index.d.ts +11 -5
- package/dist/components/planet/index.js +73 -20
- package/dist/components/prompt/index.d.ts +30 -23
- package/dist/components/prompt/index.js +148 -52
- package/dist/components/radio-group/index.d.ts +124 -0
- package/dist/components/radio-group/index.js +216 -0
- package/dist/components/range-meter/index.d.ts +9 -3
- package/dist/components/range-meter/index.js +61 -27
- package/dist/components/rolling-number/index.d.ts +8 -1
- package/dist/components/rolling-number/index.js +15 -6
- package/dist/components/select/index.d.ts +7 -1
- package/dist/components/select/index.js +13 -4
- package/dist/components/sheet/index.d.ts +8 -2
- package/dist/components/sheet/index.js +27 -15
- package/dist/components/slider/index.d.ts +11 -4
- package/dist/components/slider/index.js +30 -10
- package/dist/components/soundwave/index.d.ts +8 -2
- package/dist/components/soundwave/index.js +26 -6
- package/dist/components/stat/index.d.ts +13 -7
- package/dist/components/stat/index.js +45 -15
- package/dist/components/table/index.d.ts +128 -0
- package/dist/components/table/index.js +145 -0
- package/dist/components/tabs/index.d.ts +7 -1
- package/dist/components/tabs/index.js +8 -2
- package/dist/components/textarea/index.d.ts +0 -23
- package/dist/components/textarea/index.js +22 -5
- package/dist/components/tilt-card/index.d.ts +9 -3
- package/dist/components/tilt-card/index.js +28 -8
- package/dist/components/timeline/index.d.ts +20 -13
- package/dist/components/timeline/index.js +166 -59
- package/dist/components/toast/dither-text.d.ts +13 -0
- package/dist/components/toast/dither-text.js +143 -0
- package/dist/components/toast/dither.d.ts +29 -0
- package/dist/components/toast/dither.js +153 -0
- package/dist/components/toast/index.d.ts +108 -0
- package/dist/components/toast/index.js +225 -0
- package/dist/components/toolbar/index.d.ts +9 -3
- package/dist/components/toolbar/index.js +23 -6
- package/dist/components/workbench/index.d.ts +50 -43
- package/dist/components/workbench/index.js +77 -20
- package/dist/components/xy-pad/index.d.ts +9 -3
- package/dist/components/xy-pad/index.js +13 -2
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- 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
|
|
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 (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
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:
|
|
29
|
-
secondary:
|
|
30
|
-
outline:
|
|
31
|
-
ghost:
|
|
32
|
-
destructive:
|
|
33
|
-
link:
|
|
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:
|
|
37
|
-
md:
|
|
38
|
-
lg:
|
|
39
|
-
'icon-sm':
|
|
40
|
-
icon:
|
|
41
|
-
'icon-lg':
|
|
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:
|
|
46
|
-
pill:
|
|
55
|
+
default: "";
|
|
56
|
+
pill: "rounded-full";
|
|
47
57
|
};
|
|
48
58
|
}, undefined, string[], {
|
|
49
59
|
variant: {
|
|
50
|
-
default:
|
|
51
|
-
secondary:
|
|
52
|
-
outline:
|
|
53
|
-
ghost:
|
|
54
|
-
destructive:
|
|
55
|
-
link:
|
|
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:
|
|
59
|
-
md:
|
|
60
|
-
lg:
|
|
61
|
-
'icon-sm':
|
|
62
|
-
icon:
|
|
63
|
-
'icon-lg':
|
|
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:
|
|
68
|
-
pill:
|
|
77
|
+
default: "";
|
|
78
|
+
pill: "rounded-full";
|
|
69
79
|
};
|
|
70
|
-
}, undefined, import("tailwind-variants").
|
|
80
|
+
}, undefined, import("tailwind-variants").TVReturnTypeLike<{
|
|
71
81
|
variant: {
|
|
72
|
-
default:
|
|
73
|
-
secondary:
|
|
74
|
-
outline:
|
|
75
|
-
ghost:
|
|
76
|
-
destructive:
|
|
77
|
-
link:
|
|
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:
|
|
81
|
-
md:
|
|
82
|
-
lg:
|
|
83
|
-
'icon-sm':
|
|
84
|
-
icon:
|
|
85
|
-
'icon-lg':
|
|
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:
|
|
90
|
-
pill:
|
|
99
|
+
default: "";
|
|
100
|
+
pill: "rounded-full";
|
|
91
101
|
};
|
|
92
|
-
}, 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
|
-
|
|
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").
|
|
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
|
-
}
|
|
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").
|
|
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").
|
|
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").
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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").
|
|
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").
|
|
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").
|
|
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 (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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 (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
|
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, {
|