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
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Radio Group — one choice from a short list, all of it visible at once.
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* <RadioGroup defaultValue="standard" aria-label="Delivery">
|
|
8
|
+
* <Label><Radio value="standard" /><Label.Text>Standard</Label.Text></Label>
|
|
9
|
+
* <Label><Radio value="express" /><Label.Text>Express</Label.Text></Label>
|
|
10
|
+
* </RadioGroup>
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* ## The dot lands
|
|
14
|
+
*
|
|
15
|
+
* The chosen radio's centre grows in with a small overshoot, and the ring
|
|
16
|
+
* thickens to meet it, so the choice reads as something being set down
|
|
17
|
+
* rather than a colour changing. The one it leaves shrinks back at the same
|
|
18
|
+
* time. With reduced motion both happen at once.
|
|
19
|
+
*
|
|
20
|
+
* ## Arrow keys move the choice
|
|
21
|
+
*
|
|
22
|
+
* The group is one stop for Tab. Inside it, the arrow keys move the choice
|
|
23
|
+
* from one radio to the next, as a native radio group does.
|
|
24
|
+
*
|
|
25
|
+
* ## As cards
|
|
26
|
+
*
|
|
27
|
+
* `RadioGroup.Card` puts each option in a card of its own, with a title, a
|
|
28
|
+
* line of description and something at the far end such as a price, and the
|
|
29
|
+
* radio drawn inside it. The whole card is the target. It comes in three
|
|
30
|
+
* versions: `outline` rings the chosen card in its colour, `filled` tints
|
|
31
|
+
* it, and `dither` fills it from the far edge with an ordered dither in its
|
|
32
|
+
* colour, densest at the edge and thinning towards the text, which sweeps in
|
|
33
|
+
* as the card is chosen and back out as it is left.
|
|
34
|
+
*
|
|
35
|
+
* ## When to use something else
|
|
36
|
+
*
|
|
37
|
+
* For two to five options that each need a line of explanation, ChoiceCards
|
|
38
|
+
* lays them out as cards. For more than about seven, a Select keeps the form
|
|
39
|
+
* short. For a choice that takes effect at once, without a form to submit,
|
|
40
|
+
* Tabs or a ButtonGroup read better.
|
|
41
|
+
*/
|
|
42
|
+
import { Radio as BaseRadio } from '@base-ui/react/radio';
|
|
43
|
+
import { RadioGroup as BaseRadioGroup } from '@base-ui/react/radio-group';
|
|
44
|
+
import { tv } from 'tailwind-variants';
|
|
45
|
+
import { mergeProps } from '@base-ui/react/merge-props';
|
|
46
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
47
|
+
const radioVariants = tv({
|
|
48
|
+
slots: {
|
|
49
|
+
group: 'flex gap-3 data-[orientation=vertical]:flex-col data-[orientation=horizontal]:flex-row data-[orientation=horizontal]:flex-wrap',
|
|
50
|
+
root: [
|
|
51
|
+
'group/radio relative inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border outline-none',
|
|
52
|
+
'border-input bg-background shadow-xs dark:bg-input/30',
|
|
53
|
+
'transition-[border-color,border-width,box-shadow] duration-200',
|
|
54
|
+
'hover:not-data-disabled:border-[color-mix(in_oklab,var(--input),var(--foreground)_25%)]',
|
|
55
|
+
'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
|
|
56
|
+
'data-checked:border-(--radio-color)',
|
|
57
|
+
'aria-invalid:border-destructive',
|
|
58
|
+
'data-disabled:cursor-not-allowed data-disabled:opacity-50',
|
|
59
|
+
"after:absolute after:-inset-2 after:content-['']",
|
|
60
|
+
],
|
|
61
|
+
indicator: [
|
|
62
|
+
'pointer-events-none size-[46%] rounded-full bg-(--radio-color)',
|
|
63
|
+
'scale-0 transition-transform duration-300 ease-[cubic-bezier(0.3,1.6,0.5,1)]',
|
|
64
|
+
'data-checked:scale-100 motion-reduce:transition-none',
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
variants: {
|
|
68
|
+
size: {
|
|
69
|
+
sm: { root: 'size-3.5' },
|
|
70
|
+
md: { root: 'size-4 data-checked:border-[1.5px]' },
|
|
71
|
+
lg: { root: 'size-5 data-checked:border-2' },
|
|
72
|
+
},
|
|
73
|
+
color: {
|
|
74
|
+
primary: { root: '[--radio-color:var(--primary)]' },
|
|
75
|
+
success: { root: '[--radio-color:var(--success)]' },
|
|
76
|
+
info: { root: '[--radio-color:var(--info)]' },
|
|
77
|
+
destructive: { root: '[--radio-color:var(--destructive)]' },
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
defaultVariants: { size: 'md', color: 'primary' },
|
|
81
|
+
});
|
|
82
|
+
/** Holds one value for the radios inside it. Takes `value`/`onValueChange` or `defaultValue`, and `name` for a form. */
|
|
83
|
+
function RadioGroupRoot({ orientation = 'vertical', className, ...props }) {
|
|
84
|
+
return (_jsx(BaseRadioGroup, { "data-slot": "radio-group", "data-orientation": orientation, "aria-orientation": orientation, className: radioVariants().group({ className }), ...props }));
|
|
85
|
+
}
|
|
86
|
+
RadioGroupRoot.displayName = 'RadioGroup';
|
|
87
|
+
/** One option. `value` is what the group's value becomes when it is chosen. */
|
|
88
|
+
function Radio({ size = 'md', color, label, className, ...props }) {
|
|
89
|
+
const slots = radioVariants({ size, color });
|
|
90
|
+
return (_jsx(BaseRadio.Root, { "data-slot": "radio", "data-size": size, "aria-label": label, className: slots.root({ className }), ...props, children: _jsx(BaseRadio.Indicator, { "data-slot": "radio-indicator", keepMounted: true, className: slots.indicator() }) }));
|
|
91
|
+
}
|
|
92
|
+
Radio.displayName = 'RadioGroup.Item';
|
|
93
|
+
/** The 4×4 Bayer matrix, as thresholds 0–15. */
|
|
94
|
+
const BAYER = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5];
|
|
95
|
+
/** Masks an element to an even ordered dither at `level` of 16, in 2px cells. */
|
|
96
|
+
function ditherMask(level) {
|
|
97
|
+
let rects = '';
|
|
98
|
+
BAYER.forEach((threshold, index) => {
|
|
99
|
+
if (threshold < level)
|
|
100
|
+
rects += `<rect x='${index % 4}' y='${Math.floor(index / 4)}' width='1' height='1'/>`;
|
|
101
|
+
});
|
|
102
|
+
const image = `url("data:image/svg+xml,${encodeURIComponent(`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4' shape-rendering='crispEdges'>${rects}</svg>`)}")`;
|
|
103
|
+
return { maskImage: image, WebkitMaskImage: image, maskSize: '8px 8px', WebkitMaskSize: '8px 8px' };
|
|
104
|
+
}
|
|
105
|
+
/** The dither ramp's bands, from the far edge in: how wide each reaches, and how dense it is. */
|
|
106
|
+
const RAMP = [
|
|
107
|
+
{ reach: '9%', level: 12 },
|
|
108
|
+
{ reach: '17%', level: 7 },
|
|
109
|
+
{ reach: '25%', level: 3 },
|
|
110
|
+
{ reach: '32%', level: 1 },
|
|
111
|
+
].map((band) => ({ ...band, style: ditherMask(band.level) }));
|
|
112
|
+
const cardVariants = tv({
|
|
113
|
+
slots: {
|
|
114
|
+
card: [
|
|
115
|
+
'group/card relative flex w-full min-w-0 cursor-pointer items-start gap-3 overflow-hidden rounded-xl border p-3.5 text-start outline-none select-none',
|
|
116
|
+
'border-border bg-background shadow-xs transition-[border-color,background-color,box-shadow] duration-200 dark:bg-input/20',
|
|
117
|
+
'hover:not-data-disabled:border-[color-mix(in_oklab,var(--border),var(--foreground)_18%)]',
|
|
118
|
+
'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
|
|
119
|
+
'data-disabled:cursor-not-allowed data-disabled:opacity-50',
|
|
120
|
+
],
|
|
121
|
+
dot: [
|
|
122
|
+
'relative z-10 mt-0.5 inline-flex size-4 shrink-0 items-center justify-center rounded-full border border-input bg-background',
|
|
123
|
+
'transition-[border-color,border-width] duration-200 group-data-checked/card:border-[1.5px] group-data-checked/card:border-(--radio-color)',
|
|
124
|
+
],
|
|
125
|
+
dotFill: [
|
|
126
|
+
'size-[46%] rounded-full bg-(--radio-color) scale-0 transition-transform duration-300 ease-[cubic-bezier(0.3,1.6,0.5,1)]',
|
|
127
|
+
'group-data-checked/card:scale-100 motion-reduce:transition-none',
|
|
128
|
+
],
|
|
129
|
+
body: 'relative z-10 flex min-w-0 flex-1 flex-col gap-0.5',
|
|
130
|
+
title: 'text-sm font-medium leading-5 text-foreground',
|
|
131
|
+
description: 'text-[0.8125rem] leading-5 text-muted-foreground',
|
|
132
|
+
aside: 'relative z-10 ms-auto shrink-0 text-sm font-medium tabular-nums text-foreground',
|
|
133
|
+
ramp: 'pointer-events-none absolute inset-0',
|
|
134
|
+
band: [
|
|
135
|
+
'absolute inset-y-0 end-0 bg-(--radio-color)',
|
|
136
|
+
// Hidden by sliding its clip to the far edge, so the sweep runs from there inwards.
|
|
137
|
+
'[clip-path:inset(0_0_0_100%)] rtl:[clip-path:inset(0_100%_0_0)] transition-[clip-path] duration-500 ease-[cubic-bezier(0.22,1,0.36,1)]',
|
|
138
|
+
'group-data-checked/card:[clip-path:inset(0_0_0_0)] motion-reduce:transition-none',
|
|
139
|
+
],
|
|
140
|
+
cards: 'grid gap-2',
|
|
141
|
+
},
|
|
142
|
+
variants: {
|
|
143
|
+
appearance: {
|
|
144
|
+
outline: {
|
|
145
|
+
card: 'data-checked:border-(--radio-color) data-checked:shadow-[0_0_0_1px_var(--radio-color)]',
|
|
146
|
+
},
|
|
147
|
+
filled: {
|
|
148
|
+
card: [
|
|
149
|
+
'data-checked:border-[color-mix(in_oklab,var(--radio-color)_45%,var(--background))]',
|
|
150
|
+
'data-checked:bg-[color-mix(in_oklab,var(--radio-color)_9%,var(--background))] dark:data-checked:bg-[color-mix(in_oklab,var(--radio-color)_16%,var(--background))]',
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
dither: {
|
|
154
|
+
card: 'data-checked:border-[color-mix(in_oklab,var(--radio-color)_55%,var(--background))]',
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
color: {
|
|
158
|
+
primary: { card: '[--radio-color:var(--primary)]' },
|
|
159
|
+
success: { card: '[--radio-color:var(--success)]' },
|
|
160
|
+
info: { card: '[--radio-color:var(--info)]' },
|
|
161
|
+
destructive: { card: '[--radio-color:var(--destructive)]' },
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
defaultVariants: { appearance: 'outline', color: 'primary' },
|
|
165
|
+
});
|
|
166
|
+
/** One option drawn as a card, with its radio inside. The whole card is pressed to choose it. */
|
|
167
|
+
function RadioCard({ appearance = 'outline', color, className, children, ...props }) {
|
|
168
|
+
const slots = cardVariants({ appearance, color });
|
|
169
|
+
return (_jsxs(BaseRadio.Root, { "data-slot": "radio-card", "data-appearance": appearance, className: slots.card({ className }), ...props, children: [appearance === 'dither' ? (_jsx("span", { "aria-hidden": "true", className: slots.ramp(), children: RAMP.map((band) => (_jsx("span", { className: slots.band(), style: { width: band.reach, ...band.style } }, band.level))) })) : null, _jsx("span", { "data-slot": "radio-card-indicator", "aria-hidden": "true", className: slots.dot(), children: _jsx("span", { className: slots.dotFill() }) }), children] }));
|
|
170
|
+
}
|
|
171
|
+
RadioCard.displayName = 'RadioGroup.Card';
|
|
172
|
+
/** Holds a card's title and description, beside the radio. */
|
|
173
|
+
function RadioCardBody({ className, render, ...props }) {
|
|
174
|
+
return useRender({
|
|
175
|
+
defaultTagName: 'span',
|
|
176
|
+
render,
|
|
177
|
+
props: mergeProps({ 'data-slot': 'radio-card-body', className: cardVariants().body({ className }) }, props),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
RadioCardBody.displayName = 'RadioGroup.CardBody';
|
|
181
|
+
/** The option's name. */
|
|
182
|
+
function RadioCardTitle({ className, render, ...props }) {
|
|
183
|
+
return useRender({
|
|
184
|
+
defaultTagName: 'span',
|
|
185
|
+
render,
|
|
186
|
+
props: mergeProps({ 'data-slot': 'radio-card-title', className: cardVariants().title({ className }) }, props),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
RadioCardTitle.displayName = 'RadioGroup.CardTitle';
|
|
190
|
+
/** A line under the name saying what the option means. */
|
|
191
|
+
function RadioCardDescription({ className, render, ...props }) {
|
|
192
|
+
return useRender({
|
|
193
|
+
defaultTagName: 'span',
|
|
194
|
+
render,
|
|
195
|
+
props: mergeProps({ 'data-slot': 'radio-card-description', className: cardVariants().description({ className }) }, props),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
RadioCardDescription.displayName = 'RadioGroup.CardDescription';
|
|
199
|
+
/** Pushed to the far end of the card: a price, a duration, a badge. */
|
|
200
|
+
function RadioCardAside({ className, render, ...props }) {
|
|
201
|
+
return useRender({
|
|
202
|
+
defaultTagName: 'span',
|
|
203
|
+
render,
|
|
204
|
+
props: mergeProps({ 'data-slot': 'radio-card-aside', className: cardVariants().aside({ className }) }, props),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
RadioCardAside.displayName = 'RadioGroup.CardAside';
|
|
208
|
+
export const RadioGroup = Object.assign(RadioGroupRoot, {
|
|
209
|
+
Item: Radio,
|
|
210
|
+
Card: RadioCard,
|
|
211
|
+
CardBody: RadioCardBody,
|
|
212
|
+
CardTitle: RadioCardTitle,
|
|
213
|
+
CardDescription: RadioCardDescription,
|
|
214
|
+
CardAside: RadioCardAside,
|
|
215
|
+
});
|
|
216
|
+
export { Radio, Radio as RadioGroupItem, RadioCard, RadioCard as RadioGroupCard, RadioCardBody as RadioGroupCardBody, RadioCardTitle as RadioGroupCardTitle, RadioCardDescription as RadioGroupCardDescription, RadioCardAside as RadioGroupCardAside, };
|
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
* threshold is how a meter says the reading has gone from fine to not.
|
|
36
36
|
*/
|
|
37
37
|
import { type ComponentProps, type ReactNode } 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
|
/** Which of the theme's roles the lit ticks and the tag are drawn in. */
|
|
39
45
|
export type RangeMeterColor = 'primary' | 'info' | 'success' | 'warning' | 'destructive';
|
|
40
46
|
export interface RangeMeterProps extends Omit<ComponentProps<'div'>, 'onChange' | 'color'> {
|
|
@@ -66,7 +72,7 @@ export interface RangeMeterProps extends Omit<ComponentProps<'div'>, 'onChange'
|
|
|
66
72
|
/** Drawn in the tag before the number: an icon. */
|
|
67
73
|
children?: ReactNode;
|
|
68
74
|
}
|
|
69
|
-
declare function RangeMeterRoot({ min, max, value, onValueChange, step, marker, ticks, color, formatValue, label, valueText, className, children, ...props }: RangeMeterProps): import("react").
|
|
75
|
+
declare function RangeMeterRoot({ min, max, value, onValueChange, step, marker, ticks, color, formatValue, label, valueText, className, children, render, ...props }: RangeMeterProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
70
76
|
declare namespace RangeMeterRoot {
|
|
71
77
|
var displayName: string;
|
|
72
78
|
}
|
|
@@ -76,7 +82,7 @@ export interface RangeMeterLabelProps extends ComponentProps<'p'> {
|
|
|
76
82
|
* The meter's name, written over it. The meter's own `label` is what a screen
|
|
77
83
|
* reader says; this is what everyone else reads.
|
|
78
84
|
*/
|
|
79
|
-
declare function RangeMeterLabel({ className, ...props }: RangeMeterLabelProps): import("react").
|
|
85
|
+
declare function RangeMeterLabel({ className, render, ...props }: RangeMeterLabelProps & RenderProp<'p'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
80
86
|
declare namespace RangeMeterLabel {
|
|
81
87
|
var displayName: string;
|
|
82
88
|
}
|
|
@@ -87,7 +93,7 @@ export interface RangeMeterScaleProps extends Omit<ComponentProps<'div'>, 'child
|
|
|
87
93
|
end: ReactNode;
|
|
88
94
|
}
|
|
89
95
|
/** What the two ends of the ruler stand for, written under it at either side. */
|
|
90
|
-
declare function RangeMeterScale({ start, end, className, ...props }: RangeMeterScaleProps): import("react").
|
|
96
|
+
declare function RangeMeterScale({ start, end, className, render, ...props }: RangeMeterScaleProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
91
97
|
declare namespace RangeMeterScale {
|
|
92
98
|
var displayName: string;
|
|
93
99
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
4
|
* RangeMeter — where a reading sits between its lowest and its highest, on a
|
|
5
5
|
* ruler of ticks that rises under the value.
|
|
@@ -38,6 +38,8 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
|
38
38
|
*/
|
|
39
39
|
import { useCallback, useRef, } 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 rangeMeterVariants = tv({
|
|
42
44
|
slots: {
|
|
43
45
|
// Room above the ruler for the tag, so the tag is inside the control's
|
|
@@ -90,7 +92,7 @@ const clamp = (value, low, high) => Math.min(high, Math.max(low, value));
|
|
|
90
92
|
const SPREAD = 2.6;
|
|
91
93
|
/** A resting tick's height, as a share of the ruler's. */
|
|
92
94
|
const REST = 0.34;
|
|
93
|
-
function RangeMeterRoot({ min = 0, max = 100, value, onValueChange, step = 1, marker, ticks = 41, color = 'primary', formatValue, label = 'Reading', valueText, className, children, ...props }) {
|
|
95
|
+
function RangeMeterRoot({ min = 0, max = 100, value, onValueChange, step = 1, marker, ticks = 41, color = 'primary', formatValue, label = 'Reading', valueText, className, children, render, ...props }) {
|
|
94
96
|
const interactive = onValueChange !== undefined;
|
|
95
97
|
const slots = rangeMeterVariants({ color, interactive, marked: marker !== undefined });
|
|
96
98
|
const ruler = useRef(null);
|
|
@@ -150,40 +152,72 @@ function RangeMeterRoot({ min = 0, max = 100, value, onValueChange, step = 1, ma
|
|
|
150
152
|
event.preventDefault();
|
|
151
153
|
onValueChange(clamp(Number(next.toFixed(places)), min, max));
|
|
152
154
|
};
|
|
153
|
-
return (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
155
|
+
return useRender({
|
|
156
|
+
defaultTagName: 'div',
|
|
157
|
+
render,
|
|
158
|
+
props: mergeProps({
|
|
159
|
+
role: interactive ? 'slider' : 'meter',
|
|
160
|
+
'aria-label': label,
|
|
161
|
+
'aria-valuemin': min,
|
|
162
|
+
'aria-valuemax': max,
|
|
163
|
+
'aria-valuenow': value,
|
|
164
|
+
'aria-valuetext': valueText,
|
|
165
|
+
'aria-orientation': interactive ? 'horizontal' : undefined,
|
|
166
|
+
tabIndex: interactive ? 0 : undefined,
|
|
167
|
+
className: slots.root({ className }),
|
|
168
|
+
onPointerDown: onPointerDown,
|
|
169
|
+
onPointerMove: onPointerMove,
|
|
170
|
+
onPointerUp: endDrag,
|
|
171
|
+
onPointerCancel: endDrag,
|
|
172
|
+
onKeyDown: onKeyDown,
|
|
173
|
+
children: (_jsxs(_Fragment, { children: [_jsxs("span", { "data-tag": "", className: slots.tag(), style: {
|
|
174
|
+
insetInlineStart: `${(share * 100).toFixed(2)}%`,
|
|
175
|
+
// Hung by the same share of its own width as the point is along the
|
|
176
|
+
// ruler, which keeps it over the point and inside the ends.
|
|
177
|
+
translate: `calc(${share.toFixed(4)} * -100% * var(--pu-dir, 1))`,
|
|
178
|
+
}, children: [children, formatValue ? formatValue(value) : value] }), _jsxs("div", { ref: ruler, "aria-hidden": "true", className: slots.ruler(), children: [Array.from({ length: count }, (_, index) => {
|
|
179
|
+
const distance = (index - at) / SPREAD;
|
|
180
|
+
// A bell: full height at the reading, resting height a few ticks out.
|
|
181
|
+
const rise = Math.exp(-distance * distance);
|
|
182
|
+
const lit = index <= at + 0.001;
|
|
183
|
+
const part = rangeMeterVariants({ lit, crest: index === crest });
|
|
184
|
+
return (_jsx("span", { className: part.tick(), style: {
|
|
185
|
+
// Rounded, so the server and the browser write the same
|
|
186
|
+
// string for it and the page hydrates without a mismatch.
|
|
187
|
+
height: `${((REST + (1 - REST) * rise) * 100).toFixed(1)}%`,
|
|
188
|
+
// Lit ticks brighten towards the reading, like a tail behind it.
|
|
189
|
+
opacity: lit
|
|
190
|
+
? Number((0.35 + 0.65 * (crest === 0 ? 1 : Math.min(1, index / crest))).toFixed(3))
|
|
191
|
+
: undefined,
|
|
192
|
+
} }, index));
|
|
193
|
+
}), marker !== undefined ? (_jsx("span", { className: slots.marker(), style: { insetInlineStart: `${(clamp((marker - min) / span, 0, 1) * 100).toFixed(2)}%` } })) : null] })] })),
|
|
194
|
+
}, props),
|
|
195
|
+
});
|
|
174
196
|
}
|
|
175
197
|
RangeMeterRoot.displayName = 'RangeMeter';
|
|
176
198
|
/**
|
|
177
199
|
* The meter's name, written over it. The meter's own `label` is what a screen
|
|
178
200
|
* reader says; this is what everyone else reads.
|
|
179
201
|
*/
|
|
180
|
-
function RangeMeterLabel({ className, ...props }) {
|
|
181
|
-
return
|
|
202
|
+
function RangeMeterLabel({ className, render, ...props }) {
|
|
203
|
+
return useRender({
|
|
204
|
+
defaultTagName: 'p',
|
|
205
|
+
render,
|
|
206
|
+
props: mergeProps({ 'data-slot': 'range-meter-label', className: rangeMeterVariants().label({ className }) }, props),
|
|
207
|
+
});
|
|
182
208
|
}
|
|
183
209
|
RangeMeterLabel.displayName = 'RangeMeter.Label';
|
|
184
210
|
/** What the two ends of the ruler stand for, written under it at either side. */
|
|
185
|
-
function RangeMeterScale({ start, end, className, ...props }) {
|
|
186
|
-
return (
|
|
211
|
+
function RangeMeterScale({ start, end, className, render, ...props }) {
|
|
212
|
+
return useRender({
|
|
213
|
+
defaultTagName: 'div',
|
|
214
|
+
render,
|
|
215
|
+
props: mergeProps({
|
|
216
|
+
'data-slot': 'range-meter-scale',
|
|
217
|
+
className: rangeMeterVariants().scale({ className }),
|
|
218
|
+
children: (_jsxs(_Fragment, { children: [_jsx("span", { children: start }), _jsx("span", { children: end })] })),
|
|
219
|
+
}, props),
|
|
220
|
+
});
|
|
187
221
|
}
|
|
188
222
|
RangeMeterScale.displayName = 'RangeMeter.Scale';
|
|
189
223
|
export const RangeMeter = Object.assign(RangeMeterRoot, {
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
* the nines roll to zeroes instead of being replaced by them.
|
|
24
24
|
*/
|
|
25
25
|
import { type ComponentProps } from 'react';
|
|
26
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
27
|
+
import type { ElementType as RenderElementType } from 'react';
|
|
28
|
+
/** Draws a part as another element, or composes it with a component: `render={<a href="/" />}`. */
|
|
29
|
+
type RenderProp<Tag extends RenderElementType> = {
|
|
30
|
+
render?: useRender.ComponentProps<Tag>['render'];
|
|
31
|
+
};
|
|
26
32
|
export interface RollingNumberProps extends Omit<ComponentProps<'span'>, 'children'> {
|
|
27
33
|
/** The number to show. Each digit rolls to its new value on its own. */
|
|
28
34
|
value: number;
|
|
@@ -40,7 +46,8 @@ export interface RollingNumberProps extends Omit<ComponentProps<'span'>, 'childr
|
|
|
40
46
|
/** How long a digit takes to roll, in milliseconds. */
|
|
41
47
|
duration?: number;
|
|
42
48
|
}
|
|
43
|
-
export declare function RollingNumber({ value, format, locales, duration, className, ...props }: RollingNumberProps): import("react").
|
|
49
|
+
export declare function RollingNumber({ value, format, locales, duration, className, render, ...props }: RollingNumberProps & RenderProp<'span'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
44
50
|
export declare namespace RollingNumber {
|
|
45
51
|
var displayName: string;
|
|
46
52
|
}
|
|
53
|
+
export {};
|
|
@@ -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
|
* RollingNumber — a number whose digits roll to their new values.
|
|
5
5
|
*
|
|
@@ -26,6 +26,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
26
26
|
*/
|
|
27
27
|
import { useMemo } from 'react';
|
|
28
28
|
import { tv } from 'tailwind-variants';
|
|
29
|
+
import { mergeProps } from '@base-ui/react/merge-props';
|
|
30
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
29
31
|
const rollingNumberVariants = tv({
|
|
30
32
|
slots: {
|
|
31
33
|
// `tabular-nums` so a 1 is as wide as an 8 and the number does not change
|
|
@@ -61,7 +63,7 @@ function piecesOf(parts) {
|
|
|
61
63
|
});
|
|
62
64
|
return pieces;
|
|
63
65
|
}
|
|
64
|
-
export function RollingNumber({ value, format, locales = 'en-US', duration = 600, className, ...props }) {
|
|
66
|
+
export function RollingNumber({ value, format, locales = 'en-US', duration = 600, className, render, ...props }) {
|
|
65
67
|
const slots = rollingNumberVariants();
|
|
66
68
|
// The options object is usually written inline, so it is new on every
|
|
67
69
|
// render. Its contents are what the formatter depends on.
|
|
@@ -73,9 +75,16 @@ export function RollingNumber({ value, format, locales = 'en-US', duration = 600
|
|
|
73
75
|
const parts = formatter.formatToParts(value);
|
|
74
76
|
return { label: parts.map((part) => part.value).join(''), pieces: piecesOf(parts) };
|
|
75
77
|
}, [formatter, value]);
|
|
76
|
-
return (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
return useRender({
|
|
79
|
+
defaultTagName: 'span',
|
|
80
|
+
render,
|
|
81
|
+
props: mergeProps({
|
|
82
|
+
className: slots.root({ className }),
|
|
83
|
+
children: (_jsxs(_Fragment, { children: [_jsx("span", { className: "sr-only", children: label }), pieces.map((piece) => piece.digit === null ? (_jsx("span", { "aria-hidden": "true", children: piece.text }, piece.key)) : (_jsxs("span", { "aria-hidden": "true", className: slots.column(), children: [_jsx("span", { className: "invisible", children: "0" }), _jsx("span", { className: slots.window(), children: _jsx("span", { className: slots.strip(), style: {
|
|
84
|
+
transform: `translateY(${-piece.digit * 10}%)`,
|
|
85
|
+
transitionDuration: `${duration}ms`,
|
|
86
|
+
}, children: DIGITS.map((digit) => (_jsx("span", { className: slots.digit(), children: digit }, digit))) }) })] }, piece.key)))] })),
|
|
87
|
+
}, props),
|
|
88
|
+
});
|
|
80
89
|
}
|
|
81
90
|
RollingNumber.displayName = 'RollingNumber';
|
|
@@ -39,6 +39,12 @@
|
|
|
39
39
|
*/
|
|
40
40
|
import { Select as BaseSelect } from '@base-ui/react/select';
|
|
41
41
|
import type { ComponentProps, ReactNode } from 'react';
|
|
42
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
43
|
+
import type { ElementType as RenderElementType } from 'react';
|
|
44
|
+
/** Draws a part as another element, or composes it with a component: `render={<a href="/" />}`. */
|
|
45
|
+
type RenderProp<Tag extends RenderElementType> = {
|
|
46
|
+
render?: useRender.ComponentProps<Tag>['render'];
|
|
47
|
+
};
|
|
42
48
|
export type SelectProps<Value = string, Multiple extends boolean | undefined = false> = BaseSelect.Root.Props<Value, Multiple>;
|
|
43
49
|
/** Holds the chosen value. Takes `value`/`onValueChange`, `defaultValue`, `items`, `name`, `disabled` and `multiple`. */
|
|
44
50
|
declare function SelectRoot<Value = string, Multiple extends boolean | undefined = false>(props: SelectProps<Value, Multiple>): import("react").JSX.Element;
|
|
@@ -118,7 +124,7 @@ declare namespace SelectGroupLabel {
|
|
|
118
124
|
export interface SelectSeparatorProps extends ComponentProps<'div'> {
|
|
119
125
|
}
|
|
120
126
|
/** A rule between groups of options. */
|
|
121
|
-
declare function SelectSeparator({ className, ...props }: SelectSeparatorProps): import("react").
|
|
127
|
+
declare function SelectSeparator({ className, render, ...props }: SelectSeparatorProps & RenderProp<'div'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
122
128
|
declare namespace SelectSeparator {
|
|
123
129
|
var displayName: string;
|
|
124
130
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
/**
|
|
3
4
|
* Select — a button that opens a list of options, one of which is chosen.
|
|
@@ -40,6 +41,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
40
41
|
*/
|
|
41
42
|
import { Select as BaseSelect } from '@base-ui/react/select';
|
|
42
43
|
import { tv } from 'tailwind-variants';
|
|
44
|
+
import { mergeProps } from '@base-ui/react/merge-props';
|
|
45
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
43
46
|
const selectVariants = tv({
|
|
44
47
|
slots: {
|
|
45
48
|
trigger: [
|
|
@@ -112,12 +115,14 @@ SelectRoot.displayName = 'Select';
|
|
|
112
115
|
function Chevron({ variant }) {
|
|
113
116
|
// A field shows both directions, since its list can open either way over it.
|
|
114
117
|
// A chip shows one, and turns over while its list is open.
|
|
115
|
-
return (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: variant === 'default' || variant === undefined ? _jsx("path", { d: "m7 15 5 5 5-5M7 9l5-5 5 5" }) : _jsx("path", { d: "m6 9 6 6 6-6" }) }));
|
|
118
|
+
return (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: variant === 'default' || variant === undefined ? (_jsx("path", { d: "m7 15 5 5 5-5M7 9l5-5 5 5" })) : (_jsx("path", { d: "m6 9 6 6 6-6" })) }));
|
|
116
119
|
}
|
|
117
120
|
/** The button that opens the list. Put a `Select.Value` in it. */
|
|
118
121
|
function SelectTrigger({ variant, size, icon, className, children, ...props }) {
|
|
119
122
|
const slots = selectVariants({ variant, size });
|
|
120
|
-
return (_jsxs(BaseSelect.Trigger, { "data-slot": "select-trigger", className: slots.trigger({ className }), ...props, children: [children, icon === null ? null : (_jsx(BaseSelect.Icon, { "data-slot": "select-icon", className: slots.icon({
|
|
123
|
+
return (_jsxs(BaseSelect.Trigger, { "data-slot": "select-trigger", className: slots.trigger({ className }), ...props, children: [children, icon === null ? null : (_jsx(BaseSelect.Icon, { "data-slot": "select-icon", className: slots.icon({
|
|
124
|
+
className: variant === 'default' || variant === undefined ? 'rotate-0!' : undefined,
|
|
125
|
+
}), children: icon ?? _jsx(Chevron, { variant: variant }) }))] }));
|
|
121
126
|
}
|
|
122
127
|
SelectTrigger.displayName = 'Select.Trigger';
|
|
123
128
|
/** The chosen option's label, or `placeholder` while there is none. */
|
|
@@ -148,8 +153,12 @@ function SelectGroupLabel({ className, ...props }) {
|
|
|
148
153
|
}
|
|
149
154
|
SelectGroupLabel.displayName = 'Select.GroupLabel';
|
|
150
155
|
/** A rule between groups of options. */
|
|
151
|
-
function SelectSeparator({ className, ...props }) {
|
|
152
|
-
return
|
|
156
|
+
function SelectSeparator({ className, render, ...props }) {
|
|
157
|
+
return useRender({
|
|
158
|
+
defaultTagName: 'div',
|
|
159
|
+
render,
|
|
160
|
+
props: mergeProps({ role: 'separator', 'data-slot': 'select-separator', className: selectVariants().separator({ className }) }, props),
|
|
161
|
+
});
|
|
153
162
|
}
|
|
154
163
|
SelectSeparator.displayName = 'Select.Separator';
|
|
155
164
|
export const Select = Object.assign(SelectRoot, {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { type ComponentProps, type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
+
import type { ElementType as RenderElementType } from 'react';
|
|
4
|
+
/** Draws a part as another element, or composes it with a component: `render={<a href="/" />}`. */
|
|
5
|
+
type RenderProp<Tag extends RenderElementType> = {
|
|
6
|
+
render?: useRender.ComponentProps<Tag>['render'];
|
|
7
|
+
};
|
|
2
8
|
/** Where the sheet rises from, which is also the direction it leaves in. */
|
|
3
9
|
export type SheetSide = 'bottom' | 'top' | 'left' | 'right';
|
|
4
10
|
export interface SheetProps {
|
|
@@ -119,7 +125,7 @@ export interface SheetHeaderProps extends Omit<ComponentProps<'div'>, 'title'> {
|
|
|
119
125
|
* The heading block. It leaves room for the close button beside the title,
|
|
120
126
|
* so the two share the top line instead of stacking.
|
|
121
127
|
*/
|
|
122
|
-
declare function SheetHeader({ title, description, className, children, ...props }: SheetHeaderProps): import("react").
|
|
128
|
+
declare function SheetHeader({ title, description, className, children, render, ...props }: SheetHeaderProps & RenderProp<'div'>): ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
123
129
|
declare namespace SheetHeader {
|
|
124
130
|
var displayName: string;
|
|
125
131
|
}
|
|
@@ -212,7 +218,7 @@ export interface SheetSlideProps extends ComponentProps<'div'> {
|
|
|
212
218
|
aspect?: string;
|
|
213
219
|
}
|
|
214
220
|
/** One slide: a rounded frame that fills with an image, a video or anything drawn. */
|
|
215
|
-
declare function SheetSlide({ label, aspect, className, style, children, ...props }: SheetSlideProps): import("react").JSX.Element;
|
|
221
|
+
declare function SheetSlide({ label, aspect, className, style, children, render, ...props }: SheetSlideProps & RenderProp<'div'>): import("react").JSX.Element;
|
|
216
222
|
declare namespace SheetSlide {
|
|
217
223
|
var displayName: string;
|
|
218
224
|
}
|