@script-it/json-views-react 0.1.0-beta.1
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/LICENSE +21 -0
- package/README.md +301 -0
- package/THIRD_PARTY_NOTICES.txt +4917 -0
- package/dist/browser-device.d.ts +21 -0
- package/dist/browser-device.js +22 -0
- package/dist/file-content-edit-state.d.ts +10 -0
- package/dist/file-content-edit-state.js +1 -0
- package/dist/file-content-view.d.ts +8 -0
- package/dist/file-content-view.js +7 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +8 -0
- package/dist/json/copy-json.d.ts +15 -0
- package/dist/json/copy-json.js +52 -0
- package/dist/json/html-preview.d.ts +5 -0
- package/dist/json/html-preview.js +12 -0
- package/dist/json/html-view/native-control.d.ts +44 -0
- package/dist/json/html-view/native-control.js +96 -0
- package/dist/json/html-view/settings.d.ts +2 -0
- package/dist/json/html-view/settings.js +58 -0
- package/dist/json/html-view/template.d.ts +12 -0
- package/dist/json/html-view/template.js +55 -0
- package/dist/json/html-view.d.ts +9 -0
- package/dist/json/html-view.js +163 -0
- package/dist/json/json-content.d.ts +61 -0
- package/dist/json/json-content.js +433 -0
- package/dist/json/json-source-editor.d.ts +36 -0
- package/dist/json/json-source-editor.js +203 -0
- package/dist/json/json-view.d.ts +3 -0
- package/dist/json/json-view.js +322 -0
- package/dist/json/kanban-view.d.ts +20 -0
- package/dist/json/kanban-view.js +328 -0
- package/dist/json/long-text.d.ts +3 -0
- package/dist/json/long-text.js +6 -0
- package/dist/json/record-view.d.ts +38 -0
- package/dist/json/record-view.js +85 -0
- package/dist/json/row-mutations.d.ts +7 -0
- package/dist/json/row-mutations.js +93 -0
- package/dist/json/schema-value.d.ts +57 -0
- package/dist/json/schema-value.js +294 -0
- package/dist/json/source-default.d.ts +1 -0
- package/dist/json/source-default.js +1 -0
- package/dist/json/source-edit-capabilities.d.ts +8 -0
- package/dist/json/source-edit-capabilities.js +22 -0
- package/dist/json/source-literals.d.ts +15 -0
- package/dist/json/source-literals.js +24 -0
- package/dist/json/source-recovery.d.ts +32 -0
- package/dist/json/source-recovery.js +38 -0
- package/dist/json/source-selection.d.ts +3 -0
- package/dist/json/source-selection.js +16 -0
- package/dist/json/table-view.d.ts +20 -0
- package/dist/json/table-view.js +300 -0
- package/dist/json/use-kanban-card-drag.d.ts +13 -0
- package/dist/json/use-kanban-card-drag.js +255 -0
- package/dist/json/use-kanban-column-drag.d.ts +14 -0
- package/dist/json/use-kanban-column-drag.js +179 -0
- package/dist/json/view-diagnostics.d.ts +4 -0
- package/dist/json/view-diagnostics.js +26 -0
- package/dist/json/view-model.d.ts +27 -0
- package/dist/json/view-model.js +180 -0
- package/dist/json/view-save-status.d.ts +12 -0
- package/dist/json/view-save-status.js +38 -0
- package/dist/json/view-settings.d.ts +36 -0
- package/dist/json/view-settings.js +661 -0
- package/dist/json/view-types.d.ts +47 -0
- package/dist/json/view-types.js +4 -0
- package/dist/json/viewers/general/general-json-view.d.ts +46 -0
- package/dist/json/viewers/general/general-json-view.js +163 -0
- package/dist/json/viewers/general/json-flatten.d.ts +33 -0
- package/dist/json/viewers/general/json-flatten.js +51 -0
- package/dist/json/viewers/general/json-node-view.d.ts +43 -0
- package/dist/json/viewers/general/json-node-view.js +215 -0
- package/dist/lib/clipboard.d.ts +1 -0
- package/dist/lib/clipboard.js +24 -0
- package/dist/lib/cn.d.ts +2 -0
- package/dist/lib/cn.js +5 -0
- package/dist/lib/file-data.d.ts +7 -0
- package/dist/lib/file-data.js +5 -0
- package/dist/lib/floating-portal-selectors.d.ts +4 -0
- package/dist/lib/floating-portal-selectors.js +4 -0
- package/dist/lib/time.d.ts +24 -0
- package/dist/lib/time.js +60 -0
- package/dist/lib/ui-size.d.ts +28 -0
- package/dist/lib/ui-size.js +39 -0
- package/dist/lib/use-anchored-position.d.ts +5 -0
- package/dist/lib/use-anchored-position.js +65 -0
- package/dist/lib/use-dismiss.d.ts +31 -0
- package/dist/lib/use-dismiss.js +65 -0
- package/dist/primitives/boolean-toggle.d.ts +10 -0
- package/dist/primitives/boolean-toggle.js +11 -0
- package/dist/primitives/button.d.ts +11 -0
- package/dist/primitives/button.js +34 -0
- package/dist/primitives/checkbox.d.ts +4 -0
- package/dist/primitives/checkbox.js +8 -0
- package/dist/primitives/choice-control.d.ts +29 -0
- package/dist/primitives/choice-control.js +131 -0
- package/dist/primitives/dialog.d.ts +29 -0
- package/dist/primitives/dialog.js +25 -0
- package/dist/primitives/dropdown-menu.d.ts +37 -0
- package/dist/primitives/dropdown-menu.js +39 -0
- package/dist/primitives/inline-feedback-action.d.ts +13 -0
- package/dist/primitives/inline-feedback-action.js +6 -0
- package/dist/primitives/menu-select.d.ts +12 -0
- package/dist/primitives/menu-select.js +7 -0
- package/dist/primitives/multi-select.d.ts +18 -0
- package/dist/primitives/multi-select.js +7 -0
- package/dist/primitives/number-input.d.ts +10 -0
- package/dist/primitives/number-input.js +8 -0
- package/dist/primitives/option-pill.d.ts +13 -0
- package/dist/primitives/option-pill.js +50 -0
- package/dist/primitives/tabs.d.ts +7 -0
- package/dist/primitives/tabs.js +12 -0
- package/dist/primitives/textarea.d.ts +4 -0
- package/dist/primitives/textarea.js +8 -0
- package/dist/primitives/tooltip.d.ts +31 -0
- package/dist/primitives/tooltip.js +37 -0
- package/dist/standalone.css +113 -0
- package/dist/structured-data/atomic-value-editor.d.ts +33 -0
- package/dist/structured-data/atomic-value-editor.js +195 -0
- package/dist/structured-data/atomic-value.d.ts +3 -0
- package/dist/structured-data/atomic-value.js +6 -0
- package/dist/structured-data/column-controls.d.ts +21 -0
- package/dist/structured-data/column-controls.js +180 -0
- package/dist/structured-data/copy-json-action.d.ts +10 -0
- package/dist/structured-data/copy-json-action.js +87 -0
- package/dist/structured-data/delete-row-action.d.ts +10 -0
- package/dist/structured-data/delete-row-action.js +37 -0
- package/dist/structured-data/edit-base.d.ts +3 -0
- package/dist/structured-data/edit-base.js +8 -0
- package/dist/structured-data/filter-value.d.ts +2 -0
- package/dist/structured-data/filter-value.js +28 -0
- package/dist/structured-data/row-selection-icon.d.ts +9 -0
- package/dist/structured-data/row-selection-icon.js +6 -0
- package/dist/structured-data/structured-cell-fill-context.d.ts +6 -0
- package/dist/structured-data/structured-cell-fill-context.js +7 -0
- package/dist/structured-data/structured-data-content.d.ts +1 -0
- package/dist/structured-data/structured-data-content.js +1 -0
- package/dist/structured-data/table-types.d.ts +108 -0
- package/dist/structured-data/table-types.js +1 -0
- package/dist/structured-data/use-metadata-persistence.d.ts +15 -0
- package/dist/structured-data/use-metadata-persistence.js +18 -0
- package/dist/structured-data/use-optimistic-text-document.d.ts +22 -0
- package/dist/structured-data/use-optimistic-text-document.js +30 -0
- package/dist/structured-data/value-cell.d.ts +15 -0
- package/dist/structured-data/value-cell.js +81 -0
- package/dist/styles.css +2 -0
- package/dist/surface.d.ts +14 -0
- package/dist/surface.js +73 -0
- package/dist/tabular-data-view.d.ts +5 -0
- package/dist/tabular-data-view.js +493 -0
- package/dist/viewer-state.d.ts +72 -0
- package/dist/viewer-state.js +79 -0
- package/dist/widget-registry.d.ts +60 -0
- package/dist/widget-registry.js +126 -0
- package/dist/widgets/boolean-widget.d.ts +2 -0
- package/dist/widgets/boolean-widget.js +15 -0
- package/dist/widgets/date-picker-widget.d.ts +18 -0
- package/dist/widgets/date-picker-widget.js +265 -0
- package/dist/widgets/dropdown-widget.d.ts +10 -0
- package/dist/widgets/dropdown-widget.js +19 -0
- package/dist/widgets/multi-select-widget.d.ts +11 -0
- package/dist/widgets/multi-select-widget.js +46 -0
- package/dist/widgets/number-widget.d.ts +2 -0
- package/dist/widgets/number-widget.js +19 -0
- package/dist/widgets/read-only-field.d.ts +36 -0
- package/dist/widgets/read-only-field.js +39 -0
- package/dist/widgets/text-widget.d.ts +2 -0
- package/dist/widgets/text-widget.js +19 -0
- package/dist/widgets/textarea-widget.d.ts +2 -0
- package/dist/widgets/textarea-widget.js +34 -0
- package/dist/widgets/types.d.ts +53 -0
- package/dist/widgets/types.js +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Check, ChevronDown, Palette } from 'lucide-react';
|
|
4
|
+
import { cn } from '../lib/cn.js';
|
|
5
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, } from './dropdown-menu.js';
|
|
6
|
+
import { OptionPill, optionColorsForValues } from './option-pill.js';
|
|
7
|
+
const OPTION_COLORS = [
|
|
8
|
+
['gray', 'bg-muted'],
|
|
9
|
+
['blue', 'bg-muted'],
|
|
10
|
+
['green', 'bg-muted'],
|
|
11
|
+
['yellow', 'bg-muted'],
|
|
12
|
+
['orange', 'bg-muted'],
|
|
13
|
+
['red', 'bg-muted'],
|
|
14
|
+
['purple', 'bg-muted'],
|
|
15
|
+
['pink', 'bg-muted'],
|
|
16
|
+
];
|
|
17
|
+
function selectedOption(options, value) {
|
|
18
|
+
return options.find((option) => option.value === value) ?? { value, label: value };
|
|
19
|
+
}
|
|
20
|
+
export const ChoiceControl = React.forwardRef(({ mode, value, onValueChange, options, profile = 'plain', creatable = false, emptyOptionLabel, disabled = false, readOnly = false, invalid = false, openOnMount = false, nonModal = profile === 'pill-cell', onDismiss, onSelectionBlur, onOptionColorChange, className, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedBy, ...props }, ref) => {
|
|
21
|
+
const [open, setOpen] = React.useState(openOnMount);
|
|
22
|
+
const [query, setQuery] = React.useState('');
|
|
23
|
+
const [colorOption, setColorOption] = React.useState(null);
|
|
24
|
+
const inputRef = React.useRef(null);
|
|
25
|
+
const knownOptions = React.useMemo(() => {
|
|
26
|
+
const byValue = new Map(options.map((option) => [option.value, option]));
|
|
27
|
+
value.forEach((selected) => {
|
|
28
|
+
if (!byValue.has(selected))
|
|
29
|
+
byValue.set(selected, { value: selected, label: selected });
|
|
30
|
+
});
|
|
31
|
+
const entries = [...byValue.values()];
|
|
32
|
+
const colors = optionColorsForValues(entries.map((option) => option.value), Object.fromEntries(entries.filter((option) => option.color).map((option) => [option.value, option.color])));
|
|
33
|
+
return entries.map((option) => ({ ...option, color: colors[option.value] }));
|
|
34
|
+
}, [options, value]);
|
|
35
|
+
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
36
|
+
const visibleOptions = knownOptions.filter((option) => (!normalizedQuery || option.label.toLocaleLowerCase().includes(normalizedQuery)));
|
|
37
|
+
const canCreate = Boolean(creatable
|
|
38
|
+
&& query.trim()
|
|
39
|
+
&& !knownOptions.some((option) => (option.value.toLocaleLowerCase() === normalizedQuery
|
|
40
|
+
|| option.label.toLocaleLowerCase() === normalizedQuery)));
|
|
41
|
+
const choose = (nextValue) => {
|
|
42
|
+
if (disabled || readOnly)
|
|
43
|
+
return;
|
|
44
|
+
if (mode === 'single') {
|
|
45
|
+
onValueChange(nextValue ? [nextValue] : []);
|
|
46
|
+
setOpen(false);
|
|
47
|
+
onSelectionBlur?.();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
onValueChange(value.includes(nextValue)
|
|
51
|
+
? value.filter((selected) => selected !== nextValue)
|
|
52
|
+
: [...value, nextValue]);
|
|
53
|
+
};
|
|
54
|
+
const createOption = () => {
|
|
55
|
+
const nextValue = query.trim();
|
|
56
|
+
if (!nextValue || !canCreate)
|
|
57
|
+
return;
|
|
58
|
+
setQuery('');
|
|
59
|
+
choose(nextValue);
|
|
60
|
+
};
|
|
61
|
+
const handleQueryKeyDown = (event) => {
|
|
62
|
+
if (event.key === 'Enter' && canCreate) {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
createOption();
|
|
65
|
+
}
|
|
66
|
+
if (event.key !== 'Escape')
|
|
67
|
+
event.stopPropagation();
|
|
68
|
+
};
|
|
69
|
+
const optionButtons = visibleOptions.map((option) => {
|
|
70
|
+
const active = value.includes(option.value);
|
|
71
|
+
const selectionButton = (_jsxs("button", { type: "button", "data-id": mode === 'single' ? 'pill-select-option' : 'multi-select-option', "data-value": option.value, "aria-pressed": active, disabled: disabled || readOnly, onClick: () => choose(option.value), className: cn(profile === 'plain'
|
|
72
|
+
? 'rounded-md border px-3 py-1.5 text-sm transition-colors'
|
|
73
|
+
: 'flex min-h-8 w-full min-w-0 flex-1 items-center justify-between gap-3 rounded-md px-1.5 text-left hover:bg-accent focus-visible:bg-accent focus-visible:outline-none', profile === 'plain' && (active
|
|
74
|
+
? 'border-primary bg-primary text-primary-foreground'
|
|
75
|
+
: 'border-border bg-card text-foreground hover:bg-accent'), 'disabled:cursor-not-allowed disabled:opacity-50'), children: [profile === 'plain'
|
|
76
|
+
? _jsx("span", { children: option.label })
|
|
77
|
+
: profile === 'menu'
|
|
78
|
+
? _jsx("span", { className: "min-w-0 flex-1 truncate px-1.5 text-xs", children: option.label })
|
|
79
|
+
: _jsx(OptionPill, { label: option.label, color: option.color, className: "min-w-0 max-w-full", labelClassName: "truncate" }), profile !== 'plain' && active && (_jsx(Check, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }))] }));
|
|
80
|
+
if (profile === 'plain' || !onOptionColorChange || readOnly)
|
|
81
|
+
return React.cloneElement(selectionButton, { key: option.value });
|
|
82
|
+
return (_jsxs("div", { className: "rounded-md", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [selectionButton, _jsx("button", { type: "button", "aria-label": `Change color for ${option.label}`, "aria-expanded": colorOption === option.value, disabled: disabled, className: "grid h-7 w-7 shrink-0 place-items-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", onClick: (event) => {
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
event.stopPropagation();
|
|
85
|
+
setColorOption((current) => current === option.value ? null : option.value);
|
|
86
|
+
}, children: _jsx(Palette, { className: "h-3.5 w-3.5" }) })] }), colorOption === option.value && (_jsx("div", { "data-id": "option-color-palette", className: "flex items-center gap-1 px-1.5 pb-1.5 pt-0.5", children: OPTION_COLORS.map(([color, swatchClass]) => (_jsx("button", { type: "button", "aria-label": `Set ${option.label} color to ${color}`, "data-option-color": color, disabled: disabled, className: cn('h-4 w-4 rounded-full border-2 border-card shadow-sm outline-none hover:scale-110 focus-visible:ring-2 focus-visible:ring-ring', swatchClass, option.color === color && 'ring-2 ring-ring'), onClick: (event) => {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
event.stopPropagation();
|
|
89
|
+
onOptionColorChange(option.value, color);
|
|
90
|
+
setColorOption(null);
|
|
91
|
+
} }, color))) }))] }, option.value));
|
|
92
|
+
});
|
|
93
|
+
if (profile === 'plain') {
|
|
94
|
+
return (_jsxs("div", { ref: ref, role: "group", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-invalid": invalid || undefined, className: cn('space-y-2', className), ...props, children: [_jsx("div", { className: "flex flex-wrap gap-2", children: optionButtons }), mode === 'multiple' && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [value.length, " selected"] }))] }));
|
|
95
|
+
}
|
|
96
|
+
const singleSelection = mode === 'single' && value[0]
|
|
97
|
+
? selectedOption(knownOptions, value[0])
|
|
98
|
+
: undefined;
|
|
99
|
+
return (_jsx("div", { ref: ref, role: "group", className: cn('relative min-w-0', className), ...props, children: _jsxs(DropdownMenu, { modal: !nonModal, open: open, onOpenChange: (nextOpen) => {
|
|
100
|
+
setOpen(nextOpen);
|
|
101
|
+
if (nextOpen)
|
|
102
|
+
setQuery('');
|
|
103
|
+
if (!nextOpen) {
|
|
104
|
+
setColorOption(null);
|
|
105
|
+
if (mode === 'multiple' && open)
|
|
106
|
+
onSelectionBlur?.();
|
|
107
|
+
if (open)
|
|
108
|
+
onDismiss?.();
|
|
109
|
+
}
|
|
110
|
+
}, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs("button", { type: "button", "data-id": mode === 'single' ? 'pill-select' : 'multi-select-trigger', "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-invalid": invalid || undefined, disabled: disabled, className: cn('flex w-full min-w-0 items-center gap-1 overflow-hidden text-left text-xs text-foreground outline-none focus:ring-2 focus:ring-inset focus:ring-ring/20 disabled:pointer-events-none disabled:opacity-50', profile === 'pill-cell'
|
|
111
|
+
? 'min-h-7 rounded-sm px-1 hover:bg-accent/40'
|
|
112
|
+
: 'min-h-8 rounded-md border border-border bg-background px-2 py-1 hover:border-ring focus:border-ring', mode === 'single' && 'justify-between gap-2', invalid && 'border-destructive focus:border-destructive focus:ring-destructive/20'), children: [mode === 'single' ? (singleSelection
|
|
113
|
+
? profile === 'menu'
|
|
114
|
+
? _jsx("span", { className: "min-w-0 flex-1 truncate px-0.5 text-xs", children: singleSelection.label })
|
|
115
|
+
: _jsx(OptionPill, { label: singleSelection.label, color: singleSelection.color })
|
|
116
|
+
: _jsx("span", { className: "text-muted-foreground", children: emptyOptionLabel ?? 'None' })) : value.length > 0 ? (_jsx("span", { "data-id": "multi-select-preview", className: "flex min-w-0 flex-nowrap gap-1 overflow-hidden", children: value.map((selected) => {
|
|
117
|
+
const option = selectedOption(knownOptions, selected);
|
|
118
|
+
return _jsx(OptionPill, { label: option.label, color: option.color }, selected);
|
|
119
|
+
}) })) : (_jsx("span", { className: cn('text-muted-foreground', profile !== 'pill-cell' && 'px-0.5'), children: "None" })), mode === 'single' && (_jsx(ChevronDown, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }))] }) }), _jsx(DropdownMenuContent, { "data-id": "input-widget-overlay", align: "start", sideOffset: 4, onOpenAutoFocus: (event) => {
|
|
120
|
+
if (mode === 'single' && !creatable)
|
|
121
|
+
return;
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
inputRef.current?.focus();
|
|
124
|
+
}, className: cn('text-foreground', mode === 'multiple'
|
|
125
|
+
? 'w-[min(36rem,calc(100vw-2rem))] p-0'
|
|
126
|
+
: 'min-w-[var(--radix-dropdown-menu-trigger-width)] p-1'), children: mode === 'single' ? (_jsxs(_Fragment, { children: [creatable && !readOnly && (_jsx("div", { className: "border-b border-border p-1.5", children: _jsx("input", { ref: inputRef, "aria-label": "Filter or add options", value: query, placeholder: "Type to add an option\u2026", className: "h-8 w-full rounded-md bg-transparent px-2 text-xs outline-none placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring", onChange: (event) => setQuery(event.target.value), onKeyDown: handleQueryKeyDown }) })), emptyOptionLabel && (_jsxs("button", { type: "button", "data-id": "pill-select-option", "aria-pressed": !singleSelection, className: "flex min-h-8 w-full items-center justify-between gap-3 rounded-md px-1.5 text-left hover:bg-accent focus-visible:bg-accent focus-visible:outline-none", onClick: () => choose(''), children: [_jsx("span", { className: "px-2 text-xs text-muted-foreground", children: emptyOptionLabel }), !singleSelection && _jsx(Check, { className: "h-3.5 w-3.5 text-muted-foreground" })] })), optionButtons, canCreate && (_jsxs("button", { type: "button", "data-id": "select-create", className: "flex min-h-8 w-full items-center gap-2 rounded-md px-1.5 text-left text-xs hover:bg-accent focus-visible:bg-accent focus-visible:outline-none", onClick: createOption, children: ["Add ", _jsx(OptionPill, { label: query.trim() })] })), visibleOptions.length === 0 && !canCreate && (_jsx("p", { className: "px-2 py-3 text-xs text-muted-foreground", children: "No matching options" }))] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex min-h-12 flex-wrap items-center gap-1.5 border-b border-border p-2.5", children: [value.map((selected) => {
|
|
127
|
+
const option = selectedOption(knownOptions, selected);
|
|
128
|
+
return (_jsx(OptionPill, { label: option.label, color: option.color, onRemove: readOnly ? undefined : () => choose(selected) }, selected));
|
|
129
|
+
}), !readOnly && (_jsx("input", { ref: inputRef, "aria-label": "Filter options", value: query, placeholder: value.length === 0 ? 'Add an option…' : '', className: "h-6 min-w-24 flex-1 bg-transparent px-1 text-xs outline-none placeholder:text-muted-foreground", onChange: (event) => setQuery(event.target.value), onKeyDown: handleQueryKeyDown }))] }), _jsxs("div", { className: "p-2", children: [_jsx("p", { className: "px-1.5 pb-1.5 text-[0.6875rem] font-medium text-muted-foreground", children: readOnly ? 'Selected options' : creatable ? 'Select an option or create one' : 'Select an option' }), _jsxs("div", { "data-id": "multi-select-options", className: "max-h-64 space-y-0.5 overflow-y-auto", children: [optionButtons, canCreate && (_jsxs("button", { type: "button", "data-id": "multi-select-create", className: "flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-xs hover:bg-accent focus-visible:bg-accent focus-visible:outline-none", onClick: createOption, children: ["Create ", _jsx(OptionPill, { label: query.trim() })] })), visibleOptions.length === 0 && !canCreate && (_jsx("p", { className: "px-2 py-3 text-xs text-muted-foreground", children: "No matching options" }))] })] })] })) })] }) }));
|
|
130
|
+
});
|
|
131
|
+
ChoiceControl.displayName = 'ChoiceControl';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
3
|
+
declare const Dialog: React.FC<DialogPrimitive.DialogProps>;
|
|
4
|
+
declare const DialogTrigger: React.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
5
|
+
declare const DialogPortal: React.FC<DialogPrimitive.DialogPortalProps>;
|
|
6
|
+
declare const DialogClose: React.ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
|
|
7
|
+
declare const DialogOverlay: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogOverlayProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
8
|
+
interface DialogContentProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
|
|
9
|
+
/** Hide the dark overlay behind the dialog */
|
|
10
|
+
hideOverlay?: boolean;
|
|
11
|
+
/** Optional styling for the overlay, including nested-dialog stacking. */
|
|
12
|
+
overlayClassName?: string;
|
|
13
|
+
/** Portal into a local surface instead of the document body. */
|
|
14
|
+
portalContainer?: HTMLElement | null;
|
|
15
|
+
/** Position the overlay and content within the portal container. */
|
|
16
|
+
contained?: boolean;
|
|
17
|
+
}
|
|
18
|
+
declare const DialogContent: React.ForwardRefExoticComponent<DialogContentProps & React.RefAttributes<HTMLDivElement>>;
|
|
19
|
+
declare const DialogHeader: {
|
|
20
|
+
({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
|
|
21
|
+
displayName: string;
|
|
22
|
+
};
|
|
23
|
+
declare const DialogFooter: {
|
|
24
|
+
({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
|
|
25
|
+
displayName: string;
|
|
26
|
+
};
|
|
27
|
+
declare const DialogTitle: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogTitleProps & React.RefAttributes<HTMLHeadingElement>, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
|
|
28
|
+
declare const DialogDescription: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogDescriptionProps & React.RefAttributes<HTMLParagraphElement>, "ref"> & React.RefAttributes<HTMLParagraphElement>>;
|
|
29
|
+
export { Dialog, DialogPortal, DialogOverlay, DialogTrigger, DialogClose, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useJsonViewsPortalContainer } from '../surface.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
5
|
+
import { cn } from '../lib/cn.js';
|
|
6
|
+
const Dialog = DialogPrimitive.Root;
|
|
7
|
+
const DialogTrigger = DialogPrimitive.Trigger;
|
|
8
|
+
const DialogPortal = DialogPrimitive.Portal;
|
|
9
|
+
const DialogClose = DialogPrimitive.Close;
|
|
10
|
+
const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, "data-slot": "dialog-overlay", className: cn('fixed inset-0 z-[80] bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', className), ...props })));
|
|
11
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
12
|
+
const DialogContent = React.forwardRef(({ className, children, hideOverlay, overlayClassName, portalContainer, contained = false, ...props }, ref) => {
|
|
13
|
+
const defaultPortalContainer = useJsonViewsPortalContainer();
|
|
14
|
+
return (_jsxs(DialogPortal, { container: portalContainer ?? defaultPortalContainer, children: [!hideOverlay && contained && (_jsx("div", { "aria-hidden": "true", "data-slot": "dialog-overlay", "data-state": "open", className: cn('absolute inset-0 z-[80] bg-black/60 data-[state=open]:animate-in data-[state=open]:fade-in-0', overlayClassName) })), !hideOverlay && !contained && (_jsx(DialogOverlay, { className: overlayClassName })), _jsx(DialogPrimitive.Content, { ref: ref, className: cn('fixed left-[50%] top-[50%] z-[90] grid w-[calc(100%-2rem)] max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border bg-card p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]', contained && 'absolute', className), ...props, children: children })] }));
|
|
15
|
+
});
|
|
16
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
17
|
+
const DialogHeader = ({ className, ...props }) => (_jsx("div", { className: cn('flex flex-col space-y-1.5 text-center sm:text-left', className), ...props }));
|
|
18
|
+
DialogHeader.displayName = 'DialogHeader';
|
|
19
|
+
const DialogFooter = ({ className, ...props }) => (_jsx("div", { className: cn('flex flex-col-reverse gap-3 sm:flex-row sm:justify-end', className), ...props }));
|
|
20
|
+
DialogFooter.displayName = 'DialogFooter';
|
|
21
|
+
const DialogTitle = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Title, { ref: ref, className: cn('text-lg font-semibold leading-none tracking-tight', className), ...props })));
|
|
22
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
23
|
+
const DialogDescription = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Description, { ref: ref, className: cn('text-sm text-muted-foreground', className), ...props })));
|
|
24
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
25
|
+
export { Dialog, DialogPortal, DialogOverlay, DialogTrigger, DialogClose, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
|
+
declare const DropdownMenu: React.FC<DropdownMenuPrimitive.DropdownMenuProps>;
|
|
4
|
+
declare const DropdownMenuTrigger: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuTriggerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
5
|
+
declare const DropdownMenuGroup: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuGroupProps & React.RefAttributes<HTMLDivElement>>;
|
|
6
|
+
declare const DropdownMenuPortal: React.FC<DropdownMenuPrimitive.DropdownMenuPortalProps>;
|
|
7
|
+
declare const DropdownMenuSub: React.FC<DropdownMenuPrimitive.DropdownMenuSubProps>;
|
|
8
|
+
declare const DropdownMenuRadioGroup: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuRadioGroupProps & React.RefAttributes<HTMLDivElement>>;
|
|
9
|
+
declare const DropdownMenuSubTrigger: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSubTriggerProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
10
|
+
inset?: boolean;
|
|
11
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
12
|
+
declare const DropdownMenuSubContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSubContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
13
|
+
declare const DropdownMenuContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
14
|
+
container?: HTMLElement | null;
|
|
15
|
+
/**
|
|
16
|
+
* Fires when the menu opens, before Radix moves focus into the content;
|
|
17
|
+
* call `event.preventDefault()` to keep focus where it is (e.g. for
|
|
18
|
+
* hover-opened menus). Radix's Menu supports this at runtime but omits
|
|
19
|
+
* it from the public DropdownMenu.Content prop type, so it is declared
|
|
20
|
+
* here and forwarded via the props spread.
|
|
21
|
+
*/
|
|
22
|
+
onOpenAutoFocus?: (event: Event) => void;
|
|
23
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
24
|
+
declare const DropdownMenuItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
25
|
+
inset?: boolean;
|
|
26
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
27
|
+
declare const DropdownMenuCheckboxItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuCheckboxItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
28
|
+
declare const DropdownMenuRadioItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuRadioItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
29
|
+
declare const DropdownMenuLabel: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
30
|
+
inset?: boolean;
|
|
31
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
32
|
+
declare const DropdownMenuSeparator: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
33
|
+
declare const DropdownMenuShortcut: {
|
|
34
|
+
({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>): React.JSX.Element;
|
|
35
|
+
displayName: string;
|
|
36
|
+
};
|
|
37
|
+
export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuGroup, DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuRadioGroup, };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useJsonViewsPortalContainer } from '../surface.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
5
|
+
import { cn } from '../lib/cn.js';
|
|
6
|
+
const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
7
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
8
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
9
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
10
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
11
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
12
|
+
const DropdownMenuSubTrigger = React.forwardRef(({ className, inset, children, ...props }, ref) => (_jsxs(DropdownMenuPrimitive.SubTrigger, { ref: ref, className: cn('flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent', inset && 'pl-8', className), ...props, children: [children, _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ml-auto h-4 w-4", children: _jsx("path", { d: "m9 18 6-6-6-6" }) })] })));
|
|
13
|
+
DropdownMenuSubTrigger.displayName =
|
|
14
|
+
DropdownMenuPrimitive.SubTrigger.displayName;
|
|
15
|
+
const DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) => (_jsx(DropdownMenuPrimitive.SubContent, { ref: ref, className: cn('z-[100] min-w-[8rem] overflow-hidden rounded-md border border-border bg-card p-1 text-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', className), ...props })));
|
|
16
|
+
DropdownMenuSubContent.displayName =
|
|
17
|
+
DropdownMenuPrimitive.SubContent.displayName;
|
|
18
|
+
const DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, container, ...props }, ref) => {
|
|
19
|
+
const defaultPortalContainer = useJsonViewsPortalContainer();
|
|
20
|
+
const content = (_jsx(DropdownMenuPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn('z-[100] min-w-[8rem] overflow-hidden rounded-md border border-border bg-card p-1 text-foreground shadow-lg', className), ...props }));
|
|
21
|
+
return (_jsx(DropdownMenuPrimitive.Portal, { container: container ?? defaultPortalContainer, children: content }));
|
|
22
|
+
});
|
|
23
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
24
|
+
const DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Item, { ref: ref, className: cn('relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent focus:bg-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-50', inset && 'pl-8', className), ...props })));
|
|
25
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
26
|
+
const DropdownMenuCheckboxItem = React.forwardRef(({ className, children, checked, ...props }, ref) => (_jsxs(DropdownMenuPrimitive.CheckboxItem, { ref: ref, className: cn('relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', className), checked: checked, ...props, children: [_jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-4 w-4", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) }) }) }), children] })));
|
|
27
|
+
DropdownMenuCheckboxItem.displayName =
|
|
28
|
+
DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
29
|
+
const DropdownMenuRadioItem = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(DropdownMenuPrimitive.RadioItem, { ref: ref, className: cn('relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', className), ...props, children: [_jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-2 w-2 fill-current", children: _jsx("circle", { cx: "12", cy: "12", r: "10" }) }) }) }), children] })));
|
|
30
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
31
|
+
const DropdownMenuLabel = React.forwardRef(({ className, inset, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Label, { ref: ref, className: cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className), ...props })));
|
|
32
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
33
|
+
const DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Separator, { ref: ref, className: cn('-mx-1 my-1 h-px bg-border', className), ...props })));
|
|
34
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
35
|
+
const DropdownMenuShortcut = ({ className, ...props }) => {
|
|
36
|
+
return (_jsx("span", { className: cn('ml-auto text-xs tracking-widest opacity-60', className), ...props }));
|
|
37
|
+
};
|
|
38
|
+
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
|
39
|
+
export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuGroup, DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuRadioGroup, };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MouseEvent, ReactNode } from 'react';
|
|
2
|
+
export declare function InlineFeedbackAction({ ariaLabel, className, dataId, disabled, feedback, icon, label, onClick, tone, title, }: {
|
|
3
|
+
ariaLabel?: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
dataId?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
feedback?: string;
|
|
8
|
+
icon: ReactNode;
|
|
9
|
+
label: string;
|
|
10
|
+
onClick: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
11
|
+
tone?: 'default' | 'strong' | 'destructive';
|
|
12
|
+
title?: string;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../lib/cn.js';
|
|
3
|
+
export function InlineFeedbackAction({ ariaLabel, className, dataId, disabled = false, feedback, icon, label, onClick, tone = 'default', title, }) {
|
|
4
|
+
const showingFeedback = Boolean(feedback);
|
|
5
|
+
return (_jsxs("button", { type: "button", "data-id": dataId, "data-feedback-active": showingFeedback ? 'true' : 'false', "aria-label": ariaLabel ?? (showingFeedback ? feedback : label), "aria-live": "polite", title: title ?? (showingFeedback ? feedback : label), disabled: disabled, className: cn('json-views-inline-feedback-action', showingFeedback && 'is-feedback', tone === 'strong' && 'is-strong', tone === 'destructive' && 'is-destructive', className), onClick: onClick, children: [icon, _jsx("span", { className: "json-views-inline-feedback-label", children: feedback })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ChoiceOption } from './choice-control.js';
|
|
2
|
+
export interface MenuSelectProps {
|
|
3
|
+
ariaLabel: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
emptyOptionLabel?: string;
|
|
7
|
+
onChange: (value: string) => void;
|
|
8
|
+
options: ReadonlyArray<ChoiceOption>;
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
/** The shared compact dropdown used inside JSON Views menus and popovers. */
|
|
12
|
+
export declare function MenuSelect({ ariaLabel, className, disabled, emptyOptionLabel, onChange, options, value, }: MenuSelectProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../lib/cn.js';
|
|
3
|
+
import { ChoiceControl } from './choice-control.js';
|
|
4
|
+
/** The shared compact dropdown used inside JSON Views menus and popovers. */
|
|
5
|
+
export function MenuSelect({ ariaLabel, className, disabled, emptyOptionLabel, onChange, options, value, }) {
|
|
6
|
+
return (_jsx(ChoiceControl, { "aria-label": ariaLabel, className: cn('w-full', className), disabled: disabled, emptyOptionLabel: emptyOptionLabel, mode: "single", nonModal: true, options: options, profile: "menu", value: value ? [value] : [], onValueChange: (next) => onChange(next[0] ?? '') }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type ChoiceControlProfile, type ChoiceOption } from './choice-control.js';
|
|
3
|
+
export type MultiSelectOption = ChoiceOption;
|
|
4
|
+
export interface MultiSelectProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
5
|
+
value: string[];
|
|
6
|
+
onChange: (next: string[]) => void;
|
|
7
|
+
options: ReadonlyArray<string | number | MultiSelectOption>;
|
|
8
|
+
profile?: ChoiceControlProfile;
|
|
9
|
+
creatable?: boolean;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
readOnly?: boolean;
|
|
12
|
+
invalid?: boolean;
|
|
13
|
+
openOnMount?: boolean;
|
|
14
|
+
onDismiss?: () => void;
|
|
15
|
+
onSelectionBlur?: () => void;
|
|
16
|
+
onOptionColorChange?: (optionValue: string, color: string) => void;
|
|
17
|
+
}
|
|
18
|
+
export declare const MultiSelect: React.ForwardRefExoticComponent<MultiSelectProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { ChoiceControl, } from './choice-control.js';
|
|
4
|
+
export const MultiSelect = React.forwardRef(({ options, onChange, profile = 'plain', creatable = false, invalid = false, ...props }, ref) => (_jsx(ChoiceControl, { ref: ref, mode: "multiple", profile: profile, creatable: creatable, invalid: invalid, options: options.map((option) => typeof option === 'object'
|
|
5
|
+
? option
|
|
6
|
+
: { value: String(option), label: String(option) }), onValueChange: onChange, ...props })));
|
|
7
|
+
MultiSelect.displayName = 'MultiSelect';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface NumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type' | 'onChange' | 'value'> {
|
|
3
|
+
value: string;
|
|
4
|
+
onChange: (value: string) => void;
|
|
5
|
+
min?: number;
|
|
6
|
+
max?: number;
|
|
7
|
+
step?: number;
|
|
8
|
+
}
|
|
9
|
+
declare const NumberInput: React.ForwardRefExoticComponent<NumberInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
10
|
+
export { NumberInput };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cn } from '../lib/cn.js';
|
|
4
|
+
const NumberInput = React.forwardRef(({ className, value, onChange, ...props }, ref) => {
|
|
5
|
+
return (_jsx("input", { ref: ref, type: "text", inputMode: "decimal", value: value, onChange: (e) => onChange(e.target.value), className: cn('w-full rounded-md border border-border bg-card px-3 py-2 text-sm transition-colors focus:border-transparent focus:outline-none focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50', className), ...props }));
|
|
6
|
+
});
|
|
7
|
+
NumberInput.displayName = 'NumberInput';
|
|
8
|
+
export { NumberInput };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare const AUTOMATIC_OPTION_COLORS: readonly ["blue", "green", "yellow", "orange", "purple", "pink", "red", "gray"];
|
|
2
|
+
/** Reserve manual colors first, then balance automatic assignments across the full palette. */
|
|
3
|
+
export declare function optionColorsForValues(values: readonly string[], explicit?: Readonly<Record<string, string>>, offset?: number): Record<string, (typeof AUTOMATIC_OPTION_COLORS)[number]>;
|
|
4
|
+
export declare function optionColorForLabel(value: string): string;
|
|
5
|
+
export interface OptionPillProps {
|
|
6
|
+
label: string;
|
|
7
|
+
color?: string;
|
|
8
|
+
onRemove?: () => void;
|
|
9
|
+
className?: string;
|
|
10
|
+
labelClassName?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function OptionPill({ label, color, onRemove, className, labelClassName }: OptionPillProps): import("react").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { X } from 'lucide-react';
|
|
3
|
+
import { cn } from '../lib/cn.js';
|
|
4
|
+
const OPTION_PILL_COLORS = {
|
|
5
|
+
gray: 'bg-muted text-foreground',
|
|
6
|
+
blue: 'bg-muted text-foreground',
|
|
7
|
+
green: 'bg-muted text-foreground',
|
|
8
|
+
yellow: 'bg-muted text-foreground',
|
|
9
|
+
orange: 'bg-muted text-foreground',
|
|
10
|
+
red: 'bg-muted text-foreground',
|
|
11
|
+
purple: 'bg-muted text-foreground',
|
|
12
|
+
pink: 'bg-muted text-foreground',
|
|
13
|
+
};
|
|
14
|
+
const AUTOMATIC_OPTION_COLORS = ['blue', 'green', 'yellow', 'orange', 'purple', 'pink', 'red', 'gray'];
|
|
15
|
+
/** Reserve manual colors first, then balance automatic assignments across the full palette. */
|
|
16
|
+
export function optionColorsForValues(values, explicit = {}, offset = 0) {
|
|
17
|
+
const colors = Object.create(null);
|
|
18
|
+
const uses = new Map(AUTOMATIC_OPTION_COLORS.map((color) => [color, 0]));
|
|
19
|
+
const start = ((offset % AUTOMATIC_OPTION_COLORS.length) + AUTOMATIC_OPTION_COLORS.length) % AUTOMATIC_OPTION_COLORS.length;
|
|
20
|
+
const rotation = [...AUTOMATIC_OPTION_COLORS.slice(start), ...AUTOMATIC_OPTION_COLORS.slice(0, start)];
|
|
21
|
+
const unique = [...new Set(values)];
|
|
22
|
+
for (const value of unique) {
|
|
23
|
+
const color = explicit[value];
|
|
24
|
+
if (!uses.has(color))
|
|
25
|
+
continue;
|
|
26
|
+
colors[value] = color;
|
|
27
|
+
uses.set(color, uses.get(color) + 1);
|
|
28
|
+
}
|
|
29
|
+
for (const value of unique) {
|
|
30
|
+
if (colors[value])
|
|
31
|
+
continue;
|
|
32
|
+
const color = rotation.reduce((best, candidate) => uses.get(candidate) < uses.get(best) ? candidate : best);
|
|
33
|
+
colors[value] = color;
|
|
34
|
+
uses.set(color, uses.get(color) + 1);
|
|
35
|
+
}
|
|
36
|
+
return colors;
|
|
37
|
+
}
|
|
38
|
+
export function optionColorForLabel(value) {
|
|
39
|
+
const hash = [...value].reduce((total, character) => ((total * 31) + (character.codePointAt(0) ?? 0)) >>> 0, 0);
|
|
40
|
+
return AUTOMATIC_OPTION_COLORS[hash % AUTOMATIC_OPTION_COLORS.length];
|
|
41
|
+
}
|
|
42
|
+
export function OptionPill({ label, color, onRemove, className, labelClassName }) {
|
|
43
|
+
const resolvedColor = color && OPTION_PILL_COLORS[color]
|
|
44
|
+
? color
|
|
45
|
+
: optionColorForLabel(label);
|
|
46
|
+
return (_jsxs("span", { "data-id": "option-pill", "data-option-color": resolvedColor, className: cn('inline-flex w-fit shrink-0 items-center gap-1 whitespace-nowrap rounded-md px-2 py-0.5 text-xs font-medium', OPTION_PILL_COLORS[resolvedColor], className), children: [_jsx("span", { className: labelClassName, children: label }), onRemove && (_jsx("button", { type: "button", "aria-label": `Remove ${label}`, className: "-mr-1 grid h-4 w-4 place-items-center rounded-sm opacity-70 hover:bg-black/10 hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", onClick: (event) => {
|
|
47
|
+
event.stopPropagation();
|
|
48
|
+
onRemove();
|
|
49
|
+
}, children: _jsx(X, { className: "h-3 w-3" }) }))] }));
|
|
50
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
|
3
|
+
declare const Tabs: React.ForwardRefExoticComponent<TabsPrimitive.TabsProps & React.RefAttributes<HTMLDivElement>>;
|
|
4
|
+
declare const TabsList: React.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsListProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
5
|
+
declare const TabsTrigger: React.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
6
|
+
declare const TabsContent: React.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
7
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
|
4
|
+
import { cn } from '../lib/cn.js';
|
|
5
|
+
const Tabs = TabsPrimitive.Root;
|
|
6
|
+
const TabsList = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.List, { ref: ref, className: cn('inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground', className), ...props })));
|
|
7
|
+
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
8
|
+
const TabsTrigger = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.Trigger, { ref: ref, className: cn('inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow', className), ...props })));
|
|
9
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
10
|
+
const TabsContent = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.Content, { ref: ref, className: cn('mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', className), ...props })));
|
|
11
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
12
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cn } from '../lib/cn.js';
|
|
4
|
+
const Textarea = React.forwardRef(({ className, ...props }, ref) => {
|
|
5
|
+
return (_jsx("textarea", { ref: ref, className: cn('w-full rounded-md border border-border bg-card px-3 py-1 text-sm transition-colors focus:border-transparent focus:outline-none focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50', className), ...props }));
|
|
6
|
+
});
|
|
7
|
+
Textarea.displayName = 'Textarea';
|
|
8
|
+
export { Textarea };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
3
|
+
declare const TooltipProvider: React.FC<TooltipPrimitive.TooltipProviderProps>;
|
|
4
|
+
declare const Tooltip: React.FC<TooltipPrimitive.TooltipProps>;
|
|
5
|
+
/**
|
|
6
|
+
* Radix opens a tooltip on *any* focus, including the programmatic focus a
|
|
7
|
+
* dialog or popover moves onto its first focusable child when it mounts —
|
|
8
|
+
* which pops a tooltip the user never pointed at. Gate that to keyboard focus:
|
|
9
|
+
* Radix composes its own focus handler behind this one and skips it once
|
|
10
|
+
* default is prevented, so hover and real keyboard tabbing still open normally.
|
|
11
|
+
*/
|
|
12
|
+
declare const TooltipTrigger: React.ForwardRefExoticComponent<Omit<TooltipPrimitive.TooltipTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
13
|
+
declare const TooltipContent: React.ForwardRefExoticComponent<Omit<TooltipPrimitive.TooltipContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
14
|
+
export interface ConditionalTooltipProps {
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
label: React.ReactNode;
|
|
17
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
18
|
+
/** Cross-axis alignment of the tooltip against the trigger. `'start'`
|
|
19
|
+
* left-aligns the tooltip to the trigger when `side` is `'top'`/`'bottom'`. */
|
|
20
|
+
align?: 'start' | 'center' | 'end';
|
|
21
|
+
/** Pixel offset along the alignment axis. With `align="start"` on a
|
|
22
|
+
* `'top'`/`'bottom'` side, a negative value nudges the tooltip left. */
|
|
23
|
+
alignOffset?: number;
|
|
24
|
+
/** Open delay in ms. Omit to inherit the provider default; pass `0` for an
|
|
25
|
+
* instant tooltip on hover/focus. */
|
|
26
|
+
delayDuration?: number;
|
|
27
|
+
tooltipClassName?: string;
|
|
28
|
+
children: React.ReactElement;
|
|
29
|
+
}
|
|
30
|
+
export declare function ConditionalTooltip({ enabled, label, side, align, alignOffset, delayDuration, tooltipClassName, children, }: ConditionalTooltipProps): React.JSX.Element;
|
|
31
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useJsonViewsPortalContainer } from '../surface.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
5
|
+
import { cn } from '../lib/cn.js';
|
|
6
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
7
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
8
|
+
/**
|
|
9
|
+
* Radix opens a tooltip on *any* focus, including the programmatic focus a
|
|
10
|
+
* dialog or popover moves onto its first focusable child when it mounts —
|
|
11
|
+
* which pops a tooltip the user never pointed at. Gate that to keyboard focus:
|
|
12
|
+
* Radix composes its own focus handler behind this one and skips it once
|
|
13
|
+
* default is prevented, so hover and real keyboard tabbing still open normally.
|
|
14
|
+
*/
|
|
15
|
+
const TooltipTrigger = React.forwardRef(({ onFocus, ...props }, ref) => (_jsx(TooltipPrimitive.Trigger, { ref: ref, onFocus: (event) => {
|
|
16
|
+
onFocus?.(event);
|
|
17
|
+
if (!event.defaultPrevented && !event.currentTarget.matches(':focus-visible')) {
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
}
|
|
20
|
+
}, ...props })));
|
|
21
|
+
TooltipTrigger.displayName = TooltipPrimitive.Trigger.displayName;
|
|
22
|
+
const TooltipContent = React.forwardRef(({ className, sideOffset = 6, ...props }, ref) => {
|
|
23
|
+
const portalContainer = useJsonViewsPortalContainer();
|
|
24
|
+
return (_jsx(TooltipPrimitive.Portal, { container: portalContainer, children: _jsx(TooltipPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn(
|
|
25
|
+
// Top of the overlay ladder (slide panels 60/70, dialog 80/90, menus
|
|
26
|
+
// 100). The content portals to `body`, so a lower value paints behind
|
|
27
|
+
// whichever surface holds the trigger.
|
|
28
|
+
'z-[110] overflow-hidden rounded-md border border-border bg-card px-2.5 py-1.5 text-xs text-foreground shadow-lg', 'animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', className), ...props }) }));
|
|
29
|
+
});
|
|
30
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
31
|
+
export function ConditionalTooltip({ enabled = true, label, side = 'bottom', align, alignOffset, delayDuration, tooltipClassName, children, }) {
|
|
32
|
+
if (!enabled) {
|
|
33
|
+
return children;
|
|
34
|
+
}
|
|
35
|
+
return (_jsxs(Tooltip, { delayDuration: delayDuration, children: [_jsx(TooltipTrigger, { asChild: true, children: children }), _jsx(TooltipContent, { side: side, align: align, alignOffset: alignOffset, className: tooltipClassName, children: label })] }));
|
|
36
|
+
}
|
|
37
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|