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
|
@@ -68,6 +68,8 @@ import { Button } from '../button/index.js';
|
|
|
68
68
|
import { Menu } from '../menu/index.js';
|
|
69
69
|
import { Select } from '../select/index.js';
|
|
70
70
|
import { Textarea } from '../textarea/index.js';
|
|
71
|
+
import { mergeProps } from '@base-ui/react/merge-props';
|
|
72
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
71
73
|
const promptVariants = tv({
|
|
72
74
|
slots: {
|
|
73
75
|
root: 'isolate flex w-full flex-col',
|
|
@@ -120,10 +122,7 @@ const promptVariants = tv({
|
|
|
120
122
|
// The plus turns to a cross while its menu is open.
|
|
121
123
|
menuTrigger: 'data-popup-open:bg-accent [&_svg]:transition-transform [&_svg]:duration-200 data-popup-open:[&_svg]:rotate-45 motion-reduce:[&_svg]:transition-none',
|
|
122
124
|
modelValue: 'flex min-w-0 items-center gap-2 [&_svg]:size-4',
|
|
123
|
-
submit: [
|
|
124
|
-
'relative size-10 shrink-0 overflow-visible rounded-full p-0',
|
|
125
|
-
'disabled:opacity-40',
|
|
126
|
-
],
|
|
125
|
+
submit: ['relative size-10 shrink-0 overflow-visible rounded-full p-0', 'disabled:opacity-40'],
|
|
127
126
|
submitIcon: 'absolute inset-0 m-auto flex items-center justify-center transition-[opacity,scale,rotate] duration-200 ease-out motion-reduce:transition-none [&_svg]:size-[18px]',
|
|
128
127
|
submitRing: 'pointer-events-none absolute -inset-[3px] size-[calc(100%+6px)] text-primary transition-opacity duration-200',
|
|
129
128
|
statusText: 'min-w-0 truncate bg-[linear-gradient(90deg,var(--muted-foreground)_35%,var(--foreground)_50%,var(--muted-foreground)_65%)] bg-[length:250%_100%] bg-clip-text text-transparent',
|
|
@@ -298,7 +297,25 @@ function PromptRoot({ value: controlledValue, defaultValue = '', onValueChange,
|
|
|
298
297
|
keyHandler,
|
|
299
298
|
listbox,
|
|
300
299
|
setListbox,
|
|
301
|
-
}), [
|
|
300
|
+
}), [
|
|
301
|
+
value,
|
|
302
|
+
setValue,
|
|
303
|
+
attachments,
|
|
304
|
+
addFiles,
|
|
305
|
+
removeAttachment,
|
|
306
|
+
openFileDialog,
|
|
307
|
+
status,
|
|
308
|
+
mode,
|
|
309
|
+
busy,
|
|
310
|
+
disabled,
|
|
311
|
+
empty,
|
|
312
|
+
submit,
|
|
313
|
+
stop,
|
|
314
|
+
onStop,
|
|
315
|
+
dragging,
|
|
316
|
+
fieldId,
|
|
317
|
+
listbox,
|
|
318
|
+
]);
|
|
302
319
|
const send = (event) => {
|
|
303
320
|
event.preventDefault();
|
|
304
321
|
submit();
|
|
@@ -372,55 +389,88 @@ function PromptStatus({ open, mode, icon, spinner = true, children, ...props })
|
|
|
372
389
|
}
|
|
373
390
|
PromptStatus.displayName = 'Prompt.Status';
|
|
374
391
|
/** A strip under the field, inset from its edges, for where the message is going: a branch, a folder, a ticket. */
|
|
375
|
-
function PromptContext({ open, mode, className, ...props }) {
|
|
392
|
+
function PromptContext({ open, mode, className, render, ...props }) {
|
|
376
393
|
const shown = useBandOpen('Prompt.Context', open, mode, () => true);
|
|
377
394
|
const slots = promptVariants();
|
|
378
395
|
const clip = useRef(null);
|
|
379
396
|
useEffect(() => {
|
|
380
397
|
clip.current?.toggleAttribute('inert', !shown);
|
|
381
398
|
}, [shown]);
|
|
382
|
-
|
|
399
|
+
const element = useRender({
|
|
400
|
+
defaultTagName: 'div',
|
|
401
|
+
render,
|
|
402
|
+
props: mergeProps({ className: slots.context({ className }) }, props),
|
|
403
|
+
});
|
|
404
|
+
return (_jsx("div", { "data-slot": "prompt-context", "data-open": shown ? '' : undefined, className: slots.contextFold({
|
|
405
|
+
className: shown ? '-mt-5 grid-rows-[1fr] opacity-100' : 'mt-0 grid-rows-[0fr] opacity-0',
|
|
406
|
+
}), children: _jsx("div", { ref: clip, className: "min-h-0 overflow-hidden", children: element }) }));
|
|
383
407
|
}
|
|
384
408
|
PromptContext.displayName = 'Prompt.Context';
|
|
385
409
|
/** One fact in the context strip, with an icon before it. */
|
|
386
|
-
function PromptContextItem({ onSelect, className, children, ...props }) {
|
|
410
|
+
function PromptContextItem({ onSelect, className, children, render, ...props }) {
|
|
387
411
|
const slots = promptVariants();
|
|
388
412
|
if (onSelect) {
|
|
389
413
|
return (_jsx("button", { type: "button", "data-slot": "prompt-context-item", onClick: onSelect, className: slots.contextItem({ className: [slots.contextButton(), className] }), children: children }));
|
|
390
414
|
}
|
|
391
|
-
return (
|
|
415
|
+
return useRender({
|
|
416
|
+
defaultTagName: 'span',
|
|
417
|
+
render,
|
|
418
|
+
props: mergeProps({ 'data-slot': 'prompt-context-item', className: slots.contextItem({ className }), children: _jsx(_Fragment, { children: children }) }, props),
|
|
419
|
+
});
|
|
392
420
|
}
|
|
393
421
|
PromptContextItem.displayName = 'Prompt.ContextItem';
|
|
394
422
|
/** A quiet line under the prompt. Keys written in `<kbd>` are drawn as keys. */
|
|
395
|
-
function PromptHint({ className, children, ...props }) {
|
|
396
|
-
return (
|
|
423
|
+
function PromptHint({ className, children, render, ...props }) {
|
|
424
|
+
return useRender({
|
|
425
|
+
defaultTagName: 'p',
|
|
426
|
+
render,
|
|
427
|
+
props: mergeProps({
|
|
428
|
+
'data-slot': 'prompt-hint',
|
|
429
|
+
className: promptVariants().hint({ className }),
|
|
430
|
+
children: (_jsx(_Fragment, { children: children ?? (_jsxs(_Fragment, { children: [_jsx("kbd", { children: "Shift" }), " + ", _jsx("kbd", { children: "Enter" }), " for a new line"] })) })),
|
|
431
|
+
}, props),
|
|
432
|
+
});
|
|
397
433
|
}
|
|
398
434
|
PromptHint.displayName = 'Prompt.Hint';
|
|
399
435
|
/** The card that holds the text, the files and the footer. Files can be dropped on it. */
|
|
400
|
-
function PromptField({ layout = 'stacked', className, onDragOver, onDragLeave, onDrop, ...props }) {
|
|
436
|
+
function PromptField({ layout = 'stacked', className, onDragOver, onDragLeave, onDrop, render, ...props }) {
|
|
401
437
|
const prompt = usePrompt('Prompt.Field');
|
|
402
438
|
const hasFiles = (event) => [...event.dataTransfer.types].includes('Files');
|
|
403
|
-
return (
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
439
|
+
return useRender({
|
|
440
|
+
defaultTagName: 'div',
|
|
441
|
+
render,
|
|
442
|
+
props: mergeProps({
|
|
443
|
+
ref: prompt.fieldRef,
|
|
444
|
+
'data-slot': 'prompt-field',
|
|
445
|
+
'data-layout': layout,
|
|
446
|
+
'data-status': prompt.status,
|
|
447
|
+
'data-dragging': prompt.dragging ? '' : undefined,
|
|
448
|
+
className: promptVariants().field({ className }),
|
|
449
|
+
onDragOver: (event) => {
|
|
450
|
+
onDragOver?.(event);
|
|
451
|
+
if (prompt.disabled || !hasFiles(event))
|
|
452
|
+
return;
|
|
453
|
+
event.preventDefault();
|
|
454
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
455
|
+
if (!prompt.dragging)
|
|
456
|
+
prompt.setDragging(true);
|
|
457
|
+
},
|
|
458
|
+
onDragLeave: (event) => {
|
|
459
|
+
onDragLeave?.(event);
|
|
460
|
+
// Leaving for a child of the field is not leaving the field.
|
|
461
|
+
if (!event.currentTarget.contains(event.relatedTarget))
|
|
462
|
+
prompt.setDragging(false);
|
|
463
|
+
},
|
|
464
|
+
onDrop: (event) => {
|
|
465
|
+
onDrop?.(event);
|
|
415
466
|
prompt.setDragging(false);
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}, ...props }));
|
|
467
|
+
if (prompt.disabled || !hasFiles(event))
|
|
468
|
+
return;
|
|
469
|
+
event.preventDefault();
|
|
470
|
+
prompt.addFiles(event.dataTransfer.files);
|
|
471
|
+
},
|
|
472
|
+
}, props),
|
|
473
|
+
});
|
|
424
474
|
}
|
|
425
475
|
PromptField.displayName = 'Prompt.Field';
|
|
426
476
|
/** Where the message is written. Enter sends, Shift+Enter starts a new line, and pasted files are attached. */
|
|
@@ -462,22 +512,39 @@ function formatSize(bytes) {
|
|
|
462
512
|
}
|
|
463
513
|
const XIcon = () => (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", children: _jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }));
|
|
464
514
|
/** The files attached so far: images as thumbnails, anything else as a chip. Takes no room while there are none. */
|
|
465
|
-
function PromptAttachments({ className, 'aria-label': ariaLabel = 'Attached files', ...props }) {
|
|
515
|
+
function PromptAttachments({ className, 'aria-label': ariaLabel = 'Attached files', render, ...props }) {
|
|
466
516
|
const prompt = usePrompt('Prompt.Attachments');
|
|
467
517
|
const slots = promptVariants();
|
|
468
518
|
if (prompt.attachments.length === 0)
|
|
469
519
|
return null;
|
|
470
|
-
return (
|
|
520
|
+
return useRender({
|
|
521
|
+
defaultTagName: 'ul',
|
|
522
|
+
render,
|
|
523
|
+
props: mergeProps({
|
|
524
|
+
'data-slot': 'prompt-attachments',
|
|
525
|
+
'aria-label': ariaLabel,
|
|
526
|
+
className: slots.attachments({ className }),
|
|
527
|
+
children: (_jsx(_Fragment, { children: prompt.attachments.map(({ id, file, preview }) => (_jsxs("li", { className: slots.attachment({ className: preview ? slots.attachmentImage() : slots.attachmentFile() }), children: [preview ? (_jsx("img", { src: preview, alt: file.name, className: "size-full rounded-[inherit] object-cover" })) : (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", className: slots.attachmentGlyph(), children: _jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z" }), _jsx("path", { d: "M14 3v5h5" })] }) }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: slots.attachmentName(), children: file.name }), _jsx("span", { className: slots.attachmentMeta(), children: formatSize(file.size) })] })] })), _jsx("button", { type: "button", "aria-label": `Remove ${file.name}`, className: slots.attachmentRemove(), onClick: () => prompt.removeAttachment(id), children: _jsx(XIcon, {}) })] }, id))) })),
|
|
528
|
+
}, props),
|
|
529
|
+
});
|
|
471
530
|
}
|
|
472
531
|
PromptAttachments.displayName = 'Prompt.Attachments';
|
|
473
532
|
/** The row under the text: tools at the start, the submit button at the end. */
|
|
474
|
-
function PromptFooter({ className, ...props }) {
|
|
475
|
-
return
|
|
533
|
+
function PromptFooter({ className, render, ...props }) {
|
|
534
|
+
return useRender({
|
|
535
|
+
defaultTagName: 'div',
|
|
536
|
+
render,
|
|
537
|
+
props: mergeProps({ 'data-slot': 'prompt-footer', className: promptVariants().footer({ className }) }, props),
|
|
538
|
+
});
|
|
476
539
|
}
|
|
477
540
|
PromptFooter.displayName = 'Prompt.Footer';
|
|
478
541
|
/** The tools at the start of the footer. Wraps onto a second line where it has to. */
|
|
479
|
-
function PromptTools({ className, ...props }) {
|
|
480
|
-
return
|
|
542
|
+
function PromptTools({ className, render, ...props }) {
|
|
543
|
+
return useRender({
|
|
544
|
+
defaultTagName: 'div',
|
|
545
|
+
render,
|
|
546
|
+
props: mergeProps({ 'data-slot': 'prompt-tools', className: promptVariants().tools({ className }) }, props),
|
|
547
|
+
});
|
|
481
548
|
}
|
|
482
549
|
PromptTools.displayName = 'Prompt.Tools';
|
|
483
550
|
/** A round button in the footer, holding an icon. With `pressed`, it switches on and off. */
|
|
@@ -521,7 +588,9 @@ const PaperclipIcon = () => (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0
|
|
|
521
588
|
*/
|
|
522
589
|
function PromptMenu({ label = 'Add', icon, children, side = 'top', align = 'start', width = 'auto', className, ...props }) {
|
|
523
590
|
const prompt = usePrompt('Prompt.Menu');
|
|
524
|
-
return (_jsxs(Menu, { children: [_jsx(Menu.Trigger, { disabled: prompt.disabled, "aria-label": label, title: label, render: _jsx(Button, { "data-slot": "prompt-menu", variant: "outline", shape: "pill", size: "icon" }), className: promptVariants().menuTrigger(), children: icon ?? (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: _jsx("path", { d: "M12 5v14M5 12h14" }) })) }), _jsx(Menu.Content, { side: side, align: align, anchor: width === 'field' ? prompt.fieldRef : undefined, className: [width === 'field' ? 'w-(--anchor-width) rounded-2xl p-1.5' : undefined, className]
|
|
591
|
+
return (_jsxs(Menu, { children: [_jsx(Menu.Trigger, { disabled: prompt.disabled, "aria-label": label, title: label, render: _jsx(Button, { "data-slot": "prompt-menu", variant: "outline", shape: "pill", size: "icon" }), className: promptVariants().menuTrigger(), children: icon ?? (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: _jsx("path", { d: "M12 5v14M5 12h14" }) })) }), _jsx(Menu.Content, { side: side, align: align, anchor: width === 'field' ? prompt.fieldRef : undefined, className: [width === 'field' ? 'w-(--anchor-width) rounded-2xl p-1.5' : undefined, className]
|
|
592
|
+
.filter(Boolean)
|
|
593
|
+
.join(' ') || undefined, ...props, children: children })] }));
|
|
525
594
|
}
|
|
526
595
|
PromptMenu.displayName = 'Prompt.Menu';
|
|
527
596
|
/** The menu item that opens the file picker. */
|
|
@@ -627,16 +696,30 @@ function PromptTasks({ open, mode, title, expanded, defaultExpanded = false, onE
|
|
|
627
696
|
}
|
|
628
697
|
PromptTasks.displayName = 'Prompt.Tasks';
|
|
629
698
|
/** One task in `Prompt.Tasks`. */
|
|
630
|
-
function PromptTask({ status = 'pending', className, children, ...props }) {
|
|
699
|
+
function PromptTask({ status = 'pending', className, children, render, ...props }) {
|
|
631
700
|
const slots = promptVariants();
|
|
632
701
|
const text = useShimmer(status === 'active');
|
|
633
|
-
return (
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
702
|
+
return useRender({
|
|
703
|
+
defaultTagName: 'li',
|
|
704
|
+
render,
|
|
705
|
+
props: mergeProps({
|
|
706
|
+
'data-status': status,
|
|
707
|
+
className: slots.task({ className }),
|
|
708
|
+
children: (_jsxs(_Fragment, { children: [_jsx("span", { className: slots.taskMark(), children: status === 'done' ? (_jsx(CheckCircle, { className: slots.taskDone() })) : status === 'active' ? (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.75", strokeLinecap: "round", className: "size-3.5 animate-spin text-foreground motion-reduce:animate-none", children: _jsx("path", { d: "M21 12a9 9 0 1 1-6.22-8.56" }) })) : status === 'error' ? (_jsx("span", { className: slots.taskError(), children: "!" })) : (_jsx("span", { className: slots.taskPending() })) }), _jsx("span", { ref: text, className: status === 'active'
|
|
709
|
+
? slots.statusText({ className: 'whitespace-normal' })
|
|
710
|
+
: status === 'done'
|
|
711
|
+
? 'text-muted-foreground line-through decoration-muted-foreground/40'
|
|
712
|
+
: status === 'error'
|
|
713
|
+
? 'text-destructive'
|
|
714
|
+
: 'text-muted-foreground', children: children }), _jsx("span", { className: "sr-only", children: status === 'done'
|
|
715
|
+
? ', done'
|
|
716
|
+
: status === 'active'
|
|
717
|
+
? ', in progress'
|
|
718
|
+
: status === 'error'
|
|
719
|
+
? ', failed'
|
|
720
|
+
: '' })] })),
|
|
721
|
+
}, props),
|
|
722
|
+
});
|
|
640
723
|
}
|
|
641
724
|
PromptTask.displayName = 'Prompt.Task';
|
|
642
725
|
/**
|
|
@@ -644,18 +727,23 @@ PromptTask.displayName = 'Prompt.Task';
|
|
|
644
727
|
* word, `/sum`. The arrow keys move through it, Enter or Tab chooses, Escape
|
|
645
728
|
* puts it away.
|
|
646
729
|
*/
|
|
647
|
-
function PromptCommands({ items, trigger = '/', onSelect, label = 'Commands', maxHeight = '12rem' }) {
|
|
730
|
+
function PromptCommands({ items, trigger = '/', onSelect, label = 'Commands', maxHeight = '12rem', }) {
|
|
648
731
|
const prompt = usePrompt('Prompt.Commands');
|
|
649
732
|
const slots = promptVariants();
|
|
650
733
|
const listId = useId();
|
|
651
734
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
652
735
|
const [dismissed, setDismissed] = useState(null);
|
|
653
736
|
const list = useRef(null);
|
|
654
|
-
const query = prompt.value.startsWith(trigger) && !/\s/.test(prompt.value)
|
|
737
|
+
const query = prompt.value.startsWith(trigger) && !/\s/.test(prompt.value)
|
|
738
|
+
? prompt.value.slice(trigger.length).toLowerCase()
|
|
739
|
+
: null;
|
|
655
740
|
const matches = query === null
|
|
656
741
|
? []
|
|
657
742
|
: items
|
|
658
|
-
.filter((item) => item.value.toLowerCase().includes(query) ||
|
|
743
|
+
.filter((item) => item.value.toLowerCase().includes(query) ||
|
|
744
|
+
String(item.label ?? '')
|
|
745
|
+
.toLowerCase()
|
|
746
|
+
.includes(query))
|
|
659
747
|
// Those whose name starts with what was typed come first.
|
|
660
748
|
.sort((a, b) => Number(!a.value.toLowerCase().startsWith(query)) - Number(!b.value.toLowerCase().startsWith(query)));
|
|
661
749
|
const open = matches.length > 0 && dismissed !== prompt.value;
|
|
@@ -733,7 +821,7 @@ const BAR_PITCH = 5;
|
|
|
733
821
|
* has been heard so far, the sound as a wave scrolling to the left, how long
|
|
734
822
|
* it has been, and buttons to throw it away or keep it.
|
|
735
823
|
*/
|
|
736
|
-
function PromptRecorder({ transcript, placeholder = 'Listening…', onCancel, onDone, level, cancelLabel = 'Cancel recording', doneLabel = 'Finish recording', className, ...props }) {
|
|
824
|
+
function PromptRecorder({ transcript, placeholder = 'Listening…', onCancel, onDone, level, cancelLabel = 'Cancel recording', doneLabel = 'Finish recording', className, render, ...props }) {
|
|
737
825
|
const slots = promptVariants();
|
|
738
826
|
// Starts on a low murmur rather than flat silence, so the wave is never a row of dots.
|
|
739
827
|
const [levels, setLevels] = useState(() => Array.from({ length: BARS }, (_, i) => 0.12 + 0.06 * Math.sin(i * 1.7)));
|
|
@@ -778,7 +866,15 @@ function PromptRecorder({ transcript, placeholder = 'Listening…', onCancel, on
|
|
|
778
866
|
};
|
|
779
867
|
}, []);
|
|
780
868
|
const time = `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}`;
|
|
781
|
-
return (
|
|
869
|
+
return useRender({
|
|
870
|
+
defaultTagName: 'div',
|
|
871
|
+
render,
|
|
872
|
+
props: mergeProps({
|
|
873
|
+
'data-slot': 'prompt-recorder',
|
|
874
|
+
className: slots.recorder({ className }),
|
|
875
|
+
children: (_jsxs(_Fragment, { children: [_jsx("p", { "aria-live": "polite", className: slots.recorderTranscript(), children: transcript || (_jsx("span", { ref: listening, className: slots.recorderListening(), children: placeholder })) }), _jsxs("div", { className: slots.recorderControls(), children: [_jsx(Button, { variant: "ghost", size: "icon", shape: "pill", "aria-label": cancelLabel, onClick: onCancel, children: _jsx(XIcon, {}) }), _jsx("div", { ref: wave, "aria-hidden": "true", className: slots.recorderWave(), children: levels.slice(-fits).map((value, index) => (_jsx("span", { className: slots.recorderBar(), style: { height: `${3 + value * 33}px` } }, index))) }), _jsxs("span", { role: "timer", "aria-label": `Recording, ${time}`, className: slots.recorderTime(), children: [_jsx("span", { className: slots.recorderDot() }), time] }), _jsx(Button, { shape: "pill", "aria-label": doneLabel, onClick: onDone, className: slots.recorderDone(), children: _jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.25", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) }) })] })] })),
|
|
876
|
+
}, props),
|
|
877
|
+
});
|
|
782
878
|
}
|
|
783
879
|
PromptRecorder.displayName = 'Prompt.Recorder';
|
|
784
880
|
export const Prompt = Object.assign(PromptRoot, {
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Radio Group — one choice from a short list, all of it visible at once.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* <RadioGroup defaultValue="standard" aria-label="Delivery">
|
|
6
|
+
* <Label><Radio value="standard" /><Label.Text>Standard</Label.Text></Label>
|
|
7
|
+
* <Label><Radio value="express" /><Label.Text>Express</Label.Text></Label>
|
|
8
|
+
* </RadioGroup>
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* ## The dot lands
|
|
12
|
+
*
|
|
13
|
+
* The chosen radio's centre grows in with a small overshoot, and the ring
|
|
14
|
+
* thickens to meet it, so the choice reads as something being set down
|
|
15
|
+
* rather than a colour changing. The one it leaves shrinks back at the same
|
|
16
|
+
* time. With reduced motion both happen at once.
|
|
17
|
+
*
|
|
18
|
+
* ## Arrow keys move the choice
|
|
19
|
+
*
|
|
20
|
+
* The group is one stop for Tab. Inside it, the arrow keys move the choice
|
|
21
|
+
* from one radio to the next, as a native radio group does.
|
|
22
|
+
*
|
|
23
|
+
* ## As cards
|
|
24
|
+
*
|
|
25
|
+
* `RadioGroup.Card` puts each option in a card of its own, with a title, a
|
|
26
|
+
* line of description and something at the far end such as a price, and the
|
|
27
|
+
* radio drawn inside it. The whole card is the target. It comes in three
|
|
28
|
+
* versions: `outline` rings the chosen card in its colour, `filled` tints
|
|
29
|
+
* it, and `dither` fills it from the far edge with an ordered dither in its
|
|
30
|
+
* colour, densest at the edge and thinning towards the text, which sweeps in
|
|
31
|
+
* as the card is chosen and back out as it is left.
|
|
32
|
+
*
|
|
33
|
+
* ## When to use something else
|
|
34
|
+
*
|
|
35
|
+
* For two to five options that each need a line of explanation, ChoiceCards
|
|
36
|
+
* lays them out as cards. For more than about seven, a Select keeps the form
|
|
37
|
+
* short. For a choice that takes effect at once, without a form to submit,
|
|
38
|
+
* Tabs or a ButtonGroup read better.
|
|
39
|
+
*/
|
|
40
|
+
import { Radio as BaseRadio } from '@base-ui/react/radio';
|
|
41
|
+
import { RadioGroup as BaseRadioGroup } from '@base-ui/react/radio-group';
|
|
42
|
+
import type { ComponentProps, CSSProperties, ReactNode } from 'react';
|
|
43
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
44
|
+
import type { ElementType as RenderElementType } from 'react';
|
|
45
|
+
/** Draws a part as another element, or composes it with a component: `render={<a href="/" />}`. */
|
|
46
|
+
type RenderProp<Tag extends RenderElementType> = {
|
|
47
|
+
render?: useRender.ComponentProps<Tag>['render'];
|
|
48
|
+
};
|
|
49
|
+
export type RadioSize = 'sm' | 'md' | 'lg';
|
|
50
|
+
export type RadioColor = 'primary' | 'success' | 'info' | 'destructive';
|
|
51
|
+
export interface RadioGroupProps extends Omit<BaseRadioGroup.Props<string>, 'className'> {
|
|
52
|
+
/** Whether the radios stack or sit in a row. Also sets which arrow keys move the choice. Default `vertical`. */
|
|
53
|
+
orientation?: 'vertical' | 'horizontal';
|
|
54
|
+
className?: string;
|
|
55
|
+
}
|
|
56
|
+
/** Holds one value for the radios inside it. Takes `value`/`onValueChange` or `defaultValue`, and `name` for a form. */
|
|
57
|
+
declare function RadioGroupRoot({ orientation, className, ...props }: RadioGroupProps): import("react").JSX.Element;
|
|
58
|
+
declare namespace RadioGroupRoot {
|
|
59
|
+
var displayName: string;
|
|
60
|
+
}
|
|
61
|
+
export interface RadioProps extends Omit<BaseRadio.Root.Props<string>, 'className' | 'color'> {
|
|
62
|
+
/** Default `md`. */
|
|
63
|
+
size?: RadioSize;
|
|
64
|
+
/** The colour of the chosen radio. `primary` by default. */
|
|
65
|
+
color?: RadioColor;
|
|
66
|
+
/** What a screen reader calls it, when there is no visible label tied to it. */
|
|
67
|
+
label?: string;
|
|
68
|
+
className?: string;
|
|
69
|
+
style?: CSSProperties;
|
|
70
|
+
}
|
|
71
|
+
/** One option. `value` is what the group's value becomes when it is chosen. */
|
|
72
|
+
declare function Radio({ size, color, label, className, ...props }: RadioProps): import("react").JSX.Element;
|
|
73
|
+
declare namespace Radio {
|
|
74
|
+
var displayName: string;
|
|
75
|
+
}
|
|
76
|
+
export type RadioCardAppearance = 'outline' | 'filled' | 'dither';
|
|
77
|
+
export interface RadioCardProps extends Omit<BaseRadio.Root.Props<string>, 'className' | 'color' | 'children'> {
|
|
78
|
+
/**
|
|
79
|
+
* How the chosen card is marked: a ring, a tint, or a dither ramp from the
|
|
80
|
+
* far edge. The ramp draws where `RadioGroup.CardAside` would sit, so leave
|
|
81
|
+
* the aside out of a `dither` card. Default `outline`.
|
|
82
|
+
*/
|
|
83
|
+
appearance?: RadioCardAppearance;
|
|
84
|
+
/** The colour the chosen card is marked in. `primary` by default. */
|
|
85
|
+
color?: RadioColor;
|
|
86
|
+
className?: string;
|
|
87
|
+
style?: CSSProperties;
|
|
88
|
+
/** `RadioGroup.CardTitle`, `RadioGroup.CardDescription` and `RadioGroup.CardAside`. */
|
|
89
|
+
children?: ReactNode;
|
|
90
|
+
}
|
|
91
|
+
/** One option drawn as a card, with its radio inside. The whole card is pressed to choose it. */
|
|
92
|
+
declare function RadioCard({ appearance, color, className, children, ...props }: RadioCardProps): import("react").JSX.Element;
|
|
93
|
+
declare namespace RadioCard {
|
|
94
|
+
var displayName: string;
|
|
95
|
+
}
|
|
96
|
+
/** Holds a card's title and description, beside the radio. */
|
|
97
|
+
declare function RadioCardBody({ className, render, ...props }: ComponentProps<'span'> & RenderProp<'span'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
98
|
+
declare namespace RadioCardBody {
|
|
99
|
+
var displayName: string;
|
|
100
|
+
}
|
|
101
|
+
/** The option's name. */
|
|
102
|
+
declare function RadioCardTitle({ className, render, ...props }: ComponentProps<'span'> & RenderProp<'span'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
103
|
+
declare namespace RadioCardTitle {
|
|
104
|
+
var displayName: string;
|
|
105
|
+
}
|
|
106
|
+
/** A line under the name saying what the option means. */
|
|
107
|
+
declare function RadioCardDescription({ className, render, ...props }: ComponentProps<'span'> & RenderProp<'span'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
108
|
+
declare namespace RadioCardDescription {
|
|
109
|
+
var displayName: string;
|
|
110
|
+
}
|
|
111
|
+
/** Pushed to the far end of the card: a price, a duration, a badge. */
|
|
112
|
+
declare function RadioCardAside({ className, render, ...props }: ComponentProps<'span'> & RenderProp<'span'>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
113
|
+
declare namespace RadioCardAside {
|
|
114
|
+
var displayName: string;
|
|
115
|
+
}
|
|
116
|
+
export declare const RadioGroup: typeof RadioGroupRoot & {
|
|
117
|
+
Item: typeof Radio;
|
|
118
|
+
Card: typeof RadioCard;
|
|
119
|
+
CardBody: typeof RadioCardBody;
|
|
120
|
+
CardTitle: typeof RadioCardTitle;
|
|
121
|
+
CardDescription: typeof RadioCardDescription;
|
|
122
|
+
CardAside: typeof RadioCardAside;
|
|
123
|
+
};
|
|
124
|
+
export { Radio, Radio as RadioGroupItem, RadioCard, RadioCard as RadioGroupCard, RadioCardBody as RadioGroupCardBody, RadioCardTitle as RadioGroupCardTitle, RadioCardDescription as RadioGroupCardDescription, RadioCardAside as RadioGroupCardAside, };
|