@torpor/ui 0.4.0 → 1.0.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/README.md +83 -7
- package/dist/Accordion/Accordion.torp +42 -165
- package/dist/Accordion/AccordionContent.torp +6 -6
- package/dist/Accordion/AccordionHeader.torp +7 -11
- package/dist/Accordion/AccordionItem.torp +24 -24
- package/dist/Accordion/AccordionTrigger.torp +17 -8
- package/dist/Accordion/AccordionTypes.ts +1 -1
- package/dist/Accordion/index.d.ts +23 -25
- package/dist/Accordion/index.js +6 -22
- package/dist/Breadcrumb/Breadcrumb.torp +12 -10
- package/dist/Breadcrumb/BreadcrumbItem.torp +6 -6
- package/dist/Breadcrumb/BreadcrumbList.torp +21 -0
- package/dist/Breadcrumb/BreadcrumbSeparator.torp +4 -4
- package/dist/Breadcrumb/index.d.ts +39 -15
- package/dist/Breadcrumb/index.js +5 -14
- package/dist/Calendar/Calendar.torp +163 -156
- package/dist/Calendar/CalendarDay.torp +91 -45
- package/dist/Calendar/CalendarDayHeader.torp +36 -0
- package/dist/Calendar/CalendarGrid.torp +104 -0
- package/dist/Calendar/CalendarGridHeader.torp +43 -0
- package/dist/Calendar/CalendarHeader.torp +48 -0
- package/dist/Calendar/CalendarHeaderTrigger.torp +54 -0
- package/dist/Calendar/CalendarTypes.ts +24 -3
- package/dist/Calendar/buildDays.ts +54 -0
- package/dist/Calendar/index.d.ts +191 -5
- package/dist/Calendar/index.js +8 -10
- package/dist/Carousel/Carousel.torp +180 -0
- package/dist/Carousel/CarouselIndicators.torp +65 -0
- package/dist/Carousel/CarouselNext.torp +43 -0
- package/dist/Carousel/CarouselPrevious.torp +44 -0
- package/dist/Carousel/CarouselSlide.torp +57 -0
- package/dist/Carousel/CarouselTypes.ts +28 -0
- package/dist/Carousel/index.d.ts +147 -0
- package/dist/Carousel/index.js +6 -0
- package/dist/Charts/BarChart.torp +55 -144
- package/dist/Charts/Chart.torp +120 -0
- package/dist/Charts/ChartBars.torp +78 -0
- package/dist/Charts/ChartColumns.torp +66 -0
- package/dist/Charts/ChartGrid.torp +54 -0
- package/dist/Charts/ChartLines.torp +60 -0
- package/dist/Charts/ChartTypes.ts +25 -0
- package/dist/Charts/ChartXAxis.torp +85 -0
- package/dist/Charts/ChartYAxis.torp +107 -0
- package/dist/Charts/ColumnChart.torp +70 -0
- package/dist/Charts/GridLines.torp +4 -4
- package/dist/Charts/LineChart.torp +55 -178
- package/dist/Charts/PieChart.torp +14 -20
- package/dist/Charts/ScatterChart.torp +45 -38
- package/dist/Charts/SparkLine.torp +16 -19
- package/dist/Charts/XAxis.torp +14 -11
- package/dist/Charts/YAxis.torp +10 -8
- package/dist/Charts/index.d.ts +328 -79
- package/dist/Charts/index.js +17 -34
- package/dist/ColorPicker/ColorPalette.torp +154 -0
- package/dist/ColorPicker/ColorPicker.torp +157 -0
- package/dist/ColorPicker/index.d.ts +85 -0
- package/dist/ColorPicker/index.js +3 -0
- package/dist/ComboBox/ComboBox.torp +205 -0
- package/dist/ComboBox/ComboBoxContent.torp +120 -0
- package/dist/ComboBox/ComboBoxInput.torp +161 -0
- package/dist/ComboBox/ComboBoxTrigger.torp +49 -0
- package/dist/ComboBox/ComboBoxTypes.ts +39 -0
- package/dist/ComboBox/index.d.ts +154 -0
- package/dist/ComboBox/index.js +6 -0
- package/dist/CommandPalette/CommandPalette.torp +223 -0
- package/dist/CommandPalette/CommandTypes.ts +14 -0
- package/dist/CommandPalette/index.d.ts +45 -0
- package/dist/CommandPalette/index.js +2 -0
- package/dist/Contextual/Contextual.torp +32 -50
- package/dist/Contextual/ContextualContent.torp +29 -73
- package/dist/Contextual/ContextualHover.torp +112 -0
- package/dist/Contextual/ContextualTrigger.torp +31 -42
- package/dist/Contextual/ContextualTypes.ts +4 -17
- package/dist/Contextual/index.d.ts +44 -27
- package/dist/Contextual/index.js +5 -14
- package/dist/DataGrid/DataGrid.torp +496 -0
- package/dist/DataGrid/DataGridTypes.ts +25 -0
- package/dist/DataGrid/index.d.ts +90 -0
- package/dist/DataGrid/index.js +3 -0
- package/dist/DataGrid/sortRows.ts +32 -0
- package/dist/DatePicker/DatePicker.torp +222 -0
- package/dist/DatePicker/DatePickerHeader.torp +20 -0
- package/dist/DatePicker/index.d.ts +55 -0
- package/dist/DatePicker/index.js +2 -0
- package/dist/DateRangePicker/DateRangePicker.torp +315 -0
- package/dist/DateRangePicker/DateRangePickerDay.torp +137 -0
- package/dist/DateRangePicker/DateRangePickerTypes.ts +21 -0
- package/dist/DateRangePicker/index.d.ts +76 -0
- package/dist/DateRangePicker/index.js +3 -0
- package/dist/Dialog/Dialog.torp +24 -17
- package/dist/Dialog/DialogBody.torp +5 -9
- package/dist/Dialog/DialogButton.torp +12 -12
- package/dist/Dialog/DialogFooter.torp +3 -7
- package/dist/Dialog/DialogHeader.torp +6 -10
- package/dist/Dialog/index.d.ts +27 -27
- package/dist/Dialog/index.js +6 -22
- package/dist/Disclosure/Disclosure.torp +18 -18
- package/dist/Disclosure/DisclosureContent.torp +5 -6
- package/dist/Disclosure/DisclosureHeader.torp +7 -11
- package/dist/Disclosure/DisclosureTrigger.torp +4 -5
- package/dist/Disclosure/index.d.ts +14 -16
- package/dist/Disclosure/index.js +5 -18
- package/dist/Drawer/Drawer.torp +19 -49
- package/dist/Drawer/DrawerContent.torp +70 -13
- package/dist/Drawer/DrawerOverlay.torp +4 -4
- package/dist/Drawer/DrawerTrigger.torp +16 -4
- package/dist/Drawer/DrawerTypes.ts +3 -0
- package/dist/Drawer/index.d.ts +16 -20
- package/dist/Drawer/index.js +5 -18
- package/dist/FocusGroup/FocusGroup.torp +70 -0
- package/dist/FocusGroup/FocusGroupButton.torp +59 -0
- package/dist/FocusGroup/FocusGroupTypes.ts +14 -0
- package/dist/FocusGroup/index.d.ts +44 -0
- package/dist/FocusGroup/index.js +3 -0
- package/dist/Form/CheckBox.torp +27 -18
- package/dist/Form/Field.torp +17 -17
- package/dist/Form/File.torp +31 -22
- package/dist/Form/FileDrop.torp +187 -0
- package/dist/Form/Form.torp +24 -22
- package/dist/Form/FormTypes.ts +2 -0
- package/dist/Form/FormValues.torp +37 -0
- package/dist/Form/Hidden.torp +4 -12
- package/dist/Form/Input.torp +38 -32
- package/dist/Form/Label.torp +3 -3
- package/dist/Form/Message.torp +13 -5
- package/dist/Form/MessageSummary.torp +5 -4
- package/dist/Form/Option.torp +11 -7
- package/dist/Form/Select.torp +32 -28
- package/dist/Form/TextArea.torp +35 -31
- package/dist/Form/Toggle.torp +109 -0
- package/dist/Form/index.d.ts +178 -79
- package/dist/Form/index.js +16 -50
- package/dist/ListBox/ListBox.torp +253 -150
- package/dist/ListBox/ListBoxItem.torp +54 -30
- package/dist/ListBox/ListBoxSeparator.torp +5 -2
- package/dist/ListBox/ListBoxTypes.ts +23 -5
- package/dist/ListBox/LoadedListBoxItems.torp +41 -0
- package/dist/ListBox/index.d.ts +56 -17
- package/dist/ListBox/index.js +4 -14
- package/dist/MaskedInput/MaskedInput.torp +143 -0
- package/dist/MaskedInput/index.d.ts +52 -0
- package/dist/MaskedInput/index.js +2 -0
- package/dist/Menu/Menu.torp +86 -71
- package/dist/Menu/MenuButton.torp +17 -10
- package/dist/Menu/MenuCheck.torp +22 -18
- package/dist/Menu/MenuGroup.torp +4 -4
- package/dist/Menu/MenuIndicator.torp +5 -13
- package/dist/Menu/MenuPopout.torp +20 -50
- package/dist/Menu/MenuPopoutContent.torp +15 -66
- package/dist/Menu/MenuPopoutTrigger.torp +19 -100
- package/dist/Menu/MenuRadio.torp +25 -26
- package/dist/Menu/MenuRadioGroup.torp +25 -68
- package/dist/Menu/MenuSeparator.torp +3 -2
- package/dist/Menu/MenuTypes.ts +9 -19
- package/dist/Menu/index.d.ts +45 -51
- package/dist/Menu/index.js +12 -46
- package/dist/MenuBar/MenuBar.torp +31 -83
- package/dist/MenuBar/MenuBarItem.torp +33 -51
- package/dist/MenuBar/MenuBarItemContent.torp +15 -101
- package/dist/MenuBar/MenuBarItemTrigger.torp +20 -55
- package/dist/MenuBar/MenuBarTypes.ts +11 -10
- package/dist/MenuBar/index.d.ts +17 -19
- package/dist/MenuBar/index.js +5 -18
- package/dist/Modal/Modal.torp +22 -47
- package/dist/Modal/ModalContent.torp +21 -44
- package/dist/Modal/ModalOverlay.torp +4 -4
- package/dist/Modal/ModalTrigger.torp +16 -32
- package/dist/Modal/ModalTypes.ts +5 -16
- package/dist/Modal/index.d.ts +17 -29
- package/dist/Modal/index.js +29 -28
- package/dist/Modal/index.js.map +1 -1
- package/dist/Modal/showPrompt.ts +2 -2
- package/dist/NavMenu/NavMenu.torp +22 -111
- package/dist/NavMenu/NavMenuButton.torp +7 -8
- package/dist/NavMenu/NavMenuElement.torp +6 -7
- package/dist/NavMenu/NavMenuGroup.torp +5 -5
- package/dist/NavMenu/NavMenuGroupHeader.torp +5 -8
- package/dist/NavMenu/NavMenuLink.torp +7 -10
- package/dist/NavMenu/NavMenuPopout.torp +15 -60
- package/dist/NavMenu/NavMenuPopoutContent.torp +14 -73
- package/dist/NavMenu/NavMenuPopoutTrigger.torp +17 -99
- package/dist/NavMenu/NavMenuSeparator.torp +3 -7
- package/dist/NavMenu/NavMenuTypes.ts +4 -14
- package/dist/NavMenu/index.d.ts +40 -44
- package/dist/NavMenu/index.js +11 -42
- package/dist/Notification/Notification.torp +12 -24
- package/dist/Notification/NotificationClose.torp +6 -3
- package/dist/Notification/NotificationContainer.torp +3 -7
- package/dist/Notification/NotificationContent.torp +3 -7
- package/dist/Notification/NotificationTypes.ts +1 -1
- package/dist/Notification/index.d.ts +15 -12
- package/dist/Notification/index.js +22 -32
- package/dist/Notification/index.js.map +1 -1
- package/dist/NumberInput/NumberInput.torp +255 -0
- package/dist/NumberInput/index.d.ts +44 -0
- package/dist/NumberInput/index.js +2 -0
- package/dist/Pagination/Pagination.torp +41 -34
- package/dist/Pagination/PaginationItem.torp +50 -13
- package/dist/Pagination/PaginationTypes.ts +3 -6
- package/dist/Pagination/buildPages.ts +9 -5
- package/dist/Pagination/index.d.ts +19 -9
- package/dist/Pagination/index.js +3 -40
- package/dist/Popover/Popover.torp +32 -50
- package/dist/Popover/PopoverContent.torp +27 -76
- package/dist/Popover/PopoverHover.torp +90 -0
- package/dist/Popover/PopoverTrigger.torp +17 -40
- package/dist/Popover/PopoverTypes.ts +4 -17
- package/dist/Popover/index.d.ts +40 -25
- package/dist/Popover/index.js +18 -18
- package/dist/Popover/index.js.map +1 -1
- package/dist/Progress/Progress.torp +88 -0
- package/dist/Progress/index.d.ts +37 -0
- package/dist/Progress/index.js +2 -0
- package/dist/Rating/Rating.torp +225 -0
- package/dist/Rating/index.d.ts +44 -0
- package/dist/Rating/index.js +2 -0
- package/dist/SegmentedControl/SegmentedControl.torp +233 -0
- package/dist/SegmentedControl/SegmentedControlTypes.ts +26 -0
- package/dist/SegmentedControl/SegmentedItem.torp +78 -0
- package/dist/SegmentedControl/index.d.ts +74 -0
- package/dist/SegmentedControl/index.js +3 -0
- package/dist/SelectBox/SelectBox.torp +107 -53
- package/dist/SelectBox/SelectBoxContent.torp +49 -19
- package/dist/SelectBox/SelectBoxTrigger.torp +108 -8
- package/dist/SelectBox/SelectBoxTypes.ts +26 -11
- package/dist/SelectBox/index.d.ts +39 -54
- package/dist/SelectBox/index.js +5 -18
- package/dist/Slider/Slider.torp +245 -0
- package/dist/Slider/index.d.ts +45 -0
- package/dist/Slider/index.js +2 -0
- package/dist/Splitter/Splitter.torp +156 -0
- package/dist/Splitter/SplitterHandle.torp +161 -0
- package/dist/Splitter/SplitterPane.torp +63 -0
- package/dist/Splitter/SplitterTypes.ts +31 -0
- package/dist/Splitter/index.d.ts +103 -0
- package/dist/Splitter/index.js +4 -0
- package/dist/Stepper/Stepper.torp +146 -0
- package/dist/Stepper/StepperMarker.torp +24 -0
- package/dist/Stepper/StepperNext.torp +44 -0
- package/dist/Stepper/StepperPrevious.torp +44 -0
- package/dist/Stepper/StepperStep.torp +90 -0
- package/dist/Stepper/StepperTypes.ts +30 -0
- package/dist/Stepper/index.d.ts +149 -0
- package/dist/Stepper/index.js +6 -0
- package/dist/TabGroup/TabContent.torp +6 -6
- package/dist/TabGroup/TabGroup.torp +97 -113
- package/dist/TabGroup/TabGroupTypes.ts +1 -1
- package/dist/TabGroup/TabHeader.torp +23 -19
- package/dist/TabGroup/TabList.torp +8 -21
- package/dist/TabGroup/TabTrigger.torp +6 -7
- package/dist/TabGroup/index.d.ts +26 -26
- package/dist/TabGroup/index.js +6 -22
- package/dist/TagInput/TagInput.torp +378 -0
- package/dist/TagInput/index.d.ts +48 -0
- package/dist/TagInput/index.js +2 -0
- package/dist/TimePicker/TimePicker.torp +462 -0
- package/dist/TimePicker/index.d.ts +53 -0
- package/dist/TimePicker/index.js +2 -0
- package/dist/ToolBar/ToolBar.torp +29 -110
- package/dist/ToolBar/ToolBarButton.torp +7 -8
- package/dist/ToolBar/ToolBarElement.torp +7 -11
- package/dist/ToolBar/ToolBarPopout.torp +16 -68
- package/dist/ToolBar/ToolBarPopoutContent.torp +15 -73
- package/dist/ToolBar/ToolBarPopoutTrigger.torp +19 -110
- package/dist/ToolBar/ToolBarSeparator.torp +3 -7
- package/dist/ToolBar/ToolBarTypes.ts +7 -14
- package/dist/ToolBar/index.d.ts +30 -32
- package/dist/ToolBar/index.js +8 -30
- package/dist/Tree/Tree.torp +395 -0
- package/dist/Tree/TreeItem.torp +176 -0
- package/dist/Tree/TreeItemChildren.torp +60 -0
- package/dist/Tree/TreeItemContent.torp +46 -0
- package/dist/Tree/TreeItemLabel.torp +94 -0
- package/dist/Tree/TreeItemTrigger.torp +78 -0
- package/dist/Tree/TreeLoadedChildren.torp +122 -0
- package/dist/Tree/TreeTypes.ts +63 -0
- package/dist/Tree/index.d.ts +262 -0
- package/dist/Tree/index.js +7 -0
- package/dist/icons/ChevronDoubleLeft.torp +19 -0
- package/dist/icons/ChevronDoubleRight.torp +19 -0
- package/dist/icons/index.d.ts +61 -1
- package/dist/icons/index.js +17 -58
- package/dist/loader-DEeG5vFY.js +44 -0
- package/dist/loader-DEeG5vFY.js.map +1 -0
- package/dist/motion/index.d.ts +12 -8
- package/dist/motion/index.d.ts.map +1 -0
- package/dist/motion/index.js +70 -88
- package/dist/motion/index.js.map +1 -1
- package/dist/mount/draggable.ts +63 -0
- package/dist/mount/dropTarget.ts +108 -0
- package/dist/mount/hoverTrigger.ts +1 -1
- package/dist/utils/$handle.ts +23 -0
- package/dist/utils/LoadedItems.torp +47 -0
- package/dist/utils/PopoutClickTrigger.torp +99 -0
- package/dist/utils/PopoutTypes.ts +59 -12
- package/dist/utils/SubmenuPopout.torp +89 -0
- package/dist/utils/SubmenuPopoutContent.torp +87 -0
- package/dist/utils/SubmenuPopoutTrigger.torp +160 -0
- package/dist/utils/chartColors.ts +13 -13
- package/dist/utils/chartLayout.ts +143 -0
- package/dist/utils/colorUtils.ts +103 -0
- package/dist/utils/createItemGroup.ts +152 -0
- package/dist/utils/dateUtils.ts +8 -7
- package/dist/utils/documentEvents.ts +4 -4
- package/dist/utils/dragState.ts +86 -0
- package/dist/utils/focusGroup.ts +204 -0
- package/dist/utils/focusItem.ts +44 -0
- package/dist/utils/formField.ts +70 -0
- package/dist/utils/hoverOpen.ts +50 -0
- package/dist/utils/hoverReveal.ts +194 -0
- package/dist/utils/loader.ts +113 -0
- package/dist/utils/maskUtils.ts +98 -0
- package/dist/utils/popoutContent.ts +137 -0
- package/dist/utils/reorderList.ts +52 -0
- package/package.json +97 -22
- package/dist/Accordion/index.js.map +0 -1
- package/dist/Breadcrumb/index.js.map +0 -1
- package/dist/Calendar/index.js.map +0 -1
- package/dist/Charts/index.js.map +0 -1
- package/dist/Contextual/index.js.map +0 -1
- package/dist/Dialog/index.js.map +0 -1
- package/dist/Disclosure/index.js.map +0 -1
- package/dist/Drawer/index.js.map +0 -1
- package/dist/Form/index.js.map +0 -1
- package/dist/ListBox/index.js.map +0 -1
- package/dist/Menu/index.js.map +0 -1
- package/dist/MenuBar/index.js.map +0 -1
- package/dist/NavMenu/index.js.map +0 -1
- package/dist/Pagination/index.js.map +0 -1
- package/dist/SelectBox/SelectBoxInput.torp +0 -95
- package/dist/SelectBox/index.js.map +0 -1
- package/dist/TabGroup/index.js.map +0 -1
- package/dist/ToolBar/index.js.map +0 -1
- package/dist/icons/index.js.map +0 -1
- package/dist/index.d.ts +0 -19
- package/dist/metafile-esm.json +0 -1
- package/dist/motion/fade.ts +0 -16
- package/dist/motion/grow.ts +0 -23
- package/dist/motion/measure.ts +0 -28
- package/dist/motion/slide.ts +0 -45
- package/dist/mount/index.d.ts +0 -2
- package/dist/utils/index.d.ts +0 -2
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import getId from "../utils/getId";
|
|
3
|
+
import { type ComboBoxContext, ComboBoxContextName } from "./ComboBoxTypes";
|
|
4
|
+
|
|
5
|
+
interface ComboBoxInputProps {
|
|
6
|
+
/** An ID for the root element */
|
|
7
|
+
id?: string;
|
|
8
|
+
/** Classes for the root element */
|
|
9
|
+
class?: ClassValue;
|
|
10
|
+
/** Styles for the root element */
|
|
11
|
+
style?: StyleValue;
|
|
12
|
+
|
|
13
|
+
// TODO: Get these from the parent
|
|
14
|
+
/** The name of the input's form data field */
|
|
15
|
+
name?: string;
|
|
16
|
+
/** The value displayed in the input */
|
|
17
|
+
value?: any;
|
|
18
|
+
/** The text to display in the input (e.g. the selected item's label), instead of the raw value */
|
|
19
|
+
text?: string;
|
|
20
|
+
/** Text to display when the input value is empty */
|
|
21
|
+
placeholder?: string;
|
|
22
|
+
/** Whether the input's value is required */
|
|
23
|
+
required?: boolean;
|
|
24
|
+
|
|
25
|
+
/** An ARIA label for describing the input to screen readers when there is no associated label element */
|
|
26
|
+
ariaLabel?: string;
|
|
27
|
+
/** Raised on change */
|
|
28
|
+
onchange?: (e: Event) => void;
|
|
29
|
+
/** Raised on input */
|
|
30
|
+
oninput?: (e: InputEvent) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The input that causes a ComboBox to appear or disappear.
|
|
35
|
+
*/
|
|
36
|
+
export default function ComboBoxInput($props: ComboBoxInputProps | undefined) {
|
|
37
|
+
let defaultId = getId();
|
|
38
|
+
let input: HTMLInputElement;
|
|
39
|
+
|
|
40
|
+
// Get stuff out of the ComboBoxContext
|
|
41
|
+
const context = $context[ComboBoxContextName] as ComboBoxContext;
|
|
42
|
+
if (!context) {
|
|
43
|
+
throw new Error("ComboBoxInput must be contained within a ComboBox");
|
|
44
|
+
}
|
|
45
|
+
let $state = context.state;
|
|
46
|
+
context.focusInput = focusInput;
|
|
47
|
+
|
|
48
|
+
// Set the trigger ID so that it can be used with aria-labelledby
|
|
49
|
+
$state.triggerId = $props?.id ?? defaultId;
|
|
50
|
+
|
|
51
|
+
function focusInput() {
|
|
52
|
+
input.focus();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// The text shown in the input: the consumer-supplied label for the
|
|
56
|
+
// selected value if given, otherwise the raw value itself. One-way: what
|
|
57
|
+
// the user types is search text, not the value (that's updated on select)
|
|
58
|
+
function displayValue(): string {
|
|
59
|
+
if ($props?.text !== undefined) {
|
|
60
|
+
return $props.text;
|
|
61
|
+
}
|
|
62
|
+
const value = $state.value;
|
|
63
|
+
if (value === undefined || value === null || value === "") {
|
|
64
|
+
return "";
|
|
65
|
+
}
|
|
66
|
+
return Array.isArray(value) ? value.join(", ") : String(value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function handleClick() {
|
|
70
|
+
$state.visible = true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function handleInput(e: InputEvent) {
|
|
74
|
+
if (context.getLoadedItems) {
|
|
75
|
+
// Loaded from a network loader: refetch with the current text
|
|
76
|
+
$state.searchText = input.value;
|
|
77
|
+
$state.visible = true;
|
|
78
|
+
} else if (context.searchItems) {
|
|
79
|
+
context.searchText = input.value;
|
|
80
|
+
const found = context.searchItems(context.searchText);
|
|
81
|
+
if (found !== undefined) {
|
|
82
|
+
$state.visible = true;
|
|
83
|
+
$state.activeDescendant = found.id;
|
|
84
|
+
if (context.markElement) {
|
|
85
|
+
context.markElement(found.id);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if ($props?.oninput) {
|
|
91
|
+
$props.oninput(e);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function handleKey(e: KeyboardEvent) {
|
|
96
|
+
switch (e.key) {
|
|
97
|
+
case "ArrowDown": {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
$state.visible = true;
|
|
100
|
+
if (context.focusFirstElement) {
|
|
101
|
+
context.focusFirstElement();
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case "ArrowUp": {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
$state.visible = true;
|
|
108
|
+
if (context.focusLastElement) {
|
|
109
|
+
context.focusLastElement();
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case "Enter": {
|
|
114
|
+
if ($state.visible) {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
if (context.selectMarkedElement) {
|
|
117
|
+
context.selectMarkedElement();
|
|
118
|
+
}
|
|
119
|
+
$state.visible = false;
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case "Escape": {
|
|
124
|
+
if ($state.visible) {
|
|
125
|
+
e.preventDefault();
|
|
126
|
+
$state.visible = false;
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@render {
|
|
134
|
+
<input
|
|
135
|
+
role="combobox"
|
|
136
|
+
aria-autocomplete="list"
|
|
137
|
+
id={$props?.id ?? defaultId}
|
|
138
|
+
class={["torp-combo-box-input", $props?.class]}
|
|
139
|
+
{$props?.style}
|
|
140
|
+
name={$props?.name !== undefined ? $props.name : context.name}
|
|
141
|
+
value={displayValue()}
|
|
142
|
+
placeholder={$props?.placeholder !== undefined ? $props.placeholder : context.placeholder}
|
|
143
|
+
required={$props?.required !== undefined ? $props.required : context.required}
|
|
144
|
+
aria-readonly
|
|
145
|
+
aria-controls={$state.contentId}
|
|
146
|
+
aria-haspopup={$state.contentRole}
|
|
147
|
+
aria-activedescendant={$state.activeDescendant}
|
|
148
|
+
aria-expanded={$state.visible ? "true" : "false"}
|
|
149
|
+
aria-label={$props?.ariaLabel !== undefined ? $props.ariaLabel : context.ariaLabel}
|
|
150
|
+
data-valid={context.valid?.() ? "valid" : "invalid"}
|
|
151
|
+
aria-describedby={context.messageId?.()}
|
|
152
|
+
aria-invalid={context.inField?.() && !context.valid?.() ? "true" : undefined}
|
|
153
|
+
onblur={() => context.handleBlur?.()}
|
|
154
|
+
{$props?.onchange}
|
|
155
|
+
onclick={handleClick}
|
|
156
|
+
oninput={handleInput}
|
|
157
|
+
onkeydown={handleKey}
|
|
158
|
+
&ref={input}
|
|
159
|
+
/>
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import getId from "../utils/getId";
|
|
3
|
+
import { type ComboBoxContext, ComboBoxContextName } from "./ComboBoxTypes";
|
|
4
|
+
|
|
5
|
+
interface ComboBoxTriggerProps {
|
|
6
|
+
/** An ID for the root element */
|
|
7
|
+
id?: string;
|
|
8
|
+
/** Classes for the root element */
|
|
9
|
+
class?: ClassValue;
|
|
10
|
+
/** Styles for the root element */
|
|
11
|
+
style?: StyleValue;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The button that causes a ComboBox to appear or disappear.
|
|
16
|
+
*/
|
|
17
|
+
export default function ComboBoxTrigger($props: ComboBoxTriggerProps | undefined) {
|
|
18
|
+
let defaultId = getId();
|
|
19
|
+
|
|
20
|
+
// Get stuff out of the ComboBoxContext
|
|
21
|
+
const context = $context[ComboBoxContextName] as ComboBoxContext;
|
|
22
|
+
if (!context) {
|
|
23
|
+
throw new Error("ComboBoxTrigger must be contained within a ComboBox");
|
|
24
|
+
}
|
|
25
|
+
let $state = context.state;
|
|
26
|
+
|
|
27
|
+
// Set the trigger ID so that it can be used with aria-labelledby
|
|
28
|
+
$state.triggerId = $props?.id ?? defaultId;
|
|
29
|
+
|
|
30
|
+
function handleClick() {
|
|
31
|
+
$state.visible = !$state.visible;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@render {
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
id={$props?.id ?? defaultId}
|
|
38
|
+
class={["torp-combo-box-trigger", $props?.class]}
|
|
39
|
+
{$props?.style}
|
|
40
|
+
aria-haspopup={$state.contentRole}
|
|
41
|
+
aria-expanded={$state.visible ? "true" : "false"}
|
|
42
|
+
aria-controls={$state.contentId}
|
|
43
|
+
onclick={handleClick}
|
|
44
|
+
&ref={context.anchorElement}
|
|
45
|
+
>
|
|
46
|
+
<slot />
|
|
47
|
+
</button>
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type LoadResult, type Loader } from "../utils/loader";
|
|
2
|
+
import { type PopoutContext, type PopoutState } from "../utils/PopoutTypes";
|
|
3
|
+
|
|
4
|
+
export const ComboBoxContextName: unique symbol = Symbol.for("torp.ComboBox");
|
|
5
|
+
|
|
6
|
+
/** The shared context for ComboBox components */
|
|
7
|
+
export interface ComboBoxContext extends PopoutContext {
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
ariaLabel?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
multiple?: boolean;
|
|
13
|
+
state: ComboBoxState;
|
|
14
|
+
focusInput?: () => void;
|
|
15
|
+
/** Whether the component is contained within a Form Field */
|
|
16
|
+
inField?: () => boolean;
|
|
17
|
+
/** Whether the Field's value is valid, for data-valid / aria-invalid */
|
|
18
|
+
valid?: () => boolean;
|
|
19
|
+
/** The ID of the Field's Message component, for aria-describedby */
|
|
20
|
+
messageId?: () => string | undefined;
|
|
21
|
+
/** Called by the input when it loses focus, to run Field validation */
|
|
22
|
+
handleBlur?: () => void;
|
|
23
|
+
/** Returns the items loaded from the network loader, when one is set */
|
|
24
|
+
getLoadedItems?: () => any[];
|
|
25
|
+
/** Extracts the display text for a loaded item */
|
|
26
|
+
getItemLabel?: (item: any) => any;
|
|
27
|
+
/** Extracts the value for a loaded item */
|
|
28
|
+
getItemValue?: (item: any) => any;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ComboBoxState extends PopoutState {
|
|
32
|
+
value: any;
|
|
33
|
+
name?: string;
|
|
34
|
+
activeDescendant?: string;
|
|
35
|
+
/** The current search text, kept up to date by the input when loading */
|
|
36
|
+
searchText?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type { LoadResult, Loader };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { type SlotRender } from "@torpor/view";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
interface ComboBoxProps {
|
|
5
|
+
/** An ID for the root element */
|
|
6
|
+
id?: string;
|
|
7
|
+
/** Classes for the root element */
|
|
8
|
+
class?: ClassValue;
|
|
9
|
+
/** Styles for the root element */
|
|
10
|
+
style?: StyleValue;
|
|
11
|
+
/** The name of the input's form data field */
|
|
12
|
+
name?: string;
|
|
13
|
+
/** The data value selected by the user, or an array of values if multiple is set */
|
|
14
|
+
value: Bindable<any>;
|
|
15
|
+
/** Whether multiple values can be selected at the same time */
|
|
16
|
+
multiple?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Reads options from the network (or anywhere async) as the user types,
|
|
19
|
+
* receiving `{ searchText }`. When set and no content is slotted in, the
|
|
20
|
+
* options are rendered automatically.
|
|
21
|
+
*/
|
|
22
|
+
load?: Loader;
|
|
23
|
+
/** Called after each successful load with the normalized result */
|
|
24
|
+
onload?: (result: LoadResult) => void;
|
|
25
|
+
/** Extracts the display text for a loaded item (defaults to `label ?? text ?? item`) */
|
|
26
|
+
getItemLabel?: (item: any) => any;
|
|
27
|
+
/** Extracts the value for a loaded item (defaults to `value ?? id`) */
|
|
28
|
+
getItemValue?: (item: any) => any;
|
|
29
|
+
/** Text to display when the input value is empty */
|
|
30
|
+
placeholder?: string;
|
|
31
|
+
/** Whether the input's value is required */
|
|
32
|
+
required?: boolean;
|
|
33
|
+
/** An ARIA label for describing the input to screen readers when there is no associated label element */
|
|
34
|
+
ariaLabel?: string;
|
|
35
|
+
/** Whether the popout is visible; only bind this to control it from outside */
|
|
36
|
+
visible?: Bindable<boolean | undefined>;
|
|
37
|
+
/** Raised when the value has changed */
|
|
38
|
+
onchange?: (value: any) => void;
|
|
39
|
+
/** Raised when the popout has opened */
|
|
40
|
+
onopen?: () => void;
|
|
41
|
+
/** Raised when the popout has closed */
|
|
42
|
+
onclose?: (result: any) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* An input box for selecting a value from a popout ListBox or Dialog.
|
|
47
|
+
*
|
|
48
|
+
* See [the WAI ARIA guidelines for ComboBoxes](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/).
|
|
49
|
+
*
|
|
50
|
+
* The structure of a ComboBox is:
|
|
51
|
+
*
|
|
52
|
+
* ```
|
|
53
|
+
* <ComboBox>
|
|
54
|
+
* <ComboBoxInput />
|
|
55
|
+
* <ComboBoxTrigger /> (optional)
|
|
56
|
+
* <ComboBoxContent />
|
|
57
|
+
* </ComboBox>
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
declare function ComboBox(
|
|
61
|
+
$parent: ParentNode,
|
|
62
|
+
$anchor: Node | null,
|
|
63
|
+
$props: ComboBoxProps | undefined,
|
|
64
|
+
$context?: Record<PropertyKey, any>,
|
|
65
|
+
$slots?: Record<string, SlotRender>,
|
|
66
|
+
): void;
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
interface ComboBoxContentProps {
|
|
71
|
+
/** An ID for the root element */
|
|
72
|
+
id?: string;
|
|
73
|
+
/** Classes for the root element */
|
|
74
|
+
class?: ClassValue;
|
|
75
|
+
/** Styles for the root element */
|
|
76
|
+
style?: StyleValue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The content that is displayed for a ComboBox when it is open, displayed
|
|
81
|
+
* alongside an element.
|
|
82
|
+
*/
|
|
83
|
+
declare function ComboBoxContent(
|
|
84
|
+
$parent: ParentNode,
|
|
85
|
+
$anchor: Node | null,
|
|
86
|
+
$props: ComboBoxContentProps | undefined,
|
|
87
|
+
$context?: Record<PropertyKey, any>,
|
|
88
|
+
$slots?: Record<string, SlotRender>,
|
|
89
|
+
): void;
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
interface ComboBoxInputProps {
|
|
94
|
+
/** An ID for the root element */
|
|
95
|
+
id?: string;
|
|
96
|
+
/** Classes for the root element */
|
|
97
|
+
class?: ClassValue;
|
|
98
|
+
/** Styles for the root element */
|
|
99
|
+
style?: StyleValue;
|
|
100
|
+
|
|
101
|
+
// TODO: Get these from the parent
|
|
102
|
+
/** The name of the input's form data field */
|
|
103
|
+
name?: string;
|
|
104
|
+
/** The value displayed in the input */
|
|
105
|
+
value?: any;
|
|
106
|
+
/** The text to display in the input (e.g. the selected item's label), instead of the raw value */
|
|
107
|
+
text?: string;
|
|
108
|
+
/** Text to display when the input value is empty */
|
|
109
|
+
placeholder?: string;
|
|
110
|
+
/** Whether the input's value is required */
|
|
111
|
+
required?: boolean;
|
|
112
|
+
|
|
113
|
+
/** An ARIA label for describing the input to screen readers when there is no associated label element */
|
|
114
|
+
ariaLabel?: string;
|
|
115
|
+
/** Raised on change */
|
|
116
|
+
onchange?: (e: Event) => void;
|
|
117
|
+
/** Raised on input */
|
|
118
|
+
oninput?: (e: InputEvent) => void;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The input that causes a ComboBox to appear or disappear.
|
|
123
|
+
*/
|
|
124
|
+
declare function ComboBoxInput(
|
|
125
|
+
$parent: ParentNode,
|
|
126
|
+
$anchor: Node | null,
|
|
127
|
+
$props: ComboBoxInputProps | undefined,
|
|
128
|
+
$context?: Record<PropertyKey, any>,
|
|
129
|
+
$slots?: Record<string, SlotRender>,
|
|
130
|
+
): void;
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
interface ComboBoxTriggerProps {
|
|
135
|
+
/** An ID for the root element */
|
|
136
|
+
id?: string;
|
|
137
|
+
/** Classes for the root element */
|
|
138
|
+
class?: ClassValue;
|
|
139
|
+
/** Styles for the root element */
|
|
140
|
+
style?: StyleValue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The button that causes a ComboBox to appear or disappear.
|
|
145
|
+
*/
|
|
146
|
+
declare function ComboBoxTrigger(
|
|
147
|
+
$parent: ParentNode,
|
|
148
|
+
$anchor: Node | null,
|
|
149
|
+
$props: ComboBoxTriggerProps | undefined,
|
|
150
|
+
$context?: Record<PropertyKey, any>,
|
|
151
|
+
$slots?: Record<string, SlotRender>,
|
|
152
|
+
): void;
|
|
153
|
+
|
|
154
|
+
export { ComboBox, ComboBoxContent, ComboBoxInput, ComboBoxTrigger };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { n as normalizeLoadResult, t as createItemLoader } from "../loader-DEeG5vFY.js";
|
|
2
|
+
import ComboBox from "./ComboBox.torp";
|
|
3
|
+
import ComboBoxContent from "./ComboBoxContent.torp";
|
|
4
|
+
import ComboBoxInput from "./ComboBoxInput.torp";
|
|
5
|
+
import ComboBoxTrigger from "./ComboBoxTrigger.torp";
|
|
6
|
+
export { ComboBox, ComboBoxContent, ComboBoxInput, ComboBoxTrigger, createItemLoader, normalizeLoadResult };
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { Bindable, ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import Modal from "../Modal/Modal.torp";
|
|
3
|
+
import ModalContent from "../Modal/ModalContent.torp";
|
|
4
|
+
import ModalOverlay from "../Modal/ModalOverlay.torp";
|
|
5
|
+
import getId from "../utils/getId";
|
|
6
|
+
import type { Command } from "./CommandTypes";
|
|
7
|
+
|
|
8
|
+
interface CommandPaletteProps {
|
|
9
|
+
/** An ID for the root element */
|
|
10
|
+
id?: string;
|
|
11
|
+
/** Classes for the root element */
|
|
12
|
+
class?: ClassValue;
|
|
13
|
+
/** Styles for the root element */
|
|
14
|
+
style?: StyleValue;
|
|
15
|
+
/** Whether the palette is visible */
|
|
16
|
+
visible: Bindable<boolean | undefined>;
|
|
17
|
+
/** The commands that the user can choose from */
|
|
18
|
+
commands: Command[];
|
|
19
|
+
/** Text to display in the input when it is empty */
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
/**
|
|
22
|
+
* A keyboard shortcut that toggles the palette, e.g. "mod+k", where "mod"
|
|
23
|
+
* means the Cmd key on Mac and the Ctrl key elsewhere
|
|
24
|
+
*/
|
|
25
|
+
hotkey?: string;
|
|
26
|
+
/** An accessible name for the input */
|
|
27
|
+
ariaLabel?: string;
|
|
28
|
+
/** Text to display when no commands match the search */
|
|
29
|
+
emptyText?: string;
|
|
30
|
+
/** Raised when a command has been run */
|
|
31
|
+
onrun?: (command: Command) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A modal input for searching and running commands, opened with a keyboard
|
|
36
|
+
* shortcut or programmatically.
|
|
37
|
+
*
|
|
38
|
+
* The structure of a CommandPalette is a Modal containing a search input and
|
|
39
|
+
* a filtered list of commands.
|
|
40
|
+
*/
|
|
41
|
+
export default function CommandPalette($props: CommandPaletteProps | undefined) {
|
|
42
|
+
let defaultId = getId();
|
|
43
|
+
let input: HTMLInputElement;
|
|
44
|
+
|
|
45
|
+
function getDisabled(command: Command): boolean {
|
|
46
|
+
return command.disabled === true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let $state = $watch({
|
|
50
|
+
visible: false,
|
|
51
|
+
search: "",
|
|
52
|
+
activeIndex: 0,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
$bind($state, $props, "visible");
|
|
56
|
+
|
|
57
|
+
// Reset the active item whenever the search changes or the palette opens
|
|
58
|
+
let lastSearch = "";
|
|
59
|
+
let lastVisible = false;
|
|
60
|
+
$run(() => {
|
|
61
|
+
const search = $state.search;
|
|
62
|
+
const visible = $state.visible;
|
|
63
|
+
if (search !== lastSearch || (visible && !lastVisible)) {
|
|
64
|
+
$state.activeIndex = 0;
|
|
65
|
+
}
|
|
66
|
+
lastSearch = search;
|
|
67
|
+
lastVisible = visible;
|
|
68
|
+
if (!visible) {
|
|
69
|
+
$state.search = "";
|
|
70
|
+
if (input) {
|
|
71
|
+
input.value = "";
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
// Focus the input once the modal content has rendered
|
|
75
|
+
setTimeout(() => input?.focus());
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// The commands that match the current search text
|
|
80
|
+
function filteredCommands(): Command[] {
|
|
81
|
+
const search = $state.search.trim().toLowerCase();
|
|
82
|
+
if (search === "") return $props?.commands ?? [];
|
|
83
|
+
return ($props?.commands ?? []).filter(
|
|
84
|
+
(command) =>
|
|
85
|
+
command.label.toLowerCase().includes(search) ||
|
|
86
|
+
(command.keywords ?? "").toLowerCase().includes(search),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function activeCommand(): Command | undefined {
|
|
91
|
+
return filteredCommands()[$state.activeIndex];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function optionId(index: number): string {
|
|
95
|
+
return `${$props?.id ?? defaultId}-option-${index}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function runCommand(command: Command | undefined) {
|
|
99
|
+
if (!command || getDisabled(command)) return;
|
|
100
|
+
$state.visible = false;
|
|
101
|
+
command.run?.();
|
|
102
|
+
$props?.onrun?.(command);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function handleInput(e: Event) {
|
|
106
|
+
$state.search = (e.currentTarget as HTMLInputElement).value;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function handleKey(e: KeyboardEvent) {
|
|
110
|
+
switch (e.key) {
|
|
111
|
+
case "ArrowDown": {
|
|
112
|
+
e.preventDefault();
|
|
113
|
+
const count = filteredCommands().length;
|
|
114
|
+
$state.activeIndex = Math.min(count - 1, $state.activeIndex + 1);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case "ArrowUp": {
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
$state.activeIndex = Math.max(0, $state.activeIndex - 1);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case "Enter": {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
runCommand(activeCommand());
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case "Escape": {
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
$state.visible = false;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Toggle the palette with a keyboard shortcut, e.g. mod+k
|
|
136
|
+
$onmount(() => {
|
|
137
|
+
if (!$props?.hotkey) return;
|
|
138
|
+
const [modifier, key] = parseHotkey($props.hotkey);
|
|
139
|
+
if (!key) return;
|
|
140
|
+
const handler = (e: KeyboardEvent) => {
|
|
141
|
+
const modOk =
|
|
142
|
+
modifier === "mod" ? e.metaKey || e.ctrlKey : modifier === "shift" ? e.shiftKey : true;
|
|
143
|
+
if (modOk && e.key.toLowerCase() === key.toLowerCase()) {
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
$state.visible = !$state.visible;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
document.addEventListener("keydown", handler);
|
|
149
|
+
return () => document.removeEventListener("keydown", handler);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
function parseHotkey(hotkey: string): [string | undefined, string | undefined] {
|
|
153
|
+
const parts = hotkey.split("+");
|
|
154
|
+
if (parts.length === 2) {
|
|
155
|
+
return [parts[0].toLowerCase(), parts[1]];
|
|
156
|
+
}
|
|
157
|
+
return [undefined, hotkey];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@render {
|
|
161
|
+
<div {$props?.id} class={["torp-command-palette", $props?.class]} {$props?.style}>
|
|
162
|
+
<Modal {$state.visible}>
|
|
163
|
+
<ModalOverlay />
|
|
164
|
+
<ModalContent class="torp-command-palette-content">
|
|
165
|
+
<input
|
|
166
|
+
class="torp-command-palette-input"
|
|
167
|
+
type="text"
|
|
168
|
+
role="combobox"
|
|
169
|
+
aria-expanded="true"
|
|
170
|
+
aria-controls={`${$props?.id ?? defaultId}-list`}
|
|
171
|
+
aria-activedescendant={optionId($state.activeIndex)}
|
|
172
|
+
aria-autocomplete="list"
|
|
173
|
+
aria-label={$props?.ariaLabel}
|
|
174
|
+
placeholder={$props?.placeholder}
|
|
175
|
+
&ref={input}
|
|
176
|
+
oninput={handleInput}
|
|
177
|
+
onkeydown={handleKey}
|
|
178
|
+
/>
|
|
179
|
+
<ul class="torp-command-palette-list" role="listbox" id={`${$props?.id ?? defaultId}-list`}>
|
|
180
|
+
@for (let [index, command] of filteredCommands().entries()) {
|
|
181
|
+
@key = command.value
|
|
182
|
+
<li
|
|
183
|
+
class="torp-command-palette-item"
|
|
184
|
+
role="option"
|
|
185
|
+
id={optionId(index)}
|
|
186
|
+
aria-selected={$state.activeIndex === index ? "true" : "false"}
|
|
187
|
+
aria-disabled={getDisabled(command) ? "true" : undefined}
|
|
188
|
+
data-state={$state.activeIndex === index ? "active" : "inactive"}
|
|
189
|
+
onclick={() => runCommand(command)}
|
|
190
|
+
>
|
|
191
|
+
<span class="torp-command-palette-label">{command.label}</span>
|
|
192
|
+
@if (command.shortcut) {
|
|
193
|
+
<span class="torp-command-palette-shortcut" aria-hidden="true">
|
|
194
|
+
{command.shortcut}
|
|
195
|
+
</span>
|
|
196
|
+
}
|
|
197
|
+
</li>
|
|
198
|
+
}
|
|
199
|
+
@if (filteredCommands().length === 0) {
|
|
200
|
+
<li class="torp-command-palette-empty">
|
|
201
|
+
{$props?.emptyText ?? "No results"}
|
|
202
|
+
</li>
|
|
203
|
+
}
|
|
204
|
+
</ul>
|
|
205
|
+
</ModalContent>
|
|
206
|
+
</Modal>
|
|
207
|
+
</div>
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@style {
|
|
211
|
+
/* Headless: layout only, so the input sits above the list. All
|
|
212
|
+
appearance is up to the consuming theme */
|
|
213
|
+
.torp-command-palette-content {
|
|
214
|
+
display: flex;
|
|
215
|
+
flex-direction: column;
|
|
216
|
+
}
|
|
217
|
+
.torp-command-palette-list {
|
|
218
|
+
list-style: none;
|
|
219
|
+
margin: 0;
|
|
220
|
+
padding: 0;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface Command {
|
|
2
|
+
/** A value identifying the command */
|
|
3
|
+
value: any;
|
|
4
|
+
/** The display label for the command */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Extra text that is matched when searching, but isn't displayed */
|
|
7
|
+
keywords?: string;
|
|
8
|
+
/** A keyboard shortcut hint to display, e.g. "⌘N" */
|
|
9
|
+
shortcut?: string;
|
|
10
|
+
/** If set to true, the command can't be run */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
/** The function to call when the command is run */
|
|
13
|
+
run?: () => void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type SlotRender } from "@torpor/view";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
interface CommandPaletteProps {
|
|
5
|
+
/** An ID for the root element */
|
|
6
|
+
id?: string;
|
|
7
|
+
/** Classes for the root element */
|
|
8
|
+
class?: ClassValue;
|
|
9
|
+
/** Styles for the root element */
|
|
10
|
+
style?: StyleValue;
|
|
11
|
+
/** Whether the palette is visible */
|
|
12
|
+
visible: Bindable<boolean | undefined>;
|
|
13
|
+
/** The commands that the user can choose from */
|
|
14
|
+
commands: Command[];
|
|
15
|
+
/** Text to display in the input when it is empty */
|
|
16
|
+
placeholder?: string;
|
|
17
|
+
/**
|
|
18
|
+
* A keyboard shortcut that toggles the palette, e.g. "mod+k", where "mod"
|
|
19
|
+
* means the Cmd key on Mac and the Ctrl key elsewhere
|
|
20
|
+
*/
|
|
21
|
+
hotkey?: string;
|
|
22
|
+
/** An accessible name for the input */
|
|
23
|
+
ariaLabel?: string;
|
|
24
|
+
/** Text to display when no commands match the search */
|
|
25
|
+
emptyText?: string;
|
|
26
|
+
/** Raised when a command has been run */
|
|
27
|
+
onrun?: (command: Command) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A modal input for searching and running commands, opened with a keyboard
|
|
32
|
+
* shortcut or programmatically.
|
|
33
|
+
*
|
|
34
|
+
* The structure of a CommandPalette is a Modal containing a search input and
|
|
35
|
+
* a filtered list of commands.
|
|
36
|
+
*/
|
|
37
|
+
declare function CommandPalette(
|
|
38
|
+
$parent: ParentNode,
|
|
39
|
+
$anchor: Node | null,
|
|
40
|
+
$props: CommandPaletteProps | undefined,
|
|
41
|
+
$context?: Record<PropertyKey, any>,
|
|
42
|
+
$slots?: Record<string, SlotRender>,
|
|
43
|
+
): void;
|
|
44
|
+
|
|
45
|
+
export { CommandPalette };
|