panelui-web 0.10.1 → 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 +76 -24
- 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
|
* Dialog — a task taken out of the page and done in a box over it: renaming
|
|
5
5
|
* something, inviting people, filling in a short form.
|
|
@@ -56,6 +56,8 @@ import { Dialog as Primitive } from '@base-ui/react/dialog';
|
|
|
56
56
|
import { ScrollArea } from '@base-ui/react/scroll-area';
|
|
57
57
|
import { Children, createContext, isValidElement, useContext, useEffect, useId, useMemo, useRef, } from 'react';
|
|
58
58
|
import { tv } from 'tailwind-variants';
|
|
59
|
+
import { mergeProps } from '@base-ui/react/merge-props';
|
|
60
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
59
61
|
const dialogVariants = tv({
|
|
60
62
|
slots: {
|
|
61
63
|
backdrop: 'fixed inset-0 z-50 bg-black/40 transition-opacity duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none',
|
|
@@ -151,7 +153,7 @@ const dialogVariants = tv({
|
|
|
151
153
|
defaultVariants: { variant: 'inset', size: 'md' },
|
|
152
154
|
});
|
|
153
155
|
const DialogContext = createContext({ inline: false, showClose: true, variant: 'inset' });
|
|
154
|
-
function DialogRoot({ children, open, defaultOpen, onOpenChange, dismissible = true
|
|
156
|
+
function DialogRoot({ children, open, defaultOpen, onOpenChange, dismissible = true }) {
|
|
155
157
|
return (_jsx(Primitive.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange ? (next) => onOpenChange(next) : undefined, disablePointerDismissal: !dismissible, children: children }));
|
|
156
158
|
}
|
|
157
159
|
DialogRoot.displayName = 'Dialog';
|
|
@@ -177,10 +179,18 @@ function DialogContent({ variant = 'inset', size = 'md', showClose = true, blur
|
|
|
177
179
|
}
|
|
178
180
|
DialogContent.displayName = 'Dialog.Content';
|
|
179
181
|
/** The heading block. It leaves room for the close button beside the title. */
|
|
180
|
-
function DialogHeader({ title, description, className, children, ...props }) {
|
|
182
|
+
function DialogHeader({ title, description, className, children, render, ...props }) {
|
|
181
183
|
const { showClose, variant } = useContext(DialogContext);
|
|
182
184
|
const slots = dialogVariants({ variant });
|
|
183
|
-
return (
|
|
185
|
+
return useRender({
|
|
186
|
+
defaultTagName: 'div',
|
|
187
|
+
render,
|
|
188
|
+
props: mergeProps({
|
|
189
|
+
'data-slot': 'dialog-header',
|
|
190
|
+
className: slots.header({ className: [showClose && 'pe-14', className] }),
|
|
191
|
+
children: (_jsxs(_Fragment, { children: [title !== undefined ? _jsx(DialogTitle, { children: title }) : null, description !== undefined ? _jsx(DialogDescription, { children: description }) : null, children] })),
|
|
192
|
+
}, props),
|
|
193
|
+
});
|
|
184
194
|
}
|
|
185
195
|
DialogHeader.displayName = 'Dialog.Header';
|
|
186
196
|
/** The dialog's name, announced when it opens. */
|
|
@@ -198,7 +208,7 @@ function DialogDescription({ className, ...props }) {
|
|
|
198
208
|
const slots = dialogVariants();
|
|
199
209
|
if (inline)
|
|
200
210
|
return _jsx("p", { "data-slot": "dialog-description", className: slots.description({ className }), ...props });
|
|
201
|
-
return _jsx(Primitive.Description, { "data-slot": "dialog-description", className: slots.description({ className }), ...props });
|
|
211
|
+
return (_jsx(Primitive.Description, { "data-slot": "dialog-description", className: slots.description({ className }), ...props }));
|
|
202
212
|
}
|
|
203
213
|
DialogDescription.displayName = 'Dialog.Description';
|
|
204
214
|
/**
|
|
@@ -216,9 +226,13 @@ DialogBody.displayName = 'Dialog.Body';
|
|
|
216
226
|
* equal pills; in `plain` they sit at the end of a ruled strip. Keep it a
|
|
217
227
|
* direct child of `Dialog.Content`, so the band can find it.
|
|
218
228
|
*/
|
|
219
|
-
function DialogFooter({ className, ...props }) {
|
|
229
|
+
function DialogFooter({ className, render, ...props }) {
|
|
220
230
|
const { variant } = useContext(DialogContext);
|
|
221
|
-
return
|
|
231
|
+
return useRender({
|
|
232
|
+
defaultTagName: 'div',
|
|
233
|
+
render,
|
|
234
|
+
props: mergeProps({ 'data-slot': 'dialog-footer', className: dialogVariants({ variant }).footer({ className }) }, props),
|
|
235
|
+
});
|
|
222
236
|
}
|
|
223
237
|
DialogFooter.displayName = 'Dialog.Footer';
|
|
224
238
|
/** Closes the dialog. */
|
|
@@ -235,9 +249,11 @@ DialogClose.displayName = 'Dialog.Close';
|
|
|
235
249
|
* A list of one-line choices in the body. The arrow keys, Home and End move
|
|
236
250
|
* between its items, and it opens scrolled to the selected one.
|
|
237
251
|
*/
|
|
238
|
-
function DialogList({ className, onKeyDown, children, ...props }) {
|
|
252
|
+
function DialogList({ className, onKeyDown, children, render, ...props }) {
|
|
239
253
|
const list = useRef(null);
|
|
240
|
-
const items = () => [
|
|
254
|
+
const items = () => [
|
|
255
|
+
...(list.current?.querySelectorAll('[data-slot=dialog-list-item]:not([aria-disabled=true])') ?? []),
|
|
256
|
+
];
|
|
241
257
|
useEffect(() => {
|
|
242
258
|
const all = items();
|
|
243
259
|
const selected = all.find((item) => item.dataset.selected !== undefined);
|
|
@@ -264,30 +280,66 @@ function DialogList({ className, onKeyDown, children, ...props }) {
|
|
|
264
280
|
event.preventDefault();
|
|
265
281
|
next.focus();
|
|
266
282
|
};
|
|
267
|
-
return (
|
|
283
|
+
return useRender({
|
|
284
|
+
defaultTagName: 'div',
|
|
285
|
+
render,
|
|
286
|
+
props: mergeProps({
|
|
287
|
+
ref: list,
|
|
288
|
+
role: 'listbox',
|
|
289
|
+
'data-slot': 'dialog-list',
|
|
290
|
+
className: dialogVariants().list({ className }),
|
|
291
|
+
onKeyDown: move,
|
|
292
|
+
children: _jsx(_Fragment, { children: children }),
|
|
293
|
+
}, props),
|
|
294
|
+
});
|
|
268
295
|
}
|
|
269
296
|
DialogList.displayName = 'Dialog.List';
|
|
270
297
|
/** A run of items in `Dialog.List` under a heading of their own. */
|
|
271
|
-
function DialogListGroup({ label, className, children, ...props }) {
|
|
298
|
+
function DialogListGroup({ label, className, children, render, ...props }) {
|
|
272
299
|
const slots = dialogVariants();
|
|
273
300
|
const id = useId();
|
|
274
|
-
return (
|
|
301
|
+
return useRender({
|
|
302
|
+
defaultTagName: 'div',
|
|
303
|
+
render,
|
|
304
|
+
props: mergeProps({
|
|
305
|
+
role: 'group',
|
|
306
|
+
'aria-labelledby': id,
|
|
307
|
+
'data-slot': 'dialog-list-group',
|
|
308
|
+
className: slots.listGroup({ className }),
|
|
309
|
+
children: (_jsxs(_Fragment, { children: [_jsx("div", { id: id, className: slots.listGroupLabel(), children: label }), children] })),
|
|
310
|
+
}, props),
|
|
311
|
+
});
|
|
275
312
|
}
|
|
276
313
|
DialogListGroup.displayName = 'Dialog.ListGroup';
|
|
277
314
|
/** One choice in `Dialog.List`, on one line. */
|
|
278
|
-
function DialogListItem({ selected = false, onSelect, meta, disabled = false, className, onClick, onKeyDown, children, ...props }) {
|
|
315
|
+
function DialogListItem({ selected = false, onSelect, meta, disabled = false, className, onClick, onKeyDown, children, render, ...props }) {
|
|
279
316
|
const slots = dialogVariants();
|
|
280
|
-
return (
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
317
|
+
return useRender({
|
|
318
|
+
defaultTagName: 'div',
|
|
319
|
+
render,
|
|
320
|
+
props: mergeProps({
|
|
321
|
+
role: 'option',
|
|
322
|
+
'aria-selected': selected,
|
|
323
|
+
'aria-disabled': disabled || undefined,
|
|
324
|
+
tabIndex: selected ? 0 : -1,
|
|
325
|
+
'data-slot': 'dialog-list-item',
|
|
326
|
+
'data-selected': selected ? '' : undefined,
|
|
327
|
+
className: slots.listItem({ className }),
|
|
328
|
+
onClick: (event) => {
|
|
329
|
+
onClick?.(event);
|
|
330
|
+
if (!disabled)
|
|
331
|
+
onSelect?.();
|
|
332
|
+
},
|
|
333
|
+
onKeyDown: (event) => {
|
|
334
|
+
onKeyDown?.(event);
|
|
335
|
+
if ((event.key === 'Enter' || event.key === ' ') && !disabled) {
|
|
336
|
+
event.preventDefault();
|
|
337
|
+
onSelect?.();
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
children: (_jsxs(_Fragment, { children: [_jsx("span", { className: slots.listItemLabel(), children: children }), meta !== undefined ? _jsx("span", { className: slots.listItemMeta(), children: meta }) : null, _jsx("span", { "aria-hidden": "true", className: slots.listItemCheck(), children: selected ? (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "m5 12.5 4.5 4.5L19 7.5" }) })) : null })] })),
|
|
341
|
+
}, props),
|
|
342
|
+
});
|
|
291
343
|
}
|
|
292
344
|
DialogListItem.displayName = 'Dialog.ListItem';
|
|
293
345
|
export const Dialog = Object.assign(DialogRoot, {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { type ComponentProps, type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
+
import type * as ReactTypes from 'react';
|
|
4
|
+
import type { ElementType as RenderElementType } from 'react';
|
|
5
|
+
/** Draws a part as another element, or composes it with a component: `render={<a href="/" />}`. */
|
|
6
|
+
type RenderProp<Tag extends RenderElementType> = {
|
|
7
|
+
render?: useRender.ComponentProps<Tag>['render'];
|
|
8
|
+
};
|
|
2
9
|
export interface FeedbackProps {
|
|
3
10
|
children?: ReactNode;
|
|
4
11
|
/** Whether the dialog is open, when the caller holds that. */
|
|
@@ -20,7 +27,7 @@ export interface FeedbackProps {
|
|
|
20
27
|
*/
|
|
21
28
|
dismissible?: boolean;
|
|
22
29
|
}
|
|
23
|
-
declare function FeedbackRoot({ children, open, onOpenChange, defaultOpen, value, defaultValue, onValueChange, dismissible, }: FeedbackProps):
|
|
30
|
+
declare function FeedbackRoot({ children, open, onOpenChange, defaultOpen, value, defaultValue, onValueChange, dismissible, }: FeedbackProps): ReactTypes.JSX.Element;
|
|
24
31
|
declare namespace FeedbackRoot {
|
|
25
32
|
var displayName: string;
|
|
26
33
|
}
|
|
@@ -33,7 +40,7 @@ export interface FeedbackTriggerProps extends Omit<ComponentProps<'button'>, 'ch
|
|
|
33
40
|
render?: ReactElement;
|
|
34
41
|
}
|
|
35
42
|
/** What opens the dialog. A plain button unless `render` supplies one. */
|
|
36
|
-
declare function FeedbackTrigger({ render, children, ...props }: FeedbackTriggerProps):
|
|
43
|
+
declare function FeedbackTrigger({ render, children, ...props }: FeedbackTriggerProps): ReactTypes.JSX.Element;
|
|
37
44
|
declare namespace FeedbackTrigger {
|
|
38
45
|
var displayName: string;
|
|
39
46
|
}
|
|
@@ -50,21 +57,21 @@ export interface FeedbackContentProps extends Omit<ComponentProps<'div'>, 'child
|
|
|
50
57
|
blur?: boolean;
|
|
51
58
|
}
|
|
52
59
|
/** The shell: the darker band, and everything set in it. */
|
|
53
|
-
declare function FeedbackContent({ inline, blur, className, children, ...props }: FeedbackContentProps):
|
|
60
|
+
declare function FeedbackContent({ inline, blur, className, children, ...props }: FeedbackContentProps): ReactTypes.JSX.Element;
|
|
54
61
|
declare namespace FeedbackContent {
|
|
55
62
|
var displayName: string;
|
|
56
63
|
}
|
|
57
64
|
export interface FeedbackPanelProps extends ComponentProps<'div'> {
|
|
58
65
|
}
|
|
59
66
|
/** The well set into the shell: the title, the close button and the field. */
|
|
60
|
-
declare function FeedbackPanel({ className, ...props }: FeedbackPanelProps):
|
|
67
|
+
declare function FeedbackPanel({ className, render, ...props }: FeedbackPanelProps & RenderProp<'div'>): ReactElement<unknown, string | ReactTypes.JSXElementConstructor<any>>;
|
|
61
68
|
declare namespace FeedbackPanel {
|
|
62
69
|
var displayName: string;
|
|
63
70
|
}
|
|
64
71
|
export interface FeedbackTitleProps extends ComponentProps<'h2'> {
|
|
65
72
|
}
|
|
66
73
|
/** The question. In a dialog it is also what the dialog is announced as. */
|
|
67
|
-
declare function FeedbackTitle({ className, ...props }: FeedbackTitleProps):
|
|
74
|
+
declare function FeedbackTitle({ className, ...props }: FeedbackTitleProps): ReactTypes.JSX.Element;
|
|
68
75
|
declare namespace FeedbackTitle {
|
|
69
76
|
var displayName: string;
|
|
70
77
|
}
|
|
@@ -73,7 +80,7 @@ export interface FeedbackCloseProps extends Omit<ComponentProps<'button'>, 'chil
|
|
|
73
80
|
label?: string;
|
|
74
81
|
}
|
|
75
82
|
/** The ✕ in the panel's corner. */
|
|
76
|
-
declare function FeedbackClose({ label, className, onClick, ...props }: FeedbackCloseProps):
|
|
83
|
+
declare function FeedbackClose({ label, className, onClick, render, ...props }: FeedbackCloseProps & RenderProp<'button'>): ReactElement<unknown, string | ReactTypes.JSXElementConstructor<any>>;
|
|
77
84
|
declare namespace FeedbackClose {
|
|
78
85
|
var displayName: string;
|
|
79
86
|
}
|
|
@@ -91,7 +98,7 @@ export interface FeedbackFieldProps extends Omit<ComponentProps<'textarea'>, 'va
|
|
|
91
98
|
* What is being written. No border and no background of its own: it is
|
|
92
99
|
* already inside a well.
|
|
93
100
|
*/
|
|
94
|
-
declare function FeedbackField({ value, onValueChange, minHeight, maxHeight, className, style, onKeyDown, ref, 'aria-label': ariaLabel, ...props }: FeedbackFieldProps):
|
|
101
|
+
declare function FeedbackField({ value, onValueChange, minHeight, maxHeight, className, style, onKeyDown, ref, 'aria-label': ariaLabel, ...props }: FeedbackFieldProps): ReactTypes.JSX.Element;
|
|
95
102
|
declare namespace FeedbackField {
|
|
96
103
|
var displayName: string;
|
|
97
104
|
}
|
|
@@ -101,7 +108,7 @@ export interface FeedbackDescriptionProps extends ComponentProps<'p'> {
|
|
|
101
108
|
* Body text in the panel, in place of the field: a thank-you once the message
|
|
102
109
|
* has gone. In a dialog it is also what the dialog is described as.
|
|
103
110
|
*/
|
|
104
|
-
declare function FeedbackDescription({ className, ...props }: FeedbackDescriptionProps):
|
|
111
|
+
declare function FeedbackDescription({ className, ...props }: FeedbackDescriptionProps): ReactTypes.JSX.Element;
|
|
105
112
|
declare namespace FeedbackDescription {
|
|
106
113
|
var displayName: string;
|
|
107
114
|
}
|
|
@@ -114,7 +121,7 @@ export interface FeedbackMessageProps extends ComponentProps<'p'> {
|
|
|
114
121
|
tone?: 'muted' | 'destructive';
|
|
115
122
|
}
|
|
116
123
|
/** A short line under the field. Figures in it keep their width as they change. */
|
|
117
|
-
declare function FeedbackMessage({ tone, className, ...props }: FeedbackMessageProps):
|
|
124
|
+
declare function FeedbackMessage({ tone, className, render, ...props }: FeedbackMessageProps & RenderProp<'p'>): ReactElement<unknown, string | ReactTypes.JSXElementConstructor<any>>;
|
|
118
125
|
declare namespace FeedbackMessage {
|
|
119
126
|
var displayName: string;
|
|
120
127
|
}
|
|
@@ -137,21 +144,21 @@ export interface FeedbackRatingProps extends Omit<ComponentProps<'div'>, 'childr
|
|
|
137
144
|
* A row of round buttons, one per score, with a word under each end. Use it
|
|
138
145
|
* as the first step of a dialog, and ask for the words once a score is in.
|
|
139
146
|
*/
|
|
140
|
-
declare function FeedbackRating({ max, onValueChange, startLabel, endLabel, minHeight, className, style, ...props }: FeedbackRatingProps):
|
|
147
|
+
declare function FeedbackRating({ max, onValueChange, startLabel, endLabel, minHeight, className, style, render, ...props }: FeedbackRatingProps & RenderProp<'div'>): ReactElement<unknown, string | ReactTypes.JSXElementConstructor<any>>;
|
|
141
148
|
declare namespace FeedbackRating {
|
|
142
149
|
var displayName: string;
|
|
143
150
|
}
|
|
144
151
|
export interface FeedbackFooterProps extends ComponentProps<'div'> {
|
|
145
152
|
}
|
|
146
153
|
/** The action row, in the band under the panel. */
|
|
147
|
-
declare function FeedbackFooter({ className, ...props }: FeedbackFooterProps):
|
|
154
|
+
declare function FeedbackFooter({ className, render, ...props }: FeedbackFooterProps & RenderProp<'div'>): ReactElement<unknown, string | ReactTypes.JSXElementConstructor<any>>;
|
|
148
155
|
declare namespace FeedbackFooter {
|
|
149
156
|
var displayName: string;
|
|
150
157
|
}
|
|
151
158
|
export interface FeedbackActionProps extends ComponentProps<'button'> {
|
|
152
159
|
}
|
|
153
160
|
/** Discards and closes. Call `preventDefault` in `onClick` to keep it open. */
|
|
154
|
-
declare function FeedbackCancel({ children, className, disabled, onClick, ...props }: FeedbackActionProps):
|
|
161
|
+
declare function FeedbackCancel({ children, className, disabled, onClick, render, ...props }: FeedbackActionProps & RenderProp<'button'>): ReactElement<unknown, string | ReactTypes.JSXElementConstructor<any>>;
|
|
155
162
|
declare namespace FeedbackCancel {
|
|
156
163
|
var displayName: string;
|
|
157
164
|
}
|
|
@@ -163,7 +170,7 @@ export interface FeedbackSubmitProps extends Omit<FeedbackActionProps, 'onSubmit
|
|
|
163
170
|
onSubmit?: (value: string) => void;
|
|
164
171
|
}
|
|
165
172
|
/** Sends. Inert while the field is empty, unless `disabled` says otherwise. */
|
|
166
|
-
declare function FeedbackSubmit({ children, className, disabled, onClick, onSubmit, ...props }: FeedbackSubmitProps):
|
|
173
|
+
declare function FeedbackSubmit({ children, className, disabled, onClick, onSubmit, render, ...props }: FeedbackSubmitProps & RenderProp<'button'>): ReactElement<unknown, string | ReactTypes.JSXElementConstructor<any>>;
|
|
167
174
|
declare namespace FeedbackSubmit {
|
|
168
175
|
var displayName: string;
|
|
169
176
|
}
|
|
@@ -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
|
* Feedback — a dialog whose body is something to write in, and whose actions
|
|
5
5
|
* sit in a band around it.
|
|
@@ -47,6 +47,8 @@ import { Dialog } from '@base-ui/react/dialog';
|
|
|
47
47
|
import { ScrollArea } from '@base-ui/react/scroll-area';
|
|
48
48
|
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
49
49
|
import { tv } from 'tailwind-variants';
|
|
50
|
+
import { mergeProps } from '@base-ui/react/merge-props';
|
|
51
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
50
52
|
const feedbackVariants = tv({
|
|
51
53
|
slots: {
|
|
52
54
|
backdrop: 'fixed inset-0 z-50 bg-black/40 transition-opacity duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0',
|
|
@@ -178,9 +180,13 @@ function FeedbackContent({ inline = false, blur = false, className, children, ..
|
|
|
178
180
|
}
|
|
179
181
|
FeedbackContent.displayName = 'Feedback.Content';
|
|
180
182
|
/** The well set into the shell: the title, the close button and the field. */
|
|
181
|
-
function FeedbackPanel({ className, ...props }) {
|
|
183
|
+
function FeedbackPanel({ className, render, ...props }) {
|
|
182
184
|
const slots = feedbackVariants();
|
|
183
|
-
return
|
|
185
|
+
return useRender({
|
|
186
|
+
defaultTagName: 'div',
|
|
187
|
+
render,
|
|
188
|
+
props: mergeProps({ className: slots.panel({ className }) }, props),
|
|
189
|
+
});
|
|
184
190
|
}
|
|
185
191
|
FeedbackPanel.displayName = 'Feedback.Panel';
|
|
186
192
|
/** The question. In a dialog it is also what the dialog is announced as. */
|
|
@@ -193,14 +199,24 @@ function FeedbackTitle({ className, ...props }) {
|
|
|
193
199
|
}
|
|
194
200
|
FeedbackTitle.displayName = 'Feedback.Title';
|
|
195
201
|
/** The ✕ in the panel's corner. */
|
|
196
|
-
function FeedbackClose({ label = 'Close', className, onClick, ...props }) {
|
|
202
|
+
function FeedbackClose({ label = 'Close', className, onClick, render, ...props }) {
|
|
197
203
|
const { setOpen } = useFeedback('Feedback.Close');
|
|
198
204
|
const slots = feedbackVariants();
|
|
199
|
-
return (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
return useRender({
|
|
206
|
+
defaultTagName: 'button',
|
|
207
|
+
render,
|
|
208
|
+
props: mergeProps({
|
|
209
|
+
type: 'button',
|
|
210
|
+
'aria-label': label,
|
|
211
|
+
className: slots.close({ className }),
|
|
212
|
+
onClick: (event) => {
|
|
213
|
+
onClick?.(event);
|
|
214
|
+
if (!event.defaultPrevented)
|
|
215
|
+
setOpen(false);
|
|
216
|
+
},
|
|
217
|
+
children: (_jsx(_Fragment, { children: _jsx("svg", { viewBox: "0 0 10 10", "aria-hidden": "true", className: "size-2.5", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", children: _jsx("path", { d: "M2 2l6 6M8 2l-6 6" }) }) })),
|
|
218
|
+
}, props),
|
|
219
|
+
});
|
|
204
220
|
}
|
|
205
221
|
FeedbackClose.displayName = 'Feedback.Close';
|
|
206
222
|
/**
|
|
@@ -258,39 +274,69 @@ function FeedbackDescription({ className, ...props }) {
|
|
|
258
274
|
}
|
|
259
275
|
FeedbackDescription.displayName = 'Feedback.Description';
|
|
260
276
|
/** A short line under the field. Figures in it keep their width as they change. */
|
|
261
|
-
function FeedbackMessage({ tone = 'muted', className, ...props }) {
|
|
277
|
+
function FeedbackMessage({ tone = 'muted', className, render, ...props }) {
|
|
262
278
|
const slots = feedbackVariants({ messageTone: tone });
|
|
263
|
-
return (
|
|
279
|
+
return useRender({
|
|
280
|
+
defaultTagName: 'p',
|
|
281
|
+
render,
|
|
282
|
+
props: mergeProps({
|
|
283
|
+
role: tone === 'destructive' ? 'alert' : undefined,
|
|
284
|
+
'aria-live': tone === 'destructive' ? undefined : 'polite',
|
|
285
|
+
className: slots.message({ className }),
|
|
286
|
+
}, props),
|
|
287
|
+
});
|
|
264
288
|
}
|
|
265
289
|
FeedbackMessage.displayName = 'Feedback.Message';
|
|
266
290
|
/**
|
|
267
291
|
* A row of round buttons, one per score, with a word under each end. Use it
|
|
268
292
|
* as the first step of a dialog, and ask for the words once a score is in.
|
|
269
293
|
*/
|
|
270
|
-
function FeedbackRating({ max = 5, onValueChange, startLabel, endLabel, minHeight, className, style, ...props }) {
|
|
294
|
+
function FeedbackRating({ max = 5, onValueChange, startLabel, endLabel, minHeight, className, style, render, ...props }) {
|
|
271
295
|
const slots = feedbackVariants();
|
|
272
|
-
return (
|
|
296
|
+
return useRender({
|
|
297
|
+
defaultTagName: 'div',
|
|
298
|
+
render,
|
|
299
|
+
props: mergeProps({
|
|
300
|
+
className: slots.rating({ className }),
|
|
301
|
+
style: { minHeight, ...style },
|
|
302
|
+
children: (_jsxs(_Fragment, { children: [_jsx("div", { className: slots.ratingScores(), children: Array.from({ length: max }, (_, index) => index + 1).map((value) => (_jsx("button", { type: "button", "aria-label": `${value} out of ${max}`, onClick: () => onValueChange?.(value), className: slots.ratingScore(), children: value }, value))) }), startLabel !== undefined || endLabel !== undefined ? (_jsxs("div", { className: slots.ratingEnds(), children: [_jsx("span", { children: startLabel }), _jsx("span", { children: endLabel })] })) : null] })),
|
|
303
|
+
}, props),
|
|
304
|
+
});
|
|
273
305
|
}
|
|
274
306
|
FeedbackRating.displayName = 'Feedback.Rating';
|
|
275
307
|
/** The action row, in the band under the panel. */
|
|
276
|
-
function FeedbackFooter({ className, ...props }) {
|
|
308
|
+
function FeedbackFooter({ className, render, ...props }) {
|
|
277
309
|
const slots = feedbackVariants();
|
|
278
|
-
return
|
|
310
|
+
return useRender({
|
|
311
|
+
defaultTagName: 'div',
|
|
312
|
+
render,
|
|
313
|
+
props: mergeProps({ className: slots.footer({ className }) }, props),
|
|
314
|
+
});
|
|
279
315
|
}
|
|
280
316
|
FeedbackFooter.displayName = 'Feedback.Footer';
|
|
281
317
|
/** Discards and closes. Call `preventDefault` in `onClick` to keep it open. */
|
|
282
|
-
function FeedbackCancel({ children = 'Cancel', className, disabled, onClick, ...props }) {
|
|
318
|
+
function FeedbackCancel({ children = 'Cancel', className, disabled, onClick, render, ...props }) {
|
|
283
319
|
const { setOpen } = useFeedback('Feedback.Cancel');
|
|
284
320
|
const slots = feedbackVariants({ tone: 'cancel', disabled: !!disabled });
|
|
285
|
-
return (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
321
|
+
return useRender({
|
|
322
|
+
defaultTagName: 'button',
|
|
323
|
+
render,
|
|
324
|
+
props: mergeProps({
|
|
325
|
+
type: 'button',
|
|
326
|
+
disabled: disabled,
|
|
327
|
+
className: slots.action({ className }),
|
|
328
|
+
onClick: (event) => {
|
|
329
|
+
onClick?.(event);
|
|
330
|
+
if (!event.defaultPrevented)
|
|
331
|
+
setOpen(false);
|
|
332
|
+
},
|
|
333
|
+
children: _jsx(_Fragment, { children: children }),
|
|
334
|
+
}, props),
|
|
335
|
+
});
|
|
290
336
|
}
|
|
291
337
|
FeedbackCancel.displayName = 'Feedback.Cancel';
|
|
292
338
|
/** Sends. Inert while the field is empty, unless `disabled` says otherwise. */
|
|
293
|
-
function FeedbackSubmit({ children = 'Submit', className, disabled, onClick, onSubmit, ...props }) {
|
|
339
|
+
function FeedbackSubmit({ children = 'Submit', className, disabled, onClick, onSubmit, render, ...props }) {
|
|
294
340
|
const feedback = useFeedback('Feedback.Submit');
|
|
295
341
|
// Empty feedback is worse than none: it is sent by somebody who believes
|
|
296
342
|
// they said something.
|
|
@@ -299,11 +345,21 @@ function FeedbackSubmit({ children = 'Submit', className, disabled, onClick, onS
|
|
|
299
345
|
// What the field's shortcut calls. Kept current on every render, so it
|
|
300
346
|
// sends what is in the field now and not what was there when it mounted.
|
|
301
347
|
feedback.submit.current = inert ? null : () => onSubmit?.(feedback.value);
|
|
302
|
-
return (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
348
|
+
return useRender({
|
|
349
|
+
defaultTagName: 'button',
|
|
350
|
+
render,
|
|
351
|
+
props: mergeProps({
|
|
352
|
+
type: 'button',
|
|
353
|
+
disabled: inert,
|
|
354
|
+
className: slots.action({ className }),
|
|
355
|
+
onClick: (event) => {
|
|
356
|
+
onClick?.(event);
|
|
357
|
+
if (!event.defaultPrevented)
|
|
358
|
+
onSubmit?.(feedback.value);
|
|
359
|
+
},
|
|
360
|
+
children: _jsx(_Fragment, { children: children }),
|
|
361
|
+
}, props),
|
|
362
|
+
});
|
|
307
363
|
}
|
|
308
364
|
FeedbackSubmit.displayName = 'Feedback.Submit';
|
|
309
365
|
export const Feedback = Object.assign(FeedbackRoot, {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field — a form control with its label, a line of help, and the reason it was refused.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* <Field name="email">
|
|
6
|
+
* <Field.Label>Work email</Field.Label>
|
|
7
|
+
* <Input type="email" required placeholder="you@company.com" />
|
|
8
|
+
* <Field.Description>We send the receipt here.</Field.Description>
|
|
9
|
+
* <Field.Error match="valueMissing">Enter an email address.</Field.Error>
|
|
10
|
+
* </Field>
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* ## The ids are wired for you
|
|
14
|
+
*
|
|
15
|
+
* A label has to point at its control, and the control has to point at the
|
|
16
|
+
* help and the error, or a screen reader reads the field with no name and no
|
|
17
|
+
* reason. Field does that wiring: `Field.Label` is tied to the control inside
|
|
18
|
+
* the field, and the control is described by `Field.Description` and the
|
|
19
|
+
* error that is showing. Input, Textarea, Select, Checkbox, Radio Group and
|
|
20
|
+
* Switch all take part, with nothing passed.
|
|
21
|
+
*
|
|
22
|
+
* ## The label is a Label
|
|
23
|
+
*
|
|
24
|
+
* `Field.Label` is the Label component, so `required`, `optional`,
|
|
25
|
+
* `Label.Hint` and `Label.Action` work in it as they do anywhere else. It
|
|
26
|
+
* turns to the destructive colour and fades with the field, without
|
|
27
|
+
* `invalid` or `disabled` passed to it.
|
|
28
|
+
*
|
|
29
|
+
* ## Errors wait for the reader
|
|
30
|
+
*
|
|
31
|
+
* The browser's own checks — `required`, `type="email"`, `minLength` — run
|
|
32
|
+
* when the control is left, not on every key, so a field is not red while it
|
|
33
|
+
* is still being typed. `Field.Error` with `match` shows a message for one of
|
|
34
|
+
* those checks; with no `match`, it shows whenever the field is invalid,
|
|
35
|
+
* which is the one to use with `invalid` set from a server's answer. An
|
|
36
|
+
* error that appears slides in under the control, and the description it
|
|
37
|
+
* replaces steps aside.
|
|
38
|
+
*/
|
|
39
|
+
import { Field as BaseField } from '@base-ui/react/field';
|
|
40
|
+
import { type LabelProps } from '../label/index.js';
|
|
41
|
+
export interface FieldProps extends Omit<BaseField.Root.Props, 'className'> {
|
|
42
|
+
className?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Groups a control with its label, help and error. Takes `name`, `invalid`
|
|
46
|
+
* (for an error from outside, such as a server), `disabled`, and `validate`
|
|
47
|
+
* for a check of your own.
|
|
48
|
+
*/
|
|
49
|
+
declare function FieldRoot({ className, ...props }: FieldProps): import("react").JSX.Element;
|
|
50
|
+
declare namespace FieldRoot {
|
|
51
|
+
var displayName: string;
|
|
52
|
+
}
|
|
53
|
+
export interface FieldLabelProps extends Omit<LabelProps, 'invalid' | 'disabled' | 'htmlFor'> {
|
|
54
|
+
}
|
|
55
|
+
/** The control's name, tied to it. Takes `required` and `optional` as Label does, and its parts. */
|
|
56
|
+
declare function FieldLabel({ required, optional, className, children, ...props }: FieldLabelProps): import("react").JSX.Element;
|
|
57
|
+
declare namespace FieldLabel {
|
|
58
|
+
var displayName: string;
|
|
59
|
+
}
|
|
60
|
+
export interface FieldDescriptionProps extends Omit<BaseField.Description.Props, 'className'> {
|
|
61
|
+
className?: string;
|
|
62
|
+
}
|
|
63
|
+
/** A line of help under the control. Hidden while an error is showing. */
|
|
64
|
+
declare function FieldDescription({ className, ...props }: FieldDescriptionProps): import("react").JSX.Element;
|
|
65
|
+
declare namespace FieldDescription {
|
|
66
|
+
var displayName: string;
|
|
67
|
+
}
|
|
68
|
+
export interface FieldErrorProps extends Omit<BaseField.Error.Props, 'className'> {
|
|
69
|
+
className?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Why the value was refused. `match` names the check it is for, such as
|
|
73
|
+
* `valueMissing` or `typeMismatch`; with none, it shows whenever the field is
|
|
74
|
+
* invalid. With no children it shows the browser's own message.
|
|
75
|
+
*/
|
|
76
|
+
declare function FieldError({ className, ...props }: FieldErrorProps): import("react").JSX.Element;
|
|
77
|
+
declare namespace FieldError {
|
|
78
|
+
var displayName: string;
|
|
79
|
+
}
|
|
80
|
+
export declare const Field: typeof FieldRoot & {
|
|
81
|
+
Label: typeof FieldLabel;
|
|
82
|
+
Description: typeof FieldDescription;
|
|
83
|
+
Error: typeof FieldError;
|
|
84
|
+
};
|
|
85
|
+
export { FieldLabel, FieldDescription, FieldError };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Field — a form control with its label, a line of help, and the reason it was refused.
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* <Field name="email">
|
|
7
|
+
* <Field.Label>Work email</Field.Label>
|
|
8
|
+
* <Input type="email" required placeholder="you@company.com" />
|
|
9
|
+
* <Field.Description>We send the receipt here.</Field.Description>
|
|
10
|
+
* <Field.Error match="valueMissing">Enter an email address.</Field.Error>
|
|
11
|
+
* </Field>
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* ## The ids are wired for you
|
|
15
|
+
*
|
|
16
|
+
* A label has to point at its control, and the control has to point at the
|
|
17
|
+
* help and the error, or a screen reader reads the field with no name and no
|
|
18
|
+
* reason. Field does that wiring: `Field.Label` is tied to the control inside
|
|
19
|
+
* the field, and the control is described by `Field.Description` and the
|
|
20
|
+
* error that is showing. Input, Textarea, Select, Checkbox, Radio Group and
|
|
21
|
+
* Switch all take part, with nothing passed.
|
|
22
|
+
*
|
|
23
|
+
* ## The label is a Label
|
|
24
|
+
*
|
|
25
|
+
* `Field.Label` is the Label component, so `required`, `optional`,
|
|
26
|
+
* `Label.Hint` and `Label.Action` work in it as they do anywhere else. It
|
|
27
|
+
* turns to the destructive colour and fades with the field, without
|
|
28
|
+
* `invalid` or `disabled` passed to it.
|
|
29
|
+
*
|
|
30
|
+
* ## Errors wait for the reader
|
|
31
|
+
*
|
|
32
|
+
* The browser's own checks — `required`, `type="email"`, `minLength` — run
|
|
33
|
+
* when the control is left, not on every key, so a field is not red while it
|
|
34
|
+
* is still being typed. `Field.Error` with `match` shows a message for one of
|
|
35
|
+
* those checks; with no `match`, it shows whenever the field is invalid,
|
|
36
|
+
* which is the one to use with `invalid` set from a server's answer. An
|
|
37
|
+
* error that appears slides in under the control, and the description it
|
|
38
|
+
* replaces steps aside.
|
|
39
|
+
*/
|
|
40
|
+
import { Field as BaseField } from '@base-ui/react/field';
|
|
41
|
+
import { tv } from 'tailwind-variants';
|
|
42
|
+
import { Label } from '../label/index.js';
|
|
43
|
+
const fieldVariants = tv({
|
|
44
|
+
slots: {
|
|
45
|
+
root: 'group/field flex w-full min-w-0 flex-col gap-2',
|
|
46
|
+
description: [
|
|
47
|
+
'text-xs leading-4 text-muted-foreground',
|
|
48
|
+
// An error showing says more than the help line, so the help steps aside.
|
|
49
|
+
'group-has-[[data-slot=field-error]]/field:hidden',
|
|
50
|
+
],
|
|
51
|
+
error: [
|
|
52
|
+
'text-xs leading-4 text-destructive',
|
|
53
|
+
'transition-[opacity,translate] duration-200 starting:-translate-y-1 starting:opacity-0 motion-reduce:transition-none',
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* Groups a control with its label, help and error. Takes `name`, `invalid`
|
|
59
|
+
* (for an error from outside, such as a server), `disabled`, and `validate`
|
|
60
|
+
* for a check of your own.
|
|
61
|
+
*/
|
|
62
|
+
function FieldRoot({ className, ...props }) {
|
|
63
|
+
return _jsx(BaseField.Root, { "data-slot": "field", className: fieldVariants().root({ className }), ...props });
|
|
64
|
+
}
|
|
65
|
+
FieldRoot.displayName = 'Field';
|
|
66
|
+
/** The control's name, tied to it. Takes `required` and `optional` as Label does, and its parts. */
|
|
67
|
+
function FieldLabel({ required, optional, className, children, ...props }) {
|
|
68
|
+
return (_jsx(BaseField.Label, { "data-slot": "field-label", className: className, render: (labelProps) => _jsx(Label, { ...labelProps, required: required, optional: optional }), ...props, children: children }));
|
|
69
|
+
}
|
|
70
|
+
FieldLabel.displayName = 'Field.Label';
|
|
71
|
+
/** A line of help under the control. Hidden while an error is showing. */
|
|
72
|
+
function FieldDescription({ className, ...props }) {
|
|
73
|
+
return (_jsx(BaseField.Description, { "data-slot": "field-description", className: fieldVariants().description({ className }), ...props }));
|
|
74
|
+
}
|
|
75
|
+
FieldDescription.displayName = 'Field.Description';
|
|
76
|
+
/**
|
|
77
|
+
* Why the value was refused. `match` names the check it is for, such as
|
|
78
|
+
* `valueMissing` or `typeMismatch`; with none, it shows whenever the field is
|
|
79
|
+
* invalid. With no children it shows the browser's own message.
|
|
80
|
+
*/
|
|
81
|
+
function FieldError({ className, ...props }) {
|
|
82
|
+
return _jsx(BaseField.Error, { "data-slot": "field-error", className: fieldVariants().error({ className }), ...props });
|
|
83
|
+
}
|
|
84
|
+
FieldError.displayName = 'Field.Error';
|
|
85
|
+
export const Field = Object.assign(FieldRoot, {
|
|
86
|
+
Label: FieldLabel,
|
|
87
|
+
Description: FieldDescription,
|
|
88
|
+
Error: FieldError,
|
|
89
|
+
});
|
|
90
|
+
export { FieldLabel, FieldDescription, FieldError };
|