@oprt/react 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +105 -2
- package/dist/ActionStatus/ActionStatus.js +104 -0
- package/dist/ActionStatus/ActionStatus.manifest.js +64 -0
- package/dist/ActionStatus/index.js +7 -0
- package/dist/Alert/Alert.js +86 -0
- package/dist/Alert/Alert.manifest.js +36 -0
- package/dist/Alert/index.js +7 -0
- package/dist/AppShell/AppShell.js +103 -0
- package/dist/AppShell/AppShell.manifest.js +66 -0
- package/dist/AppShell/index.js +9 -0
- package/dist/AttachmentPreview/AttachmentPreview.js +145 -0
- package/dist/AttachmentPreview/AttachmentPreview.manifest.js +42 -0
- package/dist/AttachmentPreview/index.js +9 -0
- package/dist/Avatar/Avatar.js +87 -0
- package/dist/Avatar/Avatar.manifest.js +25 -0
- package/dist/Avatar/index.js +7 -0
- package/dist/Badge/Badge.js +124 -0
- package/dist/Badge/Badge.manifest.js +41 -0
- package/dist/Badge/index.js +7 -0
- package/dist/Breadcrumb/Breadcrumb.js +64 -0
- package/dist/Breadcrumb/Breadcrumb.manifest.js +28 -0
- package/dist/Breadcrumb/index.js +7 -0
- package/dist/BulkActionBar/BulkActionBar.js +145 -0
- package/dist/BulkActionBar/BulkActionBar.manifest.js +46 -0
- package/dist/BulkActionBar/index.js +9 -0
- package/dist/BulkActionBar/selection.js +44 -0
- package/dist/BulkResults/BulkResults.js +269 -0
- package/dist/BulkResults/BulkResults.manifest.js +45 -0
- package/dist/BulkResults/index.js +7 -0
- package/dist/Button/Button.js +170 -0
- package/dist/Button/Button.manifest.js +36 -0
- package/dist/Button/index.js +7 -0
- package/dist/ButtonGroup/ButtonGroup.js +60 -0
- package/dist/ButtonGroup/ButtonGroup.manifest.js +22 -0
- package/dist/ButtonGroup/index.js +7 -0
- package/dist/Calendar/Calendar.js +106 -0
- package/dist/Calendar/Calendar.manifest.js +32 -0
- package/dist/Calendar/index.js +7 -0
- package/dist/Card/Card.js +70 -0
- package/dist/Card/Card.manifest.js +57 -0
- package/dist/Card/index.js +16 -0
- package/dist/Chart/Chart.js +1200 -0
- package/dist/Chart/Chart.manifest.js +110 -0
- package/dist/Chart/ChartControls.js +190 -0
- package/dist/Chart/geometry.js +217 -0
- package/dist/Chart/index.js +9 -0
- package/dist/Checkbox/Checkbox.js +166 -0
- package/dist/Checkbox/Checkbox.manifest.js +38 -0
- package/dist/Checkbox/index.js +7 -0
- package/dist/Collapsible/Collapsible.js +177 -0
- package/dist/Collapsible/Collapsible.manifest.js +54 -0
- package/dist/Collapsible/index.js +12 -0
- package/dist/CollectionState/CollectionState.js +68 -0
- package/dist/CollectionState/CollectionState.manifest.js +44 -0
- package/dist/CollectionState/index.js +7 -0
- package/dist/ComboBox/ComboBox.js +221 -0
- package/dist/ComboBox/ComboBox.manifest.js +87 -0
- package/dist/ComboBox/index.js +7 -0
- package/dist/CommandPalette/CommandPalette.js +276 -0
- package/dist/CommandPalette/CommandPalette.manifest.js +24 -0
- package/dist/CommandPalette/index.js +7 -0
- package/dist/ConfirmDialog/ConfirmDialog.js +84 -0
- package/dist/ConfirmDialog/ConfirmDialog.manifest.js +32 -0
- package/dist/ConfirmDialog/index.js +7 -0
- package/dist/ContextMenu/ContextMenu.js +192 -0
- package/dist/ContextMenu/ContextMenu.manifest.js +41 -0
- package/dist/ContextMenu/index.js +7 -0
- package/dist/CoordinateInput/CoordinateInput.js +141 -0
- package/dist/CoordinateInput/CoordinateInput.manifest.js +36 -0
- package/dist/CoordinateInput/index.js +9 -0
- package/dist/CopyButton/CopyButton.js +129 -0
- package/dist/CopyButton/CopyButton.manifest.js +35 -0
- package/dist/CopyButton/index.js +7 -0
- package/dist/DataGrid/DataGrid.js +459 -0
- package/dist/DataGrid/DataGrid.manifest.js +135 -0
- package/dist/DataGrid/PageControls.js +41 -0
- package/dist/DataGrid/index.js +9 -0
- package/dist/DataTable/DataTable.js +375 -0
- package/dist/DataTable/DataTable.manifest.js +86 -0
- package/dist/DataTable/index.js +9 -0
- package/dist/DataTable/sort.js +45 -0
- package/dist/DataTable/useScrollsSideways.js +61 -0
- package/dist/DataTable/useTableWindow.js +185 -0
- package/dist/DatePicker/DatePicker.js +187 -0
- package/dist/DatePicker/DatePicker.manifest.js +40 -0
- package/dist/DatePicker/index.js +7 -0
- package/dist/DateRangePicker/DateRangePicker.js +156 -0
- package/dist/DateRangePicker/DateRangePicker.manifest.js +41 -0
- package/dist/DateRangePicker/index.js +7 -0
- package/dist/DateTimePicker/DateTimePicker.js +213 -0
- package/dist/DateTimePicker/DateTimePicker.manifest.js +44 -0
- package/dist/DateTimePicker/index.js +7 -0
- package/dist/DescriptionList/DescriptionList.js +93 -0
- package/dist/DescriptionList/DescriptionList.manifest.js +40 -0
- package/dist/DescriptionList/index.js +9 -0
- package/dist/Dialog/Dialog.js +214 -0
- package/dist/Dialog/Dialog.manifest.js +31 -0
- package/dist/Dialog/index.js +7 -0
- package/dist/Divider/Divider.js +64 -0
- package/dist/Divider/Divider.manifest.js +21 -0
- package/dist/Divider/index.js +7 -0
- package/dist/DropdownMenu/DropdownMenu.js +170 -0
- package/dist/DropdownMenu/DropdownMenu.manifest.js +82 -0
- package/dist/DropdownMenu/index.js +16 -0
- package/dist/EntityPreview/EntityPreview.js +82 -0
- package/dist/EntityPreview/EntityPreview.manifest.js +43 -0
- package/dist/EntityPreview/index.js +7 -0
- package/dist/EventTimeline/EventTimeline.js +249 -0
- package/dist/EventTimeline/EventTimeline.manifest.js +57 -0
- package/dist/EventTimeline/index.js +7 -0
- package/dist/Field/Field.js +81 -0
- package/dist/Field/Field.manifest.js +32 -0
- package/dist/Field/index.js +9 -0
- package/dist/FileUpload/FileUpload.js +215 -0
- package/dist/FileUpload/FileUpload.manifest.js +48 -0
- package/dist/FileUpload/index.js +7 -0
- package/dist/FilterBar/FilterBar.js +173 -0
- package/dist/FilterBar/FilterBar.manifest.js +49 -0
- package/dist/FilterBar/index.js +7 -0
- package/dist/FormErrorSummary/FormErrorSummary.js +70 -0
- package/dist/FormErrorSummary/FormErrorSummary.manifest.js +29 -0
- package/dist/FormErrorSummary/index.js +9 -0
- package/dist/FormSection/FormSection.js +51 -0
- package/dist/FormSection/FormSection.manifest.js +30 -0
- package/dist/FormSection/index.js +7 -0
- package/dist/InlineEdit/InlineEdit.js +261 -0
- package/dist/InlineEdit/InlineEdit.manifest.js +40 -0
- package/dist/InlineEdit/index.js +7 -0
- package/dist/Input/Input.js +126 -0
- package/dist/Input/Input.manifest.js +49 -0
- package/dist/Input/index.js +7 -0
- package/dist/Inspector/Inspector.js +63 -0
- package/dist/Inspector/Inspector.manifest.js +35 -0
- package/dist/Inspector/index.js +7 -0
- package/dist/JobStatus/JobStatus.js +118 -0
- package/dist/JobStatus/JobStatus.manifest.js +53 -0
- package/dist/JobStatus/index.js +7 -0
- package/dist/LayerControl/LayerControl.js +199 -0
- package/dist/LayerControl/LayerControl.manifest.js +34 -0
- package/dist/LayerControl/index.js +7 -0
- package/dist/Link/Link.js +83 -0
- package/dist/Link/Link.manifest.js +37 -0
- package/dist/Link/index.js +7 -0
- package/dist/MapControls/MapControls.js +122 -0
- package/dist/MapControls/MapControls.manifest.js +31 -0
- package/dist/MapControls/index.js +7 -0
- package/dist/MapLegend/MapLegend.js +89 -0
- package/dist/MapLegend/MapLegend.manifest.js +29 -0
- package/dist/MapLegend/index.js +7 -0
- package/dist/MapScale/MapScale.js +96 -0
- package/dist/MapScale/MapScale.manifest.js +31 -0
- package/dist/MapScale/index.js +7 -0
- package/dist/MediaFrame/MediaFrame.js +113 -0
- package/dist/MediaFrame/MediaFrame.manifest.js +57 -0
- package/dist/MediaFrame/index.js +7 -0
- package/dist/MultiSelect/MultiSelect.js +113 -0
- package/dist/MultiSelect/MultiSelect.manifest.js +51 -0
- package/dist/MultiSelect/index.js +7 -0
- package/dist/NumberInput/NumberInput.js +210 -0
- package/dist/NumberInput/NumberInput.manifest.js +56 -0
- package/dist/NumberInput/index.js +7 -0
- package/dist/OperationalStatus/OperationalStatus.js +89 -0
- package/dist/OperationalStatus/OperationalStatus.manifest.js +39 -0
- package/dist/OperationalStatus/index.js +9 -0
- package/dist/PlaybackControls/PlaybackControls.js +289 -0
- package/dist/PlaybackControls/PlaybackControls.manifest.js +85 -0
- package/dist/PlaybackControls/index.js +7 -0
- package/dist/Popover/Popover.js +302 -0
- package/dist/Popover/Popover.manifest.js +50 -0
- package/dist/Popover/index.js +7 -0
- package/dist/Progress/Progress.js +132 -0
- package/dist/Progress/Progress.manifest.js +30 -0
- package/dist/Progress/index.js +7 -0
- package/dist/PropertyFilter/PropertyFilter.js +306 -0
- package/dist/PropertyFilter/PropertyFilter.manifest.js +32 -0
- package/dist/PropertyFilter/index.js +11 -0
- package/dist/PropertyFilter/tokenError.js +17 -0
- package/dist/RadioGroup/RadioGroup.js +143 -0
- package/dist/RadioGroup/RadioGroup.manifest.js +44 -0
- package/dist/RadioGroup/index.js +9 -0
- package/dist/Select/Select.js +219 -0
- package/dist/Select/Select.manifest.js +77 -0
- package/dist/Select/index.js +9 -0
- package/dist/Sidebar/Sidebar.js +146 -0
- package/dist/Sidebar/Sidebar.manifest.js +72 -0
- package/dist/Sidebar/index.js +18 -0
- package/dist/Skeleton/Skeleton.js +76 -0
- package/dist/Skeleton/Skeleton.manifest.js +30 -0
- package/dist/Skeleton/index.js +7 -0
- package/dist/Slider/Slider.js +246 -0
- package/dist/Slider/Slider.manifest.js +56 -0
- package/dist/Slider/index.js +7 -0
- package/dist/Sparkline/Sparkline.js +135 -0
- package/dist/Sparkline/Sparkline.manifest.js +37 -0
- package/dist/Sparkline/index.js +7 -0
- package/dist/SplitPane/SplitPane.js +316 -0
- package/dist/SplitPane/SplitPane.manifest.js +79 -0
- package/dist/SplitPane/index.js +7 -0
- package/dist/StatCard/StatCard.js +163 -0
- package/dist/StatCard/StatCard.manifest.js +45 -0
- package/dist/StatCard/index.js +7 -0
- package/dist/Tabs/Tabs.js +181 -0
- package/dist/Tabs/Tabs.manifest.js +72 -0
- package/dist/Tabs/index.js +16 -0
- package/dist/Tabs/useTabsIndicator.js +71 -0
- package/dist/Tabs/useTabsScroll.js +123 -0
- package/dist/TagGroup/TagGroup.js +142 -0
- package/dist/TagGroup/TagGroup.manifest.js +35 -0
- package/dist/TagGroup/index.js +7 -0
- package/dist/Textarea/Textarea.js +68 -0
- package/dist/Textarea/Textarea.manifest.js +25 -0
- package/dist/Textarea/index.js +7 -0
- package/dist/TimeField/TimeField.js +118 -0
- package/dist/TimeField/TimeField.manifest.js +42 -0
- package/dist/TimeField/index.js +7 -0
- package/dist/TimeRange/TimeRange.js +184 -0
- package/dist/TimeRange/TimeRange.manifest.js +48 -0
- package/dist/TimeRange/index.js +9 -0
- package/dist/Toast/Toast.js +188 -0
- package/dist/Toast/Toast.manifest.js +77 -0
- package/dist/Toast/index.js +9 -0
- package/dist/Toggle/Toggle.js +154 -0
- package/dist/Toggle/Toggle.manifest.js +54 -0
- package/dist/Toggle/index.js +7 -0
- package/dist/Toolbar/Toolbar.js +86 -0
- package/dist/Toolbar/Toolbar.manifest.js +70 -0
- package/dist/Toolbar/index.js +12 -0
- package/dist/Tooltip/Tooltip.js +123 -0
- package/dist/Tooltip/Tooltip.manifest.js +27 -0
- package/dist/Tooltip/index.js +7 -0
- package/dist/TreeView/TreeView.js +129 -0
- package/dist/TreeView/TreeView.manifest.js +56 -0
- package/dist/TreeView/index.js +7 -0
- package/dist/UploadQueue/UploadQueue.js +296 -0
- package/dist/UploadQueue/UploadQueue.manifest.js +34 -0
- package/dist/UploadQueue/index.js +7 -0
- package/dist/_internal/MotionPopover.js +113 -0
- package/dist/_internal/aria.js +40 -0
- package/dist/_internal/bytes.js +18 -0
- package/dist/_internal/controlStyles.js +26 -0
- package/dist/_internal/date.js +179 -0
- package/dist/_internal/devValidate.js +51 -0
- package/dist/_internal/fieldIds.js +50 -0
- package/dist/_internal/hydration.js +74 -0
- package/dist/_internal/manifest-entry.js +0 -0
- package/dist/_internal/mergeRefs.js +68 -0
- package/dist/_internal/motion.js +138 -0
- package/dist/_internal/navigationLandmark.js +13 -0
- package/dist/_internal/numberText.js +176 -0
- package/dist/_internal/overlay.js +53 -0
- package/dist/_internal/segmentField.js +125 -0
- package/dist/_internal/suggestions.js +20 -0
- package/dist/_internal/timestamp.js +25 -0
- package/dist/_internal/unavailable.js +13 -0
- package/dist/_internal/useActionFocus.js +27 -0
- package/dist/_internal/useElementWidth.js +37 -0
- package/dist/_internal/useIsMounted.js +18 -0
- package/dist/_internal/useIsomorphicLayoutEffect.js +8 -0
- package/dist/_internal/useRestoreFocusOnRemoval.js +57 -0
- package/dist/index.cjs +17831 -0
- package/dist/index.d.cts +2330 -0
- package/dist/index.d.ts +2330 -0
- package/dist/index.js +407 -0
- package/dist/version.js +8 -0
- package/package.json +80 -3
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// src/ComboBox/ComboBox.manifest.ts
|
|
2
|
+
var ComboBoxManifest = {
|
|
3
|
+
ComboBox: {
|
|
4
|
+
description: "Searchable single-value selection with keyboard navigation and caller-supplied asynchronous results.",
|
|
5
|
+
props: {
|
|
6
|
+
motion: {
|
|
7
|
+
type: "enum",
|
|
8
|
+
values: ["auto", "none"],
|
|
9
|
+
default: "auto",
|
|
10
|
+
description: "Tactile pointer arrival, exit, and open-state chevron rotation; keyboard, assistive activation, and system reduced motion remain immediate."
|
|
11
|
+
},
|
|
12
|
+
options: {
|
|
13
|
+
type: "array",
|
|
14
|
+
required: true,
|
|
15
|
+
description: "Available options. Values must be unique and stable; labels preserve authored case.",
|
|
16
|
+
items: {
|
|
17
|
+
value: "string (required, unique)",
|
|
18
|
+
label: "string (required)",
|
|
19
|
+
disabled: "boolean (default false)"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
value: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: "Controlled selected option value, or null to clear selection. Omit for internal selection state."
|
|
25
|
+
},
|
|
26
|
+
defaultValue: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "Initial selected option value for uncontrolled use."
|
|
29
|
+
},
|
|
30
|
+
onValueChange: {
|
|
31
|
+
type: "function",
|
|
32
|
+
description: "(value: string | null) => void. Called on option selection or when clearing the input clears the selected value."
|
|
33
|
+
},
|
|
34
|
+
inputValue: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "Controlled search input text; independent from the selected value."
|
|
37
|
+
},
|
|
38
|
+
onInputChange: {
|
|
39
|
+
type: "function",
|
|
40
|
+
description: "(text: string) => void. Use with inputValue for caller-owned search and asynchronous results."
|
|
41
|
+
},
|
|
42
|
+
label: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "Visible accessible label. Alternatively, use Field or aria-label."
|
|
45
|
+
},
|
|
46
|
+
"aria-label": {
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "Accessible name when there is no visible label or surrounding Field."
|
|
49
|
+
},
|
|
50
|
+
"aria-describedby": {
|
|
51
|
+
type: "string",
|
|
52
|
+
description: "Description IDs; otherwise inherits the surrounding Field help or error message."
|
|
53
|
+
},
|
|
54
|
+
placeholder: { type: "string" },
|
|
55
|
+
disabled: { type: "boolean", default: false },
|
|
56
|
+
required: { type: "boolean", default: false },
|
|
57
|
+
size: { type: "enum", values: ["sm", "md", "lg"], default: "md" },
|
|
58
|
+
invalid: {
|
|
59
|
+
type: "boolean",
|
|
60
|
+
default: false,
|
|
61
|
+
description: "Error appearance and aria-invalid; otherwise inherits Field error state."
|
|
62
|
+
},
|
|
63
|
+
loading: {
|
|
64
|
+
type: "boolean",
|
|
65
|
+
default: false,
|
|
66
|
+
description: "Announces loading and marks input busy. Supplied options remain selectable unless disabled."
|
|
67
|
+
},
|
|
68
|
+
filter: {
|
|
69
|
+
type: "boolean",
|
|
70
|
+
default: true,
|
|
71
|
+
description: "Language-sensitive contains matching by default. Set false when the caller already filters options or supplies asynchronous results."
|
|
72
|
+
},
|
|
73
|
+
emptyMessage: { type: "string", default: "No options found." },
|
|
74
|
+
name: { type: "string", description: "Form field name; submits the selected option value." },
|
|
75
|
+
id: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "Input ID; otherwise inherits Field or React Aria's generated ID."
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
composable_with: ["Field", "DataGrid"],
|
|
81
|
+
never_inside: ["Button", "Badge"],
|
|
82
|
+
example: '<ComboBox\n label="STATION"\n options={[{ value: "alpha", label: "Alpha station" }, { value: "bravo", label: "Bravo station" }]}\n value={station}\n onValueChange={setStation}\n placeholder="Search stations\u2026"\n/>'
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
export {
|
|
86
|
+
ComboBoxManifest
|
|
87
|
+
};
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/CommandPalette/CommandPalette.tsx
|
|
4
|
+
import { Search } from "@oprt/icons";
|
|
5
|
+
import {
|
|
6
|
+
Autocomplete,
|
|
7
|
+
Dialog,
|
|
8
|
+
Header,
|
|
9
|
+
Heading,
|
|
10
|
+
Input,
|
|
11
|
+
ListBox,
|
|
12
|
+
ListBoxItem,
|
|
13
|
+
ListBoxSection,
|
|
14
|
+
Modal,
|
|
15
|
+
ModalOverlay,
|
|
16
|
+
TextField,
|
|
17
|
+
useFilter
|
|
18
|
+
} from "react-aria-components";
|
|
19
|
+
import {
|
|
20
|
+
forwardRef,
|
|
21
|
+
useCallback,
|
|
22
|
+
useEffect,
|
|
23
|
+
useMemo,
|
|
24
|
+
useRef,
|
|
25
|
+
useState
|
|
26
|
+
} from "react";
|
|
27
|
+
import clsx from "clsx";
|
|
28
|
+
import { FORCED_ACTIVE_ROW, MENU_EMPTY, MENU_ITEM_SHORTCUT, MENU_ROW } from "../_internal/overlay.js";
|
|
29
|
+
import { useIsomorphicLayoutEffect } from "../_internal/useIsomorphicLayoutEffect.js";
|
|
30
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
31
|
+
var itemStyles = `m-0 ${MENU_ROW} text-oprt-base font-medium text-oprt-text cursor-pointer list-none outline-hidden data-[focused]:border-oprt-border-focus data-[focused]:shadow-oprt-focus data-[focused]:bg-oprt-surface-3 data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed ${FORCED_ACTIVE_ROW}`;
|
|
32
|
+
var FOCUSABLE = "button, a[href], input, select, textarea, summary, [tabindex], [contenteditable]:not([contenteditable='false'])";
|
|
33
|
+
function reveal(list, row) {
|
|
34
|
+
const group = row.closest('[role="group"]');
|
|
35
|
+
const first = group?.querySelector('[role="option"]:not([aria-disabled="true"])');
|
|
36
|
+
const lead = group && list.contains(group) && first === row ? group : row;
|
|
37
|
+
const viewTop = list.getBoundingClientRect().top + list.clientTop;
|
|
38
|
+
const top = lead.getBoundingClientRect().top - viewTop;
|
|
39
|
+
const bottom = row.getBoundingClientRect().bottom - viewTop;
|
|
40
|
+
if (top < 0) list.scrollTop += top;
|
|
41
|
+
else if (bottom > list.clientHeight) list.scrollTop += bottom - list.clientHeight;
|
|
42
|
+
}
|
|
43
|
+
function useSettled(value, delay) {
|
|
44
|
+
const [settled, setSettled] = useState(value);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const timer = setTimeout(() => setSettled(value), delay);
|
|
47
|
+
return () => clearTimeout(timer);
|
|
48
|
+
}, [value, delay]);
|
|
49
|
+
return settled;
|
|
50
|
+
}
|
|
51
|
+
function useReturnFocusToOpener(isOpen) {
|
|
52
|
+
const clicked = useRef(null);
|
|
53
|
+
const opener = useRef(null);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const record = (event) => {
|
|
56
|
+
const target = event.target instanceof Element ? event.target.closest(FOCUSABLE) : null;
|
|
57
|
+
clicked.current = target;
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
if (clicked.current === target) clicked.current = null;
|
|
60
|
+
}, 0);
|
|
61
|
+
};
|
|
62
|
+
document.addEventListener("click", record, true);
|
|
63
|
+
return () => document.removeEventListener("click", record, true);
|
|
64
|
+
}, []);
|
|
65
|
+
useIsomorphicLayoutEffect(() => {
|
|
66
|
+
if (!isOpen) return;
|
|
67
|
+
opener.current = clicked.current;
|
|
68
|
+
return () => {
|
|
69
|
+
const target = opener.current;
|
|
70
|
+
opener.current = null;
|
|
71
|
+
if (!target) return;
|
|
72
|
+
requestAnimationFrame(() => {
|
|
73
|
+
const active = document.activeElement;
|
|
74
|
+
if (target.isConnected && (active === null || active === document.body))
|
|
75
|
+
target.focus({ preventScroll: true });
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
}, [isOpen]);
|
|
79
|
+
}
|
|
80
|
+
function PaletteContent({
|
|
81
|
+
commands,
|
|
82
|
+
placeholder,
|
|
83
|
+
emptyMessage,
|
|
84
|
+
onRun
|
|
85
|
+
}) {
|
|
86
|
+
const [query, setQuery] = useState("");
|
|
87
|
+
const byId = useMemo(() => {
|
|
88
|
+
const map = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const cmd of commands) map.set(cmd.id, cmd);
|
|
90
|
+
return map;
|
|
91
|
+
}, [commands]);
|
|
92
|
+
const hasGroups = useMemo(
|
|
93
|
+
() => commands.some((cmd) => cmd.group !== void 0 && cmd.group !== ""),
|
|
94
|
+
[commands]
|
|
95
|
+
);
|
|
96
|
+
const runs = useMemo(() => {
|
|
97
|
+
const out = [];
|
|
98
|
+
for (const cmd of commands) {
|
|
99
|
+
const group = hasGroups && cmd.group !== void 0 && cmd.group !== "" ? cmd.group : void 0;
|
|
100
|
+
const last = out[out.length - 1];
|
|
101
|
+
if (last && last.group === group) last.items.push(cmd);
|
|
102
|
+
else out.push({ key: `${group ?? ""}::${cmd.id}`, group, items: [cmd] });
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}, [commands, hasGroups]);
|
|
106
|
+
const disabledKeys = useMemo(
|
|
107
|
+
() => commands.filter((cmd) => cmd.disabled).map((cmd) => cmd.id),
|
|
108
|
+
[commands]
|
|
109
|
+
);
|
|
110
|
+
const { contains } = useFilter({ sensitivity: "base" });
|
|
111
|
+
const filter = useCallback(
|
|
112
|
+
(textValue, inputValue, node) => {
|
|
113
|
+
if (inputValue === "") return true;
|
|
114
|
+
if (contains(textValue, inputValue)) return true;
|
|
115
|
+
const group = byId.get(String(node.key))?.group;
|
|
116
|
+
return group !== void 0 && group !== "" ? contains(group, inputValue) : false;
|
|
117
|
+
},
|
|
118
|
+
[contains, byId]
|
|
119
|
+
);
|
|
120
|
+
const noMatch = query !== "" && !commands.some((cmd) => filter(cmd.label, query, { key: cmd.id }));
|
|
121
|
+
const announcement = useSettled(noMatch ? emptyMessage : "", 300);
|
|
122
|
+
const onAction = useCallback(
|
|
123
|
+
(key) => {
|
|
124
|
+
const cmd = byId.get(String(key));
|
|
125
|
+
if (cmd && !cmd.disabled) onRun(cmd);
|
|
126
|
+
},
|
|
127
|
+
[byId, onRun]
|
|
128
|
+
);
|
|
129
|
+
const listObserver = useRef(null);
|
|
130
|
+
const listRef = useCallback((list) => {
|
|
131
|
+
listObserver.current?.disconnect();
|
|
132
|
+
listObserver.current = null;
|
|
133
|
+
if (!list) return;
|
|
134
|
+
const observer = new MutationObserver(() => {
|
|
135
|
+
const row = list.querySelector('[role="option"][data-focused]');
|
|
136
|
+
if (row) reveal(list, row);
|
|
137
|
+
});
|
|
138
|
+
observer.observe(list, {
|
|
139
|
+
subtree: true,
|
|
140
|
+
childList: true,
|
|
141
|
+
attributes: true,
|
|
142
|
+
attributeFilter: ["data-focused"]
|
|
143
|
+
});
|
|
144
|
+
listObserver.current = observer;
|
|
145
|
+
}, []);
|
|
146
|
+
const renderItem = (cmd) => /* @__PURE__ */ jsxs(ListBoxItem, { id: cmd.id, textValue: cmd.label, className: itemStyles, children: [
|
|
147
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2u", children: [
|
|
148
|
+
cmd.icon && /* @__PURE__ */ jsx(
|
|
149
|
+
"span",
|
|
150
|
+
{
|
|
151
|
+
"aria-hidden": "true",
|
|
152
|
+
className: "shrink-0 text-oprt-text-dim forced-colors:[color:inherit]",
|
|
153
|
+
children: cmd.icon
|
|
154
|
+
}
|
|
155
|
+
),
|
|
156
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 uppercase tracking-oprt-caps oprt-wrap-anywhere", children: cmd.label })
|
|
157
|
+
] }),
|
|
158
|
+
cmd.shortcut && /* @__PURE__ */ jsx("span", { className: MENU_ITEM_SHORTCUT, children: cmd.shortcut })
|
|
159
|
+
] }, cmd.id);
|
|
160
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
161
|
+
/* @__PURE__ */ jsxs(Autocomplete, { filter, inputValue: query, onInputChange: setQuery, children: [
|
|
162
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2u px-3u border border-transparent border-b-oprt-border focus-within:border-oprt-border-focus focus-within:shadow-oprt-focus", children: [
|
|
163
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-oprt-text-dim", children: /* @__PURE__ */ jsx(Search, { size: 14 }) }),
|
|
164
|
+
/* @__PURE__ */ jsx(TextField, { "aria-label": "Command Palette", className: "w-full", children: /* @__PURE__ */ jsx(
|
|
165
|
+
Input,
|
|
166
|
+
{
|
|
167
|
+
autoFocus: true,
|
|
168
|
+
role: "combobox",
|
|
169
|
+
"aria-expanded": "true",
|
|
170
|
+
placeholder,
|
|
171
|
+
className: "w-full bg-transparent h-oprt-lg text-oprt-md normal-case text-oprt-text font-sans font-medium outline-hidden placeholder:text-oprt-text-placeholder"
|
|
172
|
+
}
|
|
173
|
+
) })
|
|
174
|
+
] }),
|
|
175
|
+
/* @__PURE__ */ jsx(
|
|
176
|
+
ListBox,
|
|
177
|
+
{
|
|
178
|
+
ref: listRef,
|
|
179
|
+
"aria-label": "Commands",
|
|
180
|
+
selectionMode: "none",
|
|
181
|
+
autoFocus: "first",
|
|
182
|
+
shouldFocusWrap: true,
|
|
183
|
+
disabledKeys,
|
|
184
|
+
onAction,
|
|
185
|
+
className: "m-0 min-h-0 p-0 list-none max-h-40u overflow-auto outline-hidden",
|
|
186
|
+
renderEmptyState: () => /* @__PURE__ */ jsx("div", { className: `${MENU_EMPTY} list-none`, children: emptyMessage }),
|
|
187
|
+
children: runs.map(
|
|
188
|
+
(run) => run.group !== void 0 ? /* @__PURE__ */ jsxs(ListBoxSection, { id: run.key, children: [
|
|
189
|
+
/* @__PURE__ */ jsx(Header, { className: "m-0 px-3u py-1u text-oprt-sm uppercase tracking-oprt-sm text-oprt-text-dim list-none oprt-wrap-anywhere", children: run.group }),
|
|
190
|
+
run.items.map(renderItem)
|
|
191
|
+
] }, run.key) : run.items.map(renderItem)
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
] }),
|
|
196
|
+
/* @__PURE__ */ jsx("div", { role: "status", className: "sr-only", children: announcement })
|
|
197
|
+
] });
|
|
198
|
+
}
|
|
199
|
+
var CommandPalette = forwardRef(
|
|
200
|
+
({
|
|
201
|
+
open,
|
|
202
|
+
defaultOpen,
|
|
203
|
+
onOpenChange,
|
|
204
|
+
commands,
|
|
205
|
+
placeholder = "Type a command\u2026",
|
|
206
|
+
emptyMessage = "No results",
|
|
207
|
+
className
|
|
208
|
+
}, ref) => {
|
|
209
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);
|
|
210
|
+
const isControlled = open !== void 0;
|
|
211
|
+
const isOpen = isControlled ? open : internalOpen;
|
|
212
|
+
useReturnFocusToOpener(isOpen);
|
|
213
|
+
const setOpen = useCallback(
|
|
214
|
+
(next) => {
|
|
215
|
+
if (!isControlled) setInternalOpen(next);
|
|
216
|
+
onOpenChange?.(next);
|
|
217
|
+
},
|
|
218
|
+
[isControlled, onOpenChange]
|
|
219
|
+
);
|
|
220
|
+
const onRun = useCallback(
|
|
221
|
+
(cmd) => {
|
|
222
|
+
cmd.onSelect();
|
|
223
|
+
setOpen(false);
|
|
224
|
+
},
|
|
225
|
+
[setOpen]
|
|
226
|
+
);
|
|
227
|
+
return (
|
|
228
|
+
// The overlay is as tall as the visible viewport React Aria measures (above an on-screen
|
|
229
|
+
// keyboard too) and keeps a margin on every side, so the palette never touches the edges.
|
|
230
|
+
/* @__PURE__ */ jsx(
|
|
231
|
+
ModalOverlay,
|
|
232
|
+
{
|
|
233
|
+
isOpen,
|
|
234
|
+
onOpenChange: setOpen,
|
|
235
|
+
isDismissable: true,
|
|
236
|
+
className: "fixed inset-x-0 top-0 z-40 flex h-[var(--visual-viewport-height,100dvh)] flex-col items-center bg-oprt-bg/80 p-2u",
|
|
237
|
+
children: /* @__PURE__ */ jsxs(Modal, { className: "contents", children: [
|
|
238
|
+
/* @__PURE__ */ jsx(
|
|
239
|
+
"div",
|
|
240
|
+
{
|
|
241
|
+
"aria-hidden": "true",
|
|
242
|
+
className: "pointer-events-none shrink-[999] basis-[calc(15dvh-var(--oprt-space-2))]"
|
|
243
|
+
}
|
|
244
|
+
),
|
|
245
|
+
/* @__PURE__ */ jsxs(
|
|
246
|
+
Dialog,
|
|
247
|
+
{
|
|
248
|
+
ref,
|
|
249
|
+
className: clsx(
|
|
250
|
+
"relative z-50 flex min-h-0 w-full max-w-lg flex-col bg-oprt-surface-1 border border-oprt-border font-sans focus:outline-hidden focus-visible:border-oprt-border-focus focus-visible:shadow-oprt-focus",
|
|
251
|
+
className
|
|
252
|
+
),
|
|
253
|
+
children: [
|
|
254
|
+
/* @__PURE__ */ jsx(Heading, { slot: "title", className: "sr-only", children: "Command Palette" }),
|
|
255
|
+
/* @__PURE__ */ jsx(
|
|
256
|
+
PaletteContent,
|
|
257
|
+
{
|
|
258
|
+
commands,
|
|
259
|
+
placeholder,
|
|
260
|
+
emptyMessage,
|
|
261
|
+
onRun
|
|
262
|
+
}
|
|
263
|
+
)
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
)
|
|
267
|
+
] })
|
|
268
|
+
}
|
|
269
|
+
)
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
CommandPalette.displayName = "CommandPalette";
|
|
274
|
+
export {
|
|
275
|
+
CommandPalette
|
|
276
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/CommandPalette/CommandPalette.manifest.ts
|
|
2
|
+
var CommandPaletteManifest = {
|
|
3
|
+
CommandPalette: {
|
|
4
|
+
description: "Modal command launcher: search field over a filtered, keyboard-navigable action list. Command and group labels display uppercase; search input, empty messages, and keyboard shortcuts retain their casing.",
|
|
5
|
+
props: {
|
|
6
|
+
commands: { type: "array", required: true },
|
|
7
|
+
placeholder: { type: "string", default: "Type a command\u2026" },
|
|
8
|
+
emptyMessage: { type: "string", default: "No results" },
|
|
9
|
+
open: { type: "boolean" },
|
|
10
|
+
defaultOpen: {
|
|
11
|
+
type: "boolean",
|
|
12
|
+
default: false,
|
|
13
|
+
description: "Initial open state when the palette manages its own state."
|
|
14
|
+
},
|
|
15
|
+
onOpenChange: { type: "function" }
|
|
16
|
+
},
|
|
17
|
+
composable_with: [],
|
|
18
|
+
never_inside: ["CommandPalette"],
|
|
19
|
+
example: '<CommandPalette\n open={open}\n onOpenChange={setOpen}\n commands={[\n { id: "new", label: "New File", shortcut: "\u2318N", onSelect: () => {} },\n { id: "open", label: "Open\u2026", shortcut: "\u2318O", onSelect: () => {} },\n ]}\n/>'
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
export {
|
|
23
|
+
CommandPaletteManifest
|
|
24
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/ConfirmDialog/ConfirmDialog.tsx
|
|
4
|
+
import { forwardRef, useEffect, useState } from "react";
|
|
5
|
+
import { Dialog } from "../Dialog/Dialog.js";
|
|
6
|
+
import { Button } from "../Button/Button.js";
|
|
7
|
+
import { Input } from "../Input/Input.js";
|
|
8
|
+
import { Field } from "../Field/Field.js";
|
|
9
|
+
import { ConfirmDialogManifest } from "./ConfirmDialog.manifest.js";
|
|
10
|
+
import { devValidate } from "../_internal/devValidate.js";
|
|
11
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
var ConfirmDialog = forwardRef((props, ref) => {
|
|
13
|
+
const {
|
|
14
|
+
trigger,
|
|
15
|
+
title,
|
|
16
|
+
description,
|
|
17
|
+
confirmLabel = "CONFIRM",
|
|
18
|
+
cancelLabel = "CANCEL",
|
|
19
|
+
confirmVariant = "primary",
|
|
20
|
+
confirmPhrase,
|
|
21
|
+
onConfirm,
|
|
22
|
+
open,
|
|
23
|
+
defaultOpen,
|
|
24
|
+
onOpenChange,
|
|
25
|
+
size,
|
|
26
|
+
className
|
|
27
|
+
} = props;
|
|
28
|
+
const isControlled = open !== void 0;
|
|
29
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);
|
|
30
|
+
const resolvedOpen = isControlled ? open : internalOpen;
|
|
31
|
+
const setOpen = (next) => {
|
|
32
|
+
if (!isControlled) setInternalOpen(next);
|
|
33
|
+
onOpenChange?.(next);
|
|
34
|
+
};
|
|
35
|
+
const [phrase, setPhrase] = useState("");
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!resolvedOpen) setPhrase("");
|
|
38
|
+
}, [resolvedOpen]);
|
|
39
|
+
const confirmDisabled = confirmPhrase !== void 0 && phrase.trim() !== confirmPhrase;
|
|
40
|
+
const optionalProps = {};
|
|
41
|
+
if (trigger !== void 0) optionalProps.trigger = trigger;
|
|
42
|
+
if (description !== void 0) optionalProps.description = description;
|
|
43
|
+
if (size !== void 0) optionalProps.size = size;
|
|
44
|
+
if (className !== void 0) optionalProps.className = className;
|
|
45
|
+
if (process.env.NODE_ENV !== "production") {
|
|
46
|
+
devValidate("ConfirmDialog", ConfirmDialogManifest.ConfirmDialog.props, {
|
|
47
|
+
...props,
|
|
48
|
+
confirmVariant
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return /* @__PURE__ */ jsx(
|
|
52
|
+
Dialog,
|
|
53
|
+
{
|
|
54
|
+
ref,
|
|
55
|
+
title,
|
|
56
|
+
open: resolvedOpen,
|
|
57
|
+
onOpenChange: setOpen,
|
|
58
|
+
...optionalProps,
|
|
59
|
+
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60
|
+
/* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: () => setOpen(false), children: cancelLabel }),
|
|
61
|
+
/* @__PURE__ */ jsx(
|
|
62
|
+
Button,
|
|
63
|
+
{
|
|
64
|
+
variant: confirmVariant,
|
|
65
|
+
disabled: confirmDisabled,
|
|
66
|
+
onClick: () => {
|
|
67
|
+
onConfirm();
|
|
68
|
+
setOpen(false);
|
|
69
|
+
},
|
|
70
|
+
children: confirmLabel
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
] }),
|
|
74
|
+
children: confirmPhrase !== void 0 ? (
|
|
75
|
+
// The Field's visible label names the input (WCAG 2.5.3).
|
|
76
|
+
/* @__PURE__ */ jsx(Field, { label: `Type "${confirmPhrase}" to confirm`, children: /* @__PURE__ */ jsx(Input, { value: phrase, onChange: (e) => setPhrase(e.target.value), autoComplete: "off" }) })
|
|
77
|
+
) : null
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
ConfirmDialog.displayName = "ConfirmDialog";
|
|
82
|
+
export {
|
|
83
|
+
ConfirmDialog
|
|
84
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/ConfirmDialog/ConfirmDialog.manifest.ts
|
|
2
|
+
var ConfirmDialogManifest = {
|
|
3
|
+
ConfirmDialog: {
|
|
4
|
+
description: "Confirmation modal for destructive or high-consequence actions. Composes Dialog + Button (+ Field + Input for typed confirmation). Flat API: pass title, optional description, onConfirm, and an optional confirmPhrase the user must type exactly to enable Confirm.",
|
|
5
|
+
props: {
|
|
6
|
+
title: { type: "string", required: true },
|
|
7
|
+
description: { type: "string" },
|
|
8
|
+
confirmLabel: { type: "string", default: "CONFIRM" },
|
|
9
|
+
cancelLabel: { type: "string", default: "CANCEL" },
|
|
10
|
+
confirmVariant: {
|
|
11
|
+
type: "enum",
|
|
12
|
+
values: ["primary", "danger"],
|
|
13
|
+
default: "primary"
|
|
14
|
+
},
|
|
15
|
+
confirmPhrase: { type: "string" },
|
|
16
|
+
onConfirm: { type: "function", required: true },
|
|
17
|
+
size: { type: "enum", values: ["sm", "md", "lg"], default: "md" },
|
|
18
|
+
open: { type: "boolean" },
|
|
19
|
+
defaultOpen: { type: "boolean" },
|
|
20
|
+
onOpenChange: { type: "function" }
|
|
21
|
+
},
|
|
22
|
+
slots: {
|
|
23
|
+
trigger: { type: "focusable element | text (text renders a Button)" }
|
|
24
|
+
},
|
|
25
|
+
composable_with: ["Card", "Toolbar"],
|
|
26
|
+
never_inside: ["Dialog", "ConfirmDialog"],
|
|
27
|
+
example: '<ConfirmDialog\n title="DELETE UNIT"\n description="This permanently removes UNIT-07 from the roster."\n confirmPhrase="UNIT-07"\n confirmVariant="danger"\n confirmLabel="DELETE"\n trigger={<Button variant="danger">DELETE</Button>}\n onConfirm={() => deleteUnit("UNIT-07")}\n/>'
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
export {
|
|
31
|
+
ConfirmDialogManifest
|
|
32
|
+
};
|