@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
|
@@ -2,20 +2,38 @@ export const ListBoxContextName: unique symbol = Symbol.for("torp.ListBox");
|
|
|
2
2
|
export const ListBoxItemContextName: unique symbol = Symbol.for("torp.ListBoxItem");
|
|
3
3
|
|
|
4
4
|
export interface ListBoxContext {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/** Called from
|
|
5
|
+
type: "single" | "multiple";
|
|
6
|
+
state: ListBoxState;
|
|
7
|
+
/** Called from a ListBoxItem when it is added */
|
|
8
|
+
registerItem: (state: ItemState) => void;
|
|
9
|
+
/** Called from a ListBoxItem when it is removed */
|
|
8
10
|
removeItem: (index: number) => void;
|
|
9
|
-
/** Called from
|
|
11
|
+
/** Called from a ListBoxItem when it is toggled */
|
|
10
12
|
toggleItem: (value: string) => void;
|
|
11
|
-
/** Called from
|
|
13
|
+
/** Called from a ListBoxItem when it receives a keyboard event */
|
|
12
14
|
handleKey: (index: number, e: KeyboardEvent) => void;
|
|
15
|
+
/** Called from a ListBoxItem when it receives focus */
|
|
16
|
+
setActiveDescendant?: (id: string) => void;
|
|
17
|
+
/** Reads the items loaded from the network loader, when one is set */
|
|
18
|
+
getLoadedItems?: () => any[];
|
|
19
|
+
/** Extracts the display text for a loaded item */
|
|
20
|
+
getItemLabel?: (item: any) => any;
|
|
21
|
+
/** Extracts the value for a loaded item */
|
|
22
|
+
getItemValue?: (item: any) => any;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ListBoxState {
|
|
26
|
+
value: any;
|
|
13
27
|
}
|
|
14
28
|
|
|
15
29
|
export interface ItemState {
|
|
30
|
+
id: string;
|
|
16
31
|
index: number;
|
|
32
|
+
text: string;
|
|
17
33
|
value: any;
|
|
18
34
|
selected: boolean;
|
|
35
|
+
/** Used for styling when item has keyboard focus */
|
|
36
|
+
active: boolean;
|
|
19
37
|
disabled: boolean;
|
|
20
38
|
parentDisabled: boolean;
|
|
21
39
|
/** Called in the ListBox when an item needs to be focused */
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import ListBoxItem from "./ListBoxItem.torp";
|
|
2
|
+
|
|
3
|
+
interface LoadedListBoxItemsProps {
|
|
4
|
+
/** The loaded items */
|
|
5
|
+
items: any[];
|
|
6
|
+
/** Extracts the display text for an item (defaults to `label ?? text ?? item`) */
|
|
7
|
+
getItemLabel?: (item: any) => any;
|
|
8
|
+
/** Extracts the value for an item (defaults to `value ?? id`) */
|
|
9
|
+
getItemValue?: (item: any) => any;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function defaultLabel(item: any): string {
|
|
13
|
+
const label = item?.label ?? item?.text ?? item;
|
|
14
|
+
return typeof label === "string" ? label : String(label);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function defaultValue(item: any): any {
|
|
18
|
+
return item?.value ?? item?.id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The internally rendered options for a ListBox that loads them from a
|
|
23
|
+
* network loader. Used as the slot fallback when no ListBoxItems are slotted
|
|
24
|
+
* in; not part of the public API.
|
|
25
|
+
*/
|
|
26
|
+
export default function LoadedListBoxItems($props?: LoadedListBoxItemsProps) {
|
|
27
|
+
const getLabel = $props?.getItemLabel ?? defaultLabel;
|
|
28
|
+
const getValue = $props?.getItemValue ?? defaultValue;
|
|
29
|
+
|
|
30
|
+
@render {
|
|
31
|
+
@for (let item of $props!.items) {
|
|
32
|
+
@key = getValue(item)
|
|
33
|
+
<ListBoxItem value={getValue(item)}>
|
|
34
|
+
{getLabel(item)}
|
|
35
|
+
</ListBoxItem>
|
|
36
|
+
}
|
|
37
|
+
@if ($props!.items.length === 0) {
|
|
38
|
+
<span class="torp-list-box-empty">No results.</span>
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
package/dist/ListBox/index.d.ts
CHANGED
|
@@ -5,19 +5,39 @@ interface ListBoxProps {
|
|
|
5
5
|
/** An ID for the root element */
|
|
6
6
|
id?: string;
|
|
7
7
|
/** Classes for the root element */
|
|
8
|
-
class?: ClassValue
|
|
8
|
+
class?: ClassValue;
|
|
9
9
|
/** Styles for the root element */
|
|
10
|
-
style?: StyleValue
|
|
10
|
+
style?: StyleValue;
|
|
11
11
|
/** Whether a single ListBoxItem or multiple ListBoxItems can be selected at the same time */
|
|
12
|
-
type?: "single" | "multiple"
|
|
12
|
+
type?: "single" | "multiple";
|
|
13
13
|
/** The value of the selected ListBoxItem, or an array of values if the type is "multiple" */
|
|
14
|
-
value
|
|
14
|
+
value: Bindable<any | any[]>;
|
|
15
15
|
/** If set to true, the user cannot interact with any items in the ListBox */
|
|
16
|
-
disabled?: boolean
|
|
16
|
+
disabled?: boolean;
|
|
17
17
|
/** An ARIA label for describing the ListBox to screen readers */
|
|
18
|
-
ariaLabel?: string | undefined
|
|
18
|
+
ariaLabel?: string | undefined;
|
|
19
|
+
/** An ID of an element that labels the ListBox */
|
|
20
|
+
ariaLabelledBy?: string | undefined;
|
|
21
|
+
/** The orientation of the ListBox */
|
|
22
|
+
orientation?: "vertical" | "horizontal";
|
|
19
23
|
/** Raised when the selected item(s) have changed */
|
|
20
|
-
|
|
24
|
+
/** The name of the component's form data field */
|
|
25
|
+
name?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Reads options from the network (or anywhere async), receiving
|
|
28
|
+
* `{ searchText }` whenever the bound `searchText` changes. When set and
|
|
29
|
+
* no ListBoxItems are slotted in, the options are rendered automatically
|
|
30
|
+
*/
|
|
31
|
+
load?: Loader;
|
|
32
|
+
/** Called after each successful load with the normalized result */
|
|
33
|
+
onload?: (result: LoadResult) => void;
|
|
34
|
+
/** Extracts the display text for a loaded item (defaults to `label ?? text ?? item`) */
|
|
35
|
+
getItemLabel?: (item: any) => any;
|
|
36
|
+
/** Extracts the value for a loaded item (defaults to `value ?? id`) */
|
|
37
|
+
getItemValue?: (item: any) => any;
|
|
38
|
+
/** Filter text for a `load` function; two-way bindable */
|
|
39
|
+
searchText?: Bindable<string | undefined>;
|
|
40
|
+
onchange?: (value: any | any[]) => void;
|
|
21
41
|
}
|
|
22
42
|
|
|
23
43
|
/**
|
|
@@ -48,17 +68,19 @@ interface ListBoxItemProps {
|
|
|
48
68
|
/** An ID for the root element */
|
|
49
69
|
id?: string;
|
|
50
70
|
/** Classes for the root element */
|
|
51
|
-
class?: ClassValue
|
|
71
|
+
class?: ClassValue;
|
|
52
72
|
/** Styles for the root element */
|
|
53
|
-
style?: StyleValue
|
|
73
|
+
style?: StyleValue;
|
|
54
74
|
/** The index of this item in the ListBox. Set this if you will be inserting, removing or sorting items. If you don't set this, it will be set automatically */
|
|
55
|
-
index?: number
|
|
75
|
+
index?: number;
|
|
76
|
+
/** The text for the item. If not set, this will come from it's text content */
|
|
77
|
+
text?: string;
|
|
56
78
|
/** A value for the item. Set this if you need more control over which item(s) are selected. If you don't set this, the value will be set to the item's initial index */
|
|
57
|
-
value?: string
|
|
79
|
+
value?: string;
|
|
58
80
|
/** If set to true, the user cannot interact with this ListBoxItem */
|
|
59
|
-
disabled?: boolean
|
|
60
|
-
/** Raised when this item
|
|
61
|
-
|
|
81
|
+
disabled?: boolean;
|
|
82
|
+
/** Raised when this item's selected state has changed */
|
|
83
|
+
onchange?: (selected: boolean) => void;
|
|
62
84
|
}
|
|
63
85
|
|
|
64
86
|
/**
|
|
@@ -78,9 +100,11 @@ interface ListBoxSeparatorProps {
|
|
|
78
100
|
/** An ID for the root element */
|
|
79
101
|
id?: string;
|
|
80
102
|
/** Classes for the root element */
|
|
81
|
-
class?: ClassValue
|
|
103
|
+
class?: ClassValue;
|
|
82
104
|
/** Styles for the root element */
|
|
83
|
-
style?: StyleValue
|
|
105
|
+
style?: StyleValue;
|
|
106
|
+
/** The orientation of the separator */
|
|
107
|
+
orientation?: "vertical" | "horizontal";
|
|
84
108
|
}
|
|
85
109
|
|
|
86
110
|
/**
|
|
@@ -94,4 +118,19 @@ declare function ListBoxSeparator(
|
|
|
94
118
|
$slots?: Record<string, SlotRender>,
|
|
95
119
|
): void;
|
|
96
120
|
|
|
97
|
-
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The internally rendered options for a ListBox that loads them from a
|
|
125
|
+
* network loader. Used as the slot fallback when no ListBoxItems are slotted
|
|
126
|
+
* in; not part of the public API.
|
|
127
|
+
*/
|
|
128
|
+
declare function LoadedListBoxItems(
|
|
129
|
+
$parent: ParentNode,
|
|
130
|
+
$anchor: Node | null,
|
|
131
|
+
$props?: LoadedListBoxItemsProps,
|
|
132
|
+
$context?: Record<PropertyKey, any>,
|
|
133
|
+
$slots?: Record<string, SlotRender>,
|
|
134
|
+
): void;
|
|
135
|
+
|
|
136
|
+
export { ListBox, ListBoxItem, ListBoxSeparator, LoadedListBoxItems };
|
package/dist/ListBox/index.js
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import ListBoxItem_default from "./ListBoxItem.torp";
|
|
6
|
-
|
|
7
|
-
// src/ListBox/ListBoxSeparator.torp
|
|
8
|
-
import ListBoxSeparator_default from "./ListBoxSeparator.torp";
|
|
9
|
-
export {
|
|
10
|
-
ListBox_default as ListBox,
|
|
11
|
-
ListBoxItem_default as ListBoxItem,
|
|
12
|
-
ListBoxSeparator_default as ListBoxSeparator
|
|
13
|
-
};
|
|
14
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import ListBox from "./ListBox.torp";
|
|
2
|
+
import ListBoxItem from "./ListBoxItem.torp";
|
|
3
|
+
import ListBoxSeparator from "./ListBoxSeparator.torp";
|
|
4
|
+
export { ListBox, ListBoxItem, ListBoxSeparator };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { Bindable, ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import {
|
|
3
|
+
type FieldContext,
|
|
4
|
+
FieldContextName,
|
|
5
|
+
type FormContext,
|
|
6
|
+
FormContextName,
|
|
7
|
+
} from "../Form/FormTypes";
|
|
8
|
+
import FormValues from "../Form/FormValues.torp";
|
|
9
|
+
import { extractRaw, formatMask, parseMask } from "../utils/maskUtils";
|
|
10
|
+
import createFormField from "../utils/formField";
|
|
11
|
+
|
|
12
|
+
interface MaskedInputProps {
|
|
13
|
+
/** An ID for the root element */
|
|
14
|
+
id?: string;
|
|
15
|
+
/** Classes for the root element */
|
|
16
|
+
class?: ClassValue;
|
|
17
|
+
/** Styles for the root element */
|
|
18
|
+
style?: StyleValue;
|
|
19
|
+
/**
|
|
20
|
+
* The input mask: `9` accepts a digit, `L` a letter and `a` an
|
|
21
|
+
* alphanumeric character; any other character is a literal shown as-is.
|
|
22
|
+
* Use `\x` to escape a token character as a literal. e.g. "(999) 999-9999"
|
|
23
|
+
*/
|
|
24
|
+
mask: string;
|
|
25
|
+
/** The raw entered characters, without mask literals */
|
|
26
|
+
value?: Bindable<string>;
|
|
27
|
+
/** Text displayed when no value has been entered (defaults to the mask) */
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
/** If set to true, the user cannot interact with this input */
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
/** An accessible name for the input */
|
|
32
|
+
ariaLabel?: string;
|
|
33
|
+
/** The name of the component's form data field */
|
|
34
|
+
name?: string;
|
|
35
|
+
/** Raised when the value has changed */
|
|
36
|
+
onchange?: (value: string) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* An input that formats its contents as the user types -- phone numbers,
|
|
41
|
+
* credit cards, product codes -- displaying literals automatically while
|
|
42
|
+
* binding only the underlying characters.
|
|
43
|
+
*
|
|
44
|
+
* ```
|
|
45
|
+
* <MaskedInput mask="(999) 999-9999" &value={$state.phone} />
|
|
46
|
+
* <MaskedInput mask="LLL-9999" name="code" />
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* Note that a complete value is not required: the bound value grows as the
|
|
50
|
+
* user types, so validate completeness with a schema when needed.
|
|
51
|
+
*/
|
|
52
|
+
export default function MaskedInput($props: MaskedInputProps | undefined) {
|
|
53
|
+
let input: HTMLInputElement;
|
|
54
|
+
|
|
55
|
+
const tokens = parseMask($props?.mask ?? "");
|
|
56
|
+
|
|
57
|
+
// Wire up the enclosing Form and Field, if any
|
|
58
|
+
const form = createFormField(
|
|
59
|
+
$props?.name,
|
|
60
|
+
$context[FormContextName] as FormContext | undefined,
|
|
61
|
+
$context[FieldContextName] as FieldContext | undefined,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
function getDisabled(): boolean {
|
|
65
|
+
return $props?.disabled === true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// The bound value is the raw entered characters, so the state key
|
|
69
|
+
// matches the prop name for $bind
|
|
70
|
+
let $state = $watch({
|
|
71
|
+
value: "",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
$bind($state, $props, "value");
|
|
75
|
+
|
|
76
|
+
function display(): string {
|
|
77
|
+
return formatMask(tokens, $state.value ?? "");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getPlaceholder(): string | undefined {
|
|
81
|
+
return $props?.placeholder ?? $props?.mask;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Keep the element's text matching the formatted raw value; typing sets
|
|
85
|
+
// the property directly, so an attribute alone would not update it
|
|
86
|
+
function syncInput() {
|
|
87
|
+
if (input && !getDisabled() && input.value !== display()) {
|
|
88
|
+
input.value = display();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
$run(() => {
|
|
92
|
+
if (!input) return;
|
|
93
|
+
const raw = $state.value ?? "";
|
|
94
|
+
const next = formatMask(tokens, raw);
|
|
95
|
+
if (input.value !== next) {
|
|
96
|
+
input.value = next;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
$onmount(() => syncInput());
|
|
100
|
+
|
|
101
|
+
function handleInput(e: Event) {
|
|
102
|
+
if (getDisabled()) return;
|
|
103
|
+
const el = e.currentTarget as HTMLInputElement;
|
|
104
|
+
|
|
105
|
+
const raw = extractRaw(tokens, el.value);
|
|
106
|
+
if (raw !== $state.value) {
|
|
107
|
+
$state.value = raw;
|
|
108
|
+
form.handleInput();
|
|
109
|
+
$props?.onchange?.(raw);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Rewrite the element so rejected characters disappear immediately
|
|
113
|
+
el.value = formatMask(tokens, raw);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function handleBlur() {
|
|
117
|
+
form.handleBlur();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@render {
|
|
121
|
+
<input
|
|
122
|
+
&ref={input}
|
|
123
|
+
id={$props?.id}
|
|
124
|
+
class={["torp-masked-input", $props?.class]}
|
|
125
|
+
{$props?.style}
|
|
126
|
+
type="text"
|
|
127
|
+
spellcheck="false"
|
|
128
|
+
autocomplete="off"
|
|
129
|
+
value={display()}
|
|
130
|
+
placeholder={getPlaceholder()}
|
|
131
|
+
disabled={getDisabled()}
|
|
132
|
+
tabindex={getDisabled() ? -1 : 0}
|
|
133
|
+
aria-label={$props?.ariaLabel}
|
|
134
|
+
data-valid={form.valid ? "valid" : "invalid"}
|
|
135
|
+
aria-describedby={form.messageId}
|
|
136
|
+
aria-invalid={form.inField && !form.valid ? "true" : undefined}
|
|
137
|
+
oninput={handleInput}
|
|
138
|
+
onblur={handleBlur}
|
|
139
|
+
/>
|
|
140
|
+
|
|
141
|
+
<FormValues name={form.name} value={$state.value || undefined} />
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type SlotRender } from "@torpor/view";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
interface MaskedInputProps {
|
|
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
|
+
/**
|
|
12
|
+
* The input mask: `9` accepts a digit, `L` a letter and `a` an
|
|
13
|
+
* alphanumeric character; any other character is a literal shown as-is.
|
|
14
|
+
* Use `\x` to escape a token character as a literal. e.g. "(999) 999-9999"
|
|
15
|
+
*/
|
|
16
|
+
mask: string;
|
|
17
|
+
/** The raw entered characters, without mask literals */
|
|
18
|
+
value?: Bindable<string>;
|
|
19
|
+
/** Text displayed when no value has been entered (defaults to the mask) */
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
/** If set to true, the user cannot interact with this input */
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
/** An accessible name for the input */
|
|
24
|
+
ariaLabel?: string;
|
|
25
|
+
/** The name of the component's form data field */
|
|
26
|
+
name?: string;
|
|
27
|
+
/** Raised when the value has changed */
|
|
28
|
+
onchange?: (value: string) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* An input that formats its contents as the user types -- phone numbers,
|
|
33
|
+
* credit cards, product codes -- displaying literals automatically while
|
|
34
|
+
* binding only the underlying characters.
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* <MaskedInput mask="(999) 999-9999" &value={$state.phone} />
|
|
38
|
+
* <MaskedInput mask="LLL-9999" name="code" />
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* Note that a complete value is not required: the bound value grows as the
|
|
42
|
+
* user types, so validate completeness with a schema when needed.
|
|
43
|
+
*/
|
|
44
|
+
declare function MaskedInput(
|
|
45
|
+
$parent: ParentNode,
|
|
46
|
+
$anchor: Node | null,
|
|
47
|
+
$props: MaskedInputProps | undefined,
|
|
48
|
+
$context?: Record<PropertyKey, any>,
|
|
49
|
+
$slots?: Record<string, SlotRender>,
|
|
50
|
+
): void;
|
|
51
|
+
|
|
52
|
+
export { MaskedInput };
|
package/dist/Menu/Menu.torp
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
type MenuContext,
|
|
6
|
-
type ItemState,
|
|
7
|
-
MenuPopoutContextName,
|
|
8
|
-
type MenuPopoutContext
|
|
9
|
-
} from "./MenuTypes";
|
|
2
|
+
import { type PopoutContext, PopoutContextName } from "../utils/PopoutTypes";
|
|
3
|
+
import { createFocusGroup } from "../utils/focusGroup";
|
|
4
|
+
import { type ItemState, type MenuContext, MenuContextName } from "./MenuTypes";
|
|
10
5
|
|
|
11
6
|
interface MenuProps {
|
|
12
7
|
/** An ID for the root element */
|
|
13
8
|
id?: string;
|
|
14
9
|
/** Classes for the root element */
|
|
15
|
-
class?: ClassValue
|
|
10
|
+
class?: ClassValue;
|
|
16
11
|
/** Styles for the root element */
|
|
17
|
-
style?: StyleValue
|
|
12
|
+
style?: StyleValue;
|
|
18
13
|
/** An ARIA label for describing the menu to screen readers */
|
|
19
|
-
ariaLabel?: string
|
|
14
|
+
ariaLabel?: string;
|
|
20
15
|
}
|
|
21
16
|
|
|
22
17
|
/**
|
|
@@ -39,92 +34,80 @@ interface MenuProps {
|
|
|
39
34
|
*/
|
|
40
35
|
export default function Menu($props: MenuProps | undefined) {
|
|
41
36
|
// A collection containing the state and some functions for each item
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
const group = createFocusGroup<ItemState>();
|
|
38
|
+
const { itemStates } = group;
|
|
44
39
|
|
|
45
|
-
//
|
|
46
|
-
|
|
40
|
+
// The text typed for type-ahead, which is reset after a short pause between key presses
|
|
41
|
+
let searchText = "";
|
|
42
|
+
let searchTimer: ReturnType<typeof setTimeout> | undefined;
|
|
43
|
+
|
|
44
|
+
// Get stuff out of the PopoutContext
|
|
47
45
|
const popoutContext = $context[PopoutContextName] as PopoutContext;
|
|
48
46
|
if (!popoutContext) {
|
|
49
47
|
throw new Error("Menu must be contained within a popout component");
|
|
50
48
|
}
|
|
51
|
-
const {
|
|
49
|
+
const { handleButton, state } = popoutContext;
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const inPopout = !!menuPopoutContext;
|
|
56
|
-
const setPopoutVisible = menuPopoutContext?.setVisible;
|
|
51
|
+
const inPopout = state.contentRole === "menu";
|
|
52
|
+
state.contentRole = "menu";
|
|
57
53
|
|
|
58
54
|
// Set the focusFirstElement method in the PopoverContext(s) so that it can be called after everything has been shown
|
|
59
|
-
popoutContext.focusFirstElement =
|
|
60
|
-
|
|
61
|
-
menuPopoutContext.focusFirstElement = focusFirstElement;
|
|
62
|
-
menuPopoutContext.focusLastElement = focusLastElement;
|
|
63
|
-
}
|
|
55
|
+
popoutContext.focusFirstElement = group.focusFirstItem;
|
|
56
|
+
popoutContext.focusLastElement = group.focusLastItem;
|
|
64
57
|
|
|
65
58
|
// Set the context to pass down to items
|
|
66
59
|
let context: MenuContext = {
|
|
67
|
-
setVisible,
|
|
68
60
|
handleButton,
|
|
69
61
|
handleItemFocus,
|
|
70
62
|
handleItemKey,
|
|
63
|
+
handlePopout,
|
|
71
64
|
state,
|
|
72
|
-
registerItem
|
|
65
|
+
registerItem,
|
|
73
66
|
};
|
|
74
67
|
$context[MenuContextName] = context;
|
|
75
68
|
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
itemStates[0].setFocused();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function focusLastElement() {
|
|
83
|
-
if (itemStates.length > 0) {
|
|
84
|
-
itemStates.at(-1)!.setFocused();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function registerItem(setFocused: () => void) {
|
|
89
|
-
// Add the item's state to the array and return its index
|
|
90
|
-
const newItemState: ItemState = {
|
|
91
|
-
setFocused
|
|
92
|
-
};
|
|
93
|
-
itemStates.push(newItemState);
|
|
94
|
-
itemStates = itemStates;
|
|
95
|
-
return { index: itemStates.length - 1 };
|
|
69
|
+
function registerItem(item: ItemState) {
|
|
70
|
+
return group.registerItem(item);
|
|
96
71
|
}
|
|
97
72
|
|
|
98
73
|
function handleItemFocus(index: number) {
|
|
99
|
-
|
|
74
|
+
group.handleItemFocus(index);
|
|
100
75
|
}
|
|
101
76
|
|
|
102
77
|
function handleItemKey(e: KeyboardEvent) {
|
|
103
78
|
switch (e.key) {
|
|
104
|
-
case "Esc":
|
|
105
79
|
case "Escape": {
|
|
106
80
|
e.preventDefault();
|
|
107
|
-
|
|
81
|
+
state.visible = false;
|
|
108
82
|
break;
|
|
109
83
|
}
|
|
110
|
-
case "Down":
|
|
111
84
|
case "ArrowDown": {
|
|
112
85
|
e.preventDefault();
|
|
113
86
|
focusItem("next");
|
|
114
87
|
break;
|
|
115
88
|
}
|
|
116
|
-
case "Up":
|
|
117
89
|
case "ArrowUp": {
|
|
118
90
|
e.preventDefault();
|
|
119
91
|
focusItem("previous");
|
|
120
92
|
break;
|
|
121
93
|
}
|
|
122
|
-
case "Left":
|
|
123
94
|
case "ArrowLeft": {
|
|
124
|
-
|
|
125
|
-
|
|
95
|
+
if (popoutContext.navigateMenuBar) {
|
|
96
|
+
// In a menu under a menubar item: move to the previous item and open its menu
|
|
126
97
|
e.preventDefault();
|
|
127
|
-
|
|
98
|
+
popoutContext.navigateMenuBar("previous");
|
|
99
|
+
} else if (inPopout) {
|
|
100
|
+
// TODO: This should be based on what side the menu shows on
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
state.visible = false;
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
case "ArrowRight": {
|
|
107
|
+
if (popoutContext.navigateMenuBar) {
|
|
108
|
+
// In a menu under a menubar item: move to the next item and open its menu
|
|
109
|
+
e.preventDefault();
|
|
110
|
+
popoutContext.navigateMenuBar("next");
|
|
128
111
|
}
|
|
129
112
|
break;
|
|
130
113
|
}
|
|
@@ -138,29 +121,60 @@ export default function Menu($props: MenuProps | undefined) {
|
|
|
138
121
|
focusItem("end");
|
|
139
122
|
break;
|
|
140
123
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
function focusItem(target: "start" | "previous" | "next" | "end") {
|
|
145
|
-
switch (target) {
|
|
146
|
-
case "start": {
|
|
147
|
-
focusIndex = 0;
|
|
124
|
+
case "Tab": {
|
|
125
|
+
state.visible = false;
|
|
148
126
|
break;
|
|
149
127
|
}
|
|
150
|
-
|
|
151
|
-
|
|
128
|
+
default: {
|
|
129
|
+
// Type-ahead: typing a character focuses the next item whose label starts with it
|
|
130
|
+
if (e.key.length === 1 && e.key !== " ") {
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
search(e.key);
|
|
133
|
+
}
|
|
152
134
|
break;
|
|
153
135
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function search(key: string) {
|
|
140
|
+
const character = key.toLocaleLowerCase();
|
|
141
|
+
if (searchTimer) {
|
|
142
|
+
clearTimeout(searchTimer);
|
|
143
|
+
// Typing the same character repeatedly cycles through the items that
|
|
144
|
+
// start with it, instead of searching for a repeated-character string
|
|
145
|
+
if (searchText.length !== 1 || character !== searchText) {
|
|
146
|
+
searchText += character;
|
|
157
147
|
}
|
|
158
|
-
|
|
159
|
-
|
|
148
|
+
} else {
|
|
149
|
+
searchText = character;
|
|
150
|
+
}
|
|
151
|
+
searchTimer = setTimeout(() => {
|
|
152
|
+
searchTimer = undefined;
|
|
153
|
+
searchText = "";
|
|
154
|
+
}, 1000);
|
|
155
|
+
|
|
156
|
+
// Focus the next item whose label starts with the search text, wrapping
|
|
157
|
+
// around to the start and skipping disabled items
|
|
158
|
+
for (let offset = 1; offset <= itemStates.length; offset++) {
|
|
159
|
+
const item = itemStates[(group.getFocusIndex() + offset) % itemStates.length];
|
|
160
|
+
if (!item.disabled && item.text?.toLocaleLowerCase().startsWith(searchText)) {
|
|
161
|
+
item.setFocused();
|
|
160
162
|
break;
|
|
161
163
|
}
|
|
162
164
|
}
|
|
163
|
-
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function focusItem(target: "start" | "previous" | "next" | "end") {
|
|
168
|
+
group.moveFocus(group.getFocusIndex(), target);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function handlePopout(index: number) {
|
|
172
|
+
// Hide all other submenus
|
|
173
|
+
for (let item of itemStates) {
|
|
174
|
+
if (item.index !== index) {
|
|
175
|
+
item.setVisible?.(false);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
164
178
|
}
|
|
165
179
|
|
|
166
180
|
@render {
|
|
@@ -170,6 +184,7 @@ export default function Menu($props: MenuProps | undefined) {
|
|
|
170
184
|
{$props?.style}
|
|
171
185
|
tabindex="-1"
|
|
172
186
|
role="menu"
|
|
187
|
+
aria-labelledby={state.triggerId}
|
|
173
188
|
aria-label={$props?.ariaLabel}
|
|
174
189
|
aria-orientation="vertical"
|
|
175
190
|
>
|