@torpor/ui 0.3.7 → 1.0.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/README.md +83 -7
- package/dist/Accordion/Accordion.torp +43 -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 +6 -6
- 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 +25 -4
- 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 +70 -0
- 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 +83 -0
- package/dist/Charts/ChartYAxis.torp +105 -0
- package/dist/Charts/ColumnChart.torp +70 -0
- package/dist/{Chart → Charts}/GridLines.torp +7 -4
- package/dist/Charts/LineChart.torp +73 -0
- package/dist/{Chart → Charts}/PieChart.torp +14 -20
- package/dist/{Chart → Charts}/ScatterChart.torp +45 -38
- package/dist/{Chart → Charts}/SparkLine.torp +16 -19
- package/dist/{Chart → Charts}/XAxis.torp +18 -12
- package/dist/{Chart → Charts}/YAxis.torp +15 -9
- package/dist/Charts/index.d.ts +469 -0
- package/dist/Charts/index.js +17 -0
- 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 +112 -0
- package/dist/ComboBox/ComboBoxInput.torp +145 -0
- package/dist/ComboBox/ComboBoxTrigger.torp +49 -0
- package/dist/ComboBox/ComboBoxTypes.ts +39 -0
- package/dist/ComboBox/index.d.ts +152 -0
- package/dist/ComboBox/index.js +6 -0
- package/dist/CommandPalette/CommandPalette.torp +220 -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 +39 -52
- 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 +5 -18
- 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 +311 -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 +33 -30
- package/dist/Dialog/DialogBody.torp +5 -9
- package/dist/Dialog/DialogButton.torp +14 -11
- package/dist/Dialog/DialogFooter.torp +3 -7
- package/dist/Dialog/DialogHeader.torp +6 -10
- package/dist/Dialog/DialogTypes.ts +2 -2
- package/dist/Dialog/index.d.ts +28 -26
- 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/DisclosureTypes.ts +1 -1
- package/dist/Disclosure/index.d.ts +14 -16
- package/dist/Disclosure/index.js +5 -18
- package/dist/Drawer/Drawer.torp +21 -51
- package/dist/Drawer/DrawerContent.torp +67 -13
- package/dist/Drawer/DrawerOverlay.torp +4 -4
- package/dist/Drawer/DrawerTrigger.torp +16 -4
- package/dist/Drawer/DrawerTypes.ts +4 -1
- 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 +28 -20
- 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 +39 -0
- 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 +198 -77
- package/dist/Form/index.js +16 -46
- package/dist/ListBox/ListBox.torp +374 -0
- package/dist/ListBox/ListBoxItem.torp +131 -0
- package/dist/ListBox/ListBoxSeparator.torp +29 -0
- package/dist/ListBox/ListBoxTypes.ts +41 -0
- package/dist/ListBox/LoadedListBoxItems.torp +41 -0
- package/dist/ListBox/index.d.ts +136 -0
- package/dist/ListBox/index.js +4 -0
- 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 +90 -70
- 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 +12 -21
- 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 +34 -55
- package/dist/MenuBar/MenuBarItemContent.torp +15 -101
- package/dist/MenuBar/MenuBarItemTrigger.torp +20 -55
- package/dist/MenuBar/MenuBarTypes.ts +13 -12
- package/dist/MenuBar/index.d.ts +17 -19
- package/dist/MenuBar/index.js +5 -18
- package/dist/Modal/Modal.torp +28 -48
- 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 +6 -17
- 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 -104
- package/dist/NavMenu/NavMenuButton.torp +10 -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 +16 -61
- package/dist/NavMenu/NavMenuPopoutContent.torp +14 -73
- package/dist/NavMenu/NavMenuPopoutTrigger.torp +47 -0
- package/dist/NavMenu/NavMenuSeparator.torp +3 -6
- package/dist/NavMenu/NavMenuTypes.ts +6 -15
- package/dist/NavMenu/index.d.ts +55 -55
- 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 +152 -0
- package/dist/Pagination/PaginationItem.torp +137 -0
- package/dist/Pagination/PaginationTypes.ts +22 -0
- package/dist/Pagination/buildPages.ts +61 -0
- package/dist/Pagination/index.d.ts +63 -0
- package/dist/Pagination/index.js +3 -0
- package/dist/Popover/Popover.torp +39 -52
- 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 +5 -18
- 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 +190 -0
- package/dist/SelectBox/SelectBoxContent.torp +107 -0
- package/dist/SelectBox/SelectBoxTrigger.torp +141 -0
- package/dist/SelectBox/SelectBoxTypes.ts +33 -0
- package/dist/SelectBox/index.d.ts +112 -0
- package/dist/SelectBox/index.js +5 -0
- package/dist/Slider/Slider.torp +248 -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 +3 -3
- 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 +117 -0
- package/dist/ToolBar/ToolBarButton.torp +56 -0
- package/dist/ToolBar/ToolBarElement.torp +51 -0
- package/dist/ToolBar/ToolBarPopout.torp +45 -0
- package/dist/ToolBar/ToolBarPopoutContent.torp +34 -0
- package/dist/ToolBar/ToolBarPopoutTrigger.torp +47 -0
- package/dist/ToolBar/ToolBarSeparator.torp +21 -0
- package/dist/ToolBar/ToolBarTypes.ts +28 -0
- package/dist/ToolBar/index.d.ts +210 -0
- package/dist/ToolBar/index.js +8 -0
- 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 +72 -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 +82 -0
- 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/chartUtils.ts +1 -1
- 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 +116 -25
- 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/Chart/BarChart.torp +0 -159
- package/dist/Chart/LineChart.torp +0 -196
- package/dist/Chart/index.d.ts +0 -220
- package/dist/Chart/index.js +0 -34
- package/dist/Chart/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/Menu/index.js.map +0 -1
- package/dist/MenuBar/index.js.map +0 -1
- package/dist/NavMenu/NavMenuPopoutButton.torp +0 -129
- package/dist/NavMenu/index.js.map +0 -1
- package/dist/TabGroup/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
- /package/dist/{Chart → Charts}/DataSeries.ts +0 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import type { Bindable, ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import $handle from "../utils/$handle";
|
|
3
|
+
import {
|
|
4
|
+
type FieldContext,
|
|
5
|
+
FieldContextName,
|
|
6
|
+
type FormContext,
|
|
7
|
+
FormContextName,
|
|
8
|
+
} from "../Form/FormTypes";
|
|
9
|
+
import type { LoadResult, Loader } from "../utils/loader";
|
|
10
|
+
import { type PopoutContext, PopoutContextName } from "../utils/PopoutTypes";
|
|
11
|
+
import { createItemGroup } from "../utils/createItemGroup";
|
|
12
|
+
import getId from "../utils/getId";
|
|
13
|
+
import createFormField from "../utils/formField";
|
|
14
|
+
import {
|
|
15
|
+
type ItemState,
|
|
16
|
+
type TreeContext,
|
|
17
|
+
type TreeLoadRequest,
|
|
18
|
+
TreeContextName,
|
|
19
|
+
} from "./TreeTypes";
|
|
20
|
+
import FormValues from "../Form/FormValues.torp";
|
|
21
|
+
|
|
22
|
+
interface TreeProps {
|
|
23
|
+
/** An ID for the root element */
|
|
24
|
+
id?: string;
|
|
25
|
+
/** Classes for the root element */
|
|
26
|
+
class?: ClassValue;
|
|
27
|
+
/** Styles for the root element */
|
|
28
|
+
style?: StyleValue;
|
|
29
|
+
/** The value(s) of the expanded TreeItem(s) */
|
|
30
|
+
expanded: Bindable<string | string[] | undefined>;
|
|
31
|
+
/** Whether a single TreeItem or multiple TreeItems can be selected at the same time */
|
|
32
|
+
type?: "single" | "multiple";
|
|
33
|
+
/** The value of the selected TreeItem, or an array of values if the type is "multiple" */
|
|
34
|
+
value: Bindable<string | string[] | undefined>;
|
|
35
|
+
/** If set to true, the user cannot interact with any items in the Tree */
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
/** The aria-label for the root element */
|
|
38
|
+
ariaLabel?: string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Reads the children of an item from the network (or anywhere async) when
|
|
41
|
+
* the item is first expanded. Receives a request with `item` (the parent
|
|
42
|
+
* item's value). Items load automatically when they set the TreeItem
|
|
43
|
+
* `hasChildren` prop; a TreeItem `load` prop overrides this per item.
|
|
44
|
+
*/
|
|
45
|
+
load?: Loader<any, TreeLoadRequest>;
|
|
46
|
+
/** Called after each successful child load with the normalized result */
|
|
47
|
+
onload?: (result: LoadResult, item: string | undefined) => void;
|
|
48
|
+
/** Extracts the display text for a loaded item (defaults to `label ?? text ?? item`) */
|
|
49
|
+
getItemLabel?: (item: any) => any;
|
|
50
|
+
/** Extracts the value for a loaded item (defaults to `value ?? id`) */
|
|
51
|
+
getItemValue?: (item: any) => any;
|
|
52
|
+
/** Raised when the expanded item(s) have changed */
|
|
53
|
+
onexpand?: (value: string | string[]) => void;
|
|
54
|
+
/** Raised when the selected item(s) have changed */
|
|
55
|
+
/** The name of the component's form data field */
|
|
56
|
+
name?: string;
|
|
57
|
+
onchange?: (value: string | string[] | undefined) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A hierarchical list of items that can be expanded and collapsed to reveal nested content,
|
|
62
|
+
* with support for selecting items.
|
|
63
|
+
*
|
|
64
|
+
* See [the WAI ARIA guidelines for Tree Views](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/).
|
|
65
|
+
*
|
|
66
|
+
* The structure of a Tree is:
|
|
67
|
+
*
|
|
68
|
+
* ```
|
|
69
|
+
* <Tree>
|
|
70
|
+
* <TreeItem>
|
|
71
|
+
* <TreeItemContent>
|
|
72
|
+
* <TreeItemTrigger />
|
|
73
|
+
* <TreeItemLabel />
|
|
74
|
+
* </TreeItemContent>
|
|
75
|
+
* <TreeItemChildren>
|
|
76
|
+
* <TreeItem />
|
|
77
|
+
* </TreeItemChildren>
|
|
78
|
+
* </TreeItem>
|
|
79
|
+
* </Tree>
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export default function Tree($props: TreeProps | undefined) {
|
|
83
|
+
let defaultId = getId();
|
|
84
|
+
let root: HTMLElement;
|
|
85
|
+
|
|
86
|
+
// Wire up the enclosing Form and Field, if any
|
|
87
|
+
const form = createFormField(
|
|
88
|
+
$props?.name,
|
|
89
|
+
$context[FormContextName] as FormContext | undefined,
|
|
90
|
+
$context[FieldContextName] as FieldContext | undefined,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Get the current selection type, re-reading $props so changes are picked up
|
|
94
|
+
function getType(): "single" | "multiple" {
|
|
95
|
+
return $props?.type ?? "single";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if ($props?.load !== undefined && typeof $props.load !== "function") {
|
|
99
|
+
throw new Error("Tree: `load` must be a function");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let $state = $watch({
|
|
103
|
+
expanded: $props?.expanded ?? [],
|
|
104
|
+
value: $props?.value ?? (getType() === "single" ? undefined : []),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
$bind($state, $props, "expanded", "value");
|
|
108
|
+
|
|
109
|
+
// Maybe get stuff out of a popout context
|
|
110
|
+
const popoutContext = $context[PopoutContextName] as PopoutContext | undefined;
|
|
111
|
+
if (popoutContext) {
|
|
112
|
+
// Set the content ID and role so that triggers can use them with
|
|
113
|
+
// aria-controls / aria-haspopup
|
|
114
|
+
popoutContext.state.contentId = $props?.id ?? defaultId;
|
|
115
|
+
popoutContext.state.contentRole = "tree";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Expansion behaves like a multiple-select group over the "expanded" flag;
|
|
119
|
+
// selection behaves like a single- or multiple-select group over "selected".
|
|
120
|
+
// Both groups share the same item states.
|
|
121
|
+
const expandedGroup = createItemGroup<ItemState>({
|
|
122
|
+
state: {
|
|
123
|
+
get value() {
|
|
124
|
+
return $state.expanded;
|
|
125
|
+
},
|
|
126
|
+
set value(value) {
|
|
127
|
+
$state.expanded = value;
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
type: "multiple",
|
|
131
|
+
selectedProperty: "expanded",
|
|
132
|
+
});
|
|
133
|
+
const selectGroup = createItemGroup<ItemState>({
|
|
134
|
+
state: $state,
|
|
135
|
+
type: getType,
|
|
136
|
+
selectedProperty: "selected",
|
|
137
|
+
disabled: () => $props?.disabled,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Both groups hold the same item states in the same order
|
|
141
|
+
const itemStates = selectGroup.itemStates;
|
|
142
|
+
|
|
143
|
+
let context: TreeContext = {
|
|
144
|
+
get type() {
|
|
145
|
+
return getType();
|
|
146
|
+
},
|
|
147
|
+
state: $state,
|
|
148
|
+
registerItem,
|
|
149
|
+
removeItem,
|
|
150
|
+
toggleExpand: expandedGroup.toggleItem,
|
|
151
|
+
toggleSelect: (value) => {
|
|
152
|
+
selectGroup.toggleItem(value);
|
|
153
|
+
form.handleInput();
|
|
154
|
+
},
|
|
155
|
+
handleKey,
|
|
156
|
+
focusFirstItem,
|
|
157
|
+
focusLastItem,
|
|
158
|
+
findItem,
|
|
159
|
+
getNextVisibleItem,
|
|
160
|
+
getPreviousVisibleItem,
|
|
161
|
+
getParentItem,
|
|
162
|
+
getFirstChildItem,
|
|
163
|
+
load: typeof $props?.load === "function" ? $props.load : undefined,
|
|
164
|
+
onload: $props?.onload,
|
|
165
|
+
getItemLabel: $props?.getItemLabel,
|
|
166
|
+
getItemValue: $props?.getItemValue,
|
|
167
|
+
};
|
|
168
|
+
$context[TreeContextName] = context;
|
|
169
|
+
|
|
170
|
+
// Dispatch the expand event when the expanded value changes
|
|
171
|
+
$handle((first) => {
|
|
172
|
+
const value = $state.expanded;
|
|
173
|
+
if (!first) $props?.onexpand?.(value);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Dispatch the change event when the selected value changes
|
|
177
|
+
$handle((first) => {
|
|
178
|
+
const value = $state.value;
|
|
179
|
+
if (!first) $props?.onchange?.(value);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
function registerItem(itemState: ItemState, level: number) {
|
|
183
|
+
itemState.level = level;
|
|
184
|
+
expandedGroup.registerItem(itemState);
|
|
185
|
+
selectGroup.registerItem(itemState);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function removeItem(itemIndex: number) {
|
|
189
|
+
expandedGroup.removeItem(itemIndex);
|
|
190
|
+
selectGroup.removeItem(itemIndex);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function handleKey(index: number, e: KeyboardEvent) {
|
|
194
|
+
switch (e.key) {
|
|
195
|
+
case "ArrowUp": {
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
let previousItem = getPreviousVisibleItem(index);
|
|
198
|
+
if (previousItem) {
|
|
199
|
+
previousItem.setFocused();
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case "ArrowDown": {
|
|
204
|
+
e.preventDefault();
|
|
205
|
+
let nextItem = getNextVisibleItem(index);
|
|
206
|
+
if (nextItem) {
|
|
207
|
+
nextItem.setFocused();
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case "ArrowLeft": {
|
|
212
|
+
e.preventDefault();
|
|
213
|
+
let currentItem = findItem(index);
|
|
214
|
+
if (currentItem && currentItem.expanded) {
|
|
215
|
+
expandedGroup.toggleItem(currentItem.value);
|
|
216
|
+
} else {
|
|
217
|
+
let parentItem = getParentItem(index);
|
|
218
|
+
if (parentItem) {
|
|
219
|
+
parentItem.setFocused();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
case "ArrowRight": {
|
|
225
|
+
e.preventDefault();
|
|
226
|
+
let currentItem = findItem(index);
|
|
227
|
+
if (currentItem) {
|
|
228
|
+
if (currentItem.expanded) {
|
|
229
|
+
let firstChild = getFirstChildItem(index);
|
|
230
|
+
if (firstChild) {
|
|
231
|
+
firstChild.setFocused();
|
|
232
|
+
}
|
|
233
|
+
} else if (currentItem.hasChildren) {
|
|
234
|
+
expandedGroup.toggleItem(currentItem.value);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
case "Home": {
|
|
240
|
+
e.preventDefault();
|
|
241
|
+
focusFirstItem();
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
case "End": {
|
|
245
|
+
e.preventDefault();
|
|
246
|
+
focusLastItem();
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
case "Enter":
|
|
250
|
+
case " ": {
|
|
251
|
+
e.preventDefault();
|
|
252
|
+
let currentItem = findItem(index);
|
|
253
|
+
if (currentItem) {
|
|
254
|
+
selectGroup.toggleItem(currentItem.value);
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function focusFirstItem() {
|
|
262
|
+
for (let item of itemStates) {
|
|
263
|
+
if (!isItemVisible(item, itemStates)) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
item.setFocused();
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function focusLastItem() {
|
|
272
|
+
for (let i = itemStates.length - 1; i >= 0; i--) {
|
|
273
|
+
let item = itemStates[i];
|
|
274
|
+
if (!isItemVisible(item, itemStates)) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
item.setFocused();
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function findItem(index: number): ItemState | undefined {
|
|
283
|
+
return itemStates.find((item) => item.index === index);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function getNextVisibleItem(index: number): ItemState | undefined {
|
|
287
|
+
let currentItem = findItem(index);
|
|
288
|
+
if (!currentItem) {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (currentItem.expanded && currentItem.hasChildren) {
|
|
293
|
+
let nextIndex = index + 1;
|
|
294
|
+
if (nextIndex < itemStates.length) {
|
|
295
|
+
return itemStates[nextIndex];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
for (let i = index + 1; i < itemStates.length; i++) {
|
|
300
|
+
let item = itemStates[i];
|
|
301
|
+
if (isItemVisible(item, itemStates) && !item.disabled) {
|
|
302
|
+
return item;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function getPreviousVisibleItem(index: number): ItemState | undefined {
|
|
310
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
311
|
+
let item = itemStates[i];
|
|
312
|
+
if (isItemVisible(item, itemStates) && !item.disabled) {
|
|
313
|
+
return item;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function getParentItem(index: number): ItemState | undefined {
|
|
320
|
+
let currentItem = findItem(index);
|
|
321
|
+
if (!currentItem) {
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
326
|
+
let item = itemStates[i];
|
|
327
|
+
if (item.expanded && item.hasChildren && i < index) {
|
|
328
|
+
let nextIndex = i + 1;
|
|
329
|
+
while (nextIndex < itemStates.length) {
|
|
330
|
+
let sibling = itemStates[nextIndex];
|
|
331
|
+
if (sibling.level <= item.level) {
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
if (nextIndex === index) {
|
|
335
|
+
return item;
|
|
336
|
+
}
|
|
337
|
+
nextIndex++;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function getFirstChildItem(index: number): ItemState | undefined {
|
|
346
|
+
let nextIndex = index + 1;
|
|
347
|
+
if (nextIndex < itemStates.length) {
|
|
348
|
+
let child = itemStates[nextIndex];
|
|
349
|
+
if (child.level > itemStates[index].level) {
|
|
350
|
+
return child;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return undefined;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function isItemVisible(item: ItemState, allItems: ItemState[]): boolean {
|
|
357
|
+
if (item.level === 0) {
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
for (let i = item.index - 1; i >= 0; i--) {
|
|
362
|
+
let parentItem = allItems[i];
|
|
363
|
+
if (parentItem.level < item.level) {
|
|
364
|
+
return parentItem.expanded;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
@render {
|
|
372
|
+
<div
|
|
373
|
+
id={$props?.id ?? defaultId}
|
|
374
|
+
class={["torp-tree", $props?.class]}
|
|
375
|
+
{$props?.style}
|
|
376
|
+
role="tree"
|
|
377
|
+
aria-label={$props?.ariaLabel}
|
|
378
|
+
aria-multiselectable={getType() === "multiple" ? "true" : undefined}
|
|
379
|
+
data-valid={form.valid ? "valid" : "invalid"}
|
|
380
|
+
aria-describedby={form.messageId}
|
|
381
|
+
aria-invalid={form.inField && !form.valid ? "true" : undefined}
|
|
382
|
+
onfocusout={(e: FocusEvent) => {
|
|
383
|
+
// Only validate when focus leaves the whole tree
|
|
384
|
+
if (!root.contains(e.relatedTarget as Node | null)) {
|
|
385
|
+
form.handleBlur();
|
|
386
|
+
}
|
|
387
|
+
}}
|
|
388
|
+
&ref={root}
|
|
389
|
+
>
|
|
390
|
+
<slot />
|
|
391
|
+
</div>
|
|
392
|
+
|
|
393
|
+
<FormValues name={form.name} value={$state.value} />
|
|
394
|
+
}
|
|
395
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import type { Loader } from "../utils/loader";
|
|
3
|
+
import getId from "../utils/getId";
|
|
4
|
+
import {
|
|
5
|
+
type ItemState,
|
|
6
|
+
type TreeContext,
|
|
7
|
+
TreeContextName,
|
|
8
|
+
type TreeItemContext,
|
|
9
|
+
TreeItemContextName,
|
|
10
|
+
type TreeLoadRequest,
|
|
11
|
+
} from "./TreeTypes";
|
|
12
|
+
import TreeLoadedChildren from "./TreeLoadedChildren.torp";
|
|
13
|
+
|
|
14
|
+
interface TreeItemProps {
|
|
15
|
+
/** An ID for the root element */
|
|
16
|
+
id?: string;
|
|
17
|
+
/** Classes for the root element */
|
|
18
|
+
class?: ClassValue;
|
|
19
|
+
/** Styles for the root element */
|
|
20
|
+
style?: StyleValue;
|
|
21
|
+
/** A value for the item */
|
|
22
|
+
value?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Declares that the item has children that are not slotted in. When a
|
|
25
|
+
* `load` function is available (this item's, or the tree's) they are
|
|
26
|
+
* loaded from the network when the item is first expanded.
|
|
27
|
+
*/
|
|
28
|
+
hasChildren?: boolean;
|
|
29
|
+
/** If set to true, the user cannot interact with this TreeItem */
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Reads this item's children from the network (or anywhere async),
|
|
33
|
+
* receiving `{ item }` (the item's value). Set alongside `hasChildren`;
|
|
34
|
+
* overrides the tree's `load` function for this item.
|
|
35
|
+
*/
|
|
36
|
+
load?: Loader<any, TreeLoadRequest>;
|
|
37
|
+
/** Raised when this item is expanded or collapsed */
|
|
38
|
+
onexpand?: (expanded: boolean) => void;
|
|
39
|
+
/** Raised when this item is selected or deselected */
|
|
40
|
+
onselect?: (selected: boolean) => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* An item in a Tree, which can contain nested TreeItems via TreeItemChildren.
|
|
45
|
+
*
|
|
46
|
+
* The structure of a TreeItem is:
|
|
47
|
+
*
|
|
48
|
+
* ```
|
|
49
|
+
* <TreeItem>
|
|
50
|
+
* <TreeItemContent>
|
|
51
|
+
* <TreeItemTrigger />
|
|
52
|
+
* <TreeItemLabel />
|
|
53
|
+
* </TreeItemContent>
|
|
54
|
+
* <TreeItemChildren>
|
|
55
|
+
* <TreeItem />
|
|
56
|
+
* </TreeItemChildren>
|
|
57
|
+
* </TreeItem>
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export default function TreeItem($props: TreeItemProps | undefined) {
|
|
61
|
+
let defaultId = getId();
|
|
62
|
+
|
|
63
|
+
const parentContext = $context[TreeItemContextName] as TreeItemContext;
|
|
64
|
+
const level = parentContext ? parentContext.state.level + 1 : 0;
|
|
65
|
+
|
|
66
|
+
let $state: ItemState = $watch({
|
|
67
|
+
id: $props?.id ?? defaultId,
|
|
68
|
+
index: -1,
|
|
69
|
+
value: $props?.value ?? "",
|
|
70
|
+
expanded: false,
|
|
71
|
+
selected: false,
|
|
72
|
+
disabled: $props?.disabled ?? false,
|
|
73
|
+
parentDisabled: false,
|
|
74
|
+
level,
|
|
75
|
+
hasChildren: false,
|
|
76
|
+
setFocused,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const context = $context[TreeContextName] as TreeContext;
|
|
80
|
+
if (!context) {
|
|
81
|
+
throw new Error("TreeItem must be contained within a Tree");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Whether the item declares children that aren't slotted in and can be
|
|
85
|
+
// loaded when expanded. TreeItemChildren sets `slottedChildren` itself.
|
|
86
|
+
const declaredChildren = $props?.hasChildren === true;
|
|
87
|
+
const slottedLoad = typeof $props?.load === "function" ? $props.load : undefined;
|
|
88
|
+
let slottedChildren = false;
|
|
89
|
+
|
|
90
|
+
function updateHasChildren() {
|
|
91
|
+
if (declaredChildren || slottedChildren) {
|
|
92
|
+
if (!$state.hasChildren) $state.hasChildren = true;
|
|
93
|
+
} else if ($state.hasChildren) {
|
|
94
|
+
$state.hasChildren = false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
updateHasChildren();
|
|
98
|
+
|
|
99
|
+
// The loader for this item's children: the item's own or the tree's
|
|
100
|
+
const itemLoader = slottedLoad ?? (declaredChildren ? context.load : undefined);
|
|
101
|
+
if (declaredChildren && !itemLoader) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
"TreeItem: declaring `hasChildren` requires a `load` function on the item or on the tree",
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
context.registerItem($state, $state.level);
|
|
108
|
+
|
|
109
|
+
$run(() => {
|
|
110
|
+
return () => {
|
|
111
|
+
context.removeItem($state.index);
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
$run(() => {
|
|
116
|
+
const onexpand = $props?.onexpand;
|
|
117
|
+
const expanded = $state.expanded;
|
|
118
|
+
if (onexpand) {
|
|
119
|
+
onexpand(expanded);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
$run(() => {
|
|
124
|
+
const onselect = $props?.onselect;
|
|
125
|
+
const selected = $state.selected;
|
|
126
|
+
if (onselect) {
|
|
127
|
+
onselect(selected);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
let focusLabel: (() => void) | undefined;
|
|
132
|
+
|
|
133
|
+
function setFocused() {
|
|
134
|
+
focusLabel?.();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let itemContext: TreeItemContext = {
|
|
138
|
+
state: $state,
|
|
139
|
+
setHasChildren: (hasChildren) => {
|
|
140
|
+
slottedChildren = hasChildren;
|
|
141
|
+
updateHasChildren();
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
$context[TreeItemContextName] = itemContext;
|
|
145
|
+
|
|
146
|
+
@render {
|
|
147
|
+
<div
|
|
148
|
+
{$state.id}
|
|
149
|
+
class={["torp-tree-item", $props?.class]}
|
|
150
|
+
{$props?.style}
|
|
151
|
+
role="treeitem"
|
|
152
|
+
aria-level={$state.level + 1}
|
|
153
|
+
aria-expanded={$state.hasChildren ? ($state.expanded ? "true" : "false") : undefined}
|
|
154
|
+
aria-selected={$state.selected ? "true" : "false"}
|
|
155
|
+
aria-disabled={$state.disabled || $state.parentDisabled ? "true" : undefined}
|
|
156
|
+
data-selected={$state.selected ? "selected" : undefined}
|
|
157
|
+
>
|
|
158
|
+
@// HACK: May be better to do this with CSS?
|
|
159
|
+
@if ($state.level) {
|
|
160
|
+
<div style="display: flex">
|
|
161
|
+
<div class={["torp-tree-item", $props?.class, "spacer"]} />
|
|
162
|
+
<div>
|
|
163
|
+
<slot />
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
} else {
|
|
167
|
+
<div>
|
|
168
|
+
<slot />
|
|
169
|
+
</div>
|
|
170
|
+
}
|
|
171
|
+
@if (declaredChildren && itemLoader) {
|
|
172
|
+
<TreeLoadedChildren itemState={$state} load={itemLoader} />
|
|
173
|
+
}
|
|
174
|
+
</div>
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type TreeItemContext, TreeItemContextName } from "./TreeTypes";
|
|
2
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
3
|
+
|
|
4
|
+
interface TreeItemChildrenProps {
|
|
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
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A container for nested TreeItems. This component must be a direct child of TreeItem.
|
|
15
|
+
*
|
|
16
|
+
* The structure is:
|
|
17
|
+
*
|
|
18
|
+
* ```
|
|
19
|
+
* <TreeItem>
|
|
20
|
+
* <TreeItemContent>...</TreeItemContent>
|
|
21
|
+
* <TreeItemChildren>
|
|
22
|
+
* <TreeItem />
|
|
23
|
+
* </TreeItemChildren>
|
|
24
|
+
* </TreeItem>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export default function TreeItemChildren($props?: TreeItemChildrenProps) {
|
|
28
|
+
const itemContext = $context[TreeItemContextName] as TreeItemContext;
|
|
29
|
+
|
|
30
|
+
if (!itemContext) {
|
|
31
|
+
throw new Error("TreeItemChildren must be contained within a TreeItem");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { setHasChildren, state: $state } = itemContext;
|
|
35
|
+
|
|
36
|
+
$run(() => {
|
|
37
|
+
setHasChildren(true);
|
|
38
|
+
return () => {
|
|
39
|
+
setHasChildren(false);
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// HACK: We need to keep the items in the document so that we can search through them
|
|
44
|
+
@render {
|
|
45
|
+
<div
|
|
46
|
+
{$props?.id}
|
|
47
|
+
class={["torp-tree-item-children", $props?.class, $state.expanded ? undefined : "hidden"]}
|
|
48
|
+
{$props?.style}
|
|
49
|
+
role="group"
|
|
50
|
+
>
|
|
51
|
+
<slot />
|
|
52
|
+
</div>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@style {
|
|
56
|
+
.torp-tree-item-children.hidden {
|
|
57
|
+
display: none;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import { type TreeItemContext, TreeItemContextName } from "./TreeTypes";
|
|
3
|
+
|
|
4
|
+
interface TreeItemContentProps {
|
|
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
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A wrapper for the content of a TreeItem, which should contain TreeItemTrigger and TreeItemLabel.
|
|
15
|
+
*
|
|
16
|
+
* The structure is:
|
|
17
|
+
*
|
|
18
|
+
* ```
|
|
19
|
+
* <TreeItem>
|
|
20
|
+
* <TreeItemContent>
|
|
21
|
+
* <TreeItemTrigger />
|
|
22
|
+
* <TreeItemLabel />
|
|
23
|
+
* </TreeItemContent>
|
|
24
|
+
* </TreeItem>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export default function TreeItemContent($props: TreeItemContentProps | undefined) {
|
|
28
|
+
const itemContext = $context[TreeItemContextName] as TreeItemContext;
|
|
29
|
+
|
|
30
|
+
if (!itemContext) {
|
|
31
|
+
throw new Error("TreeItemLabel must be contained within a TreeItem");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { state: $state } = itemContext;
|
|
35
|
+
|
|
36
|
+
@render {
|
|
37
|
+
<div
|
|
38
|
+
{$props?.id}
|
|
39
|
+
class={["torp-tree-item-content", $props?.class]}
|
|
40
|
+
{$props?.style}
|
|
41
|
+
data-selected={$state.selected ? "selected" : undefined}
|
|
42
|
+
>
|
|
43
|
+
<slot />
|
|
44
|
+
</div>
|
|
45
|
+
}
|
|
46
|
+
}
|