@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,248 @@
|
|
|
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 FormValues from "../Form/FormValues.torp";
|
|
10
|
+
import createFormField from "../utils/formField";
|
|
11
|
+
|
|
12
|
+
interface SliderProps {
|
|
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
|
+
/** The minimum allowed value */
|
|
20
|
+
min?: number;
|
|
21
|
+
/** The maximum allowed value */
|
|
22
|
+
max?: number;
|
|
23
|
+
/** The amount by which the value is increased or decreased */
|
|
24
|
+
step?: number;
|
|
25
|
+
/** The current value of the slider */
|
|
26
|
+
value?: Bindable<number>;
|
|
27
|
+
/** The orientation of the slider */
|
|
28
|
+
orientation?: "horizontal" | "vertical";
|
|
29
|
+
/** If set to true, the user cannot interact with this slider */
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
/** An accessible name for the slider */
|
|
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: number) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* An input where the user selects a value from within a given range, by
|
|
41
|
+
* dragging a thumb along a track or by using the keyboard.
|
|
42
|
+
*
|
|
43
|
+
* See [the WAI ARIA guidelines for Sliders](https://www.w3.org/WAI/ARIA/apg/patterns/slider/).
|
|
44
|
+
*/
|
|
45
|
+
export default function Slider($props: SliderProps | undefined) {
|
|
46
|
+
let track: HTMLElement;
|
|
47
|
+
let thumb: HTMLElement;
|
|
48
|
+
|
|
49
|
+
// Wire up the enclosing Form and Field, if any
|
|
50
|
+
const form = createFormField(
|
|
51
|
+
$props?.name,
|
|
52
|
+
$context[FormContextName] as FormContext | undefined,
|
|
53
|
+
$context[FieldContextName] as FieldContext | undefined,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
function getMin(): number {
|
|
57
|
+
return $props?.min ?? 0;
|
|
58
|
+
}
|
|
59
|
+
function getMax(): number {
|
|
60
|
+
return $props?.max ?? 100;
|
|
61
|
+
}
|
|
62
|
+
function getStep(): number {
|
|
63
|
+
const step = $props?.step ?? 1;
|
|
64
|
+
return step > 0 ? step : 1;
|
|
65
|
+
}
|
|
66
|
+
function getOrientation(): "horizontal" | "vertical" {
|
|
67
|
+
return $props?.orientation === "vertical" ? "vertical" : "horizontal";
|
|
68
|
+
}
|
|
69
|
+
function getDisabled(): boolean {
|
|
70
|
+
return $props?.disabled === true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let $state = $watch({
|
|
74
|
+
value: 0,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
$bind($state, $props, "value");
|
|
78
|
+
|
|
79
|
+
// Dispatch the change event when the value changes
|
|
80
|
+
$handle((first) => {
|
|
81
|
+
const value = $state.value;
|
|
82
|
+
if (!first) $props?.onchange?.(value);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
function clamp(value: number): number {
|
|
86
|
+
return Math.min(getMax(), Math.max(getMin(), value));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Snap a raw position-derived value to the nearest step multiple
|
|
90
|
+
function snap(value: number): number {
|
|
91
|
+
const min = getMin();
|
|
92
|
+
const snapped = min + Math.round((value - min) / getStep()) * getStep();
|
|
93
|
+
return snapped === -0 ? 0 : snapped;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// The clamped value that is displayed and reported to assistive tech
|
|
97
|
+
function getValue(): number {
|
|
98
|
+
return clamp($state.value);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// The percentage of the range that the value represents (0..100)
|
|
102
|
+
function getPct(): number {
|
|
103
|
+
const min = getMin();
|
|
104
|
+
const max = getMax();
|
|
105
|
+
if (max <= min) return 0;
|
|
106
|
+
return ((getValue() - min) / (max - min)) * 100;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function setValue(value: number) {
|
|
110
|
+
const next = clamp(snap(value));
|
|
111
|
+
if (next !== $state.value) {
|
|
112
|
+
$state.value = next;
|
|
113
|
+
form.handleInput();
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Convert a pointer position into a value along the track
|
|
120
|
+
function valueFromPointer(e: { clientX: number; clientY: number }): number {
|
|
121
|
+
const rect = track.getBoundingClientRect();
|
|
122
|
+
const horizontal = getOrientation() === "horizontal";
|
|
123
|
+
const pos = horizontal ? e.clientX - rect.left : e.clientY - rect.top;
|
|
124
|
+
const size = horizontal ? rect.width : rect.height;
|
|
125
|
+
if (!(size > 0)) {
|
|
126
|
+
return getValue();
|
|
127
|
+
}
|
|
128
|
+
const ratio = Math.min(1, Math.max(0, pos / size));
|
|
129
|
+
return getMin() + ratio * (getMax() - getMin());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let dragging = false;
|
|
133
|
+
|
|
134
|
+
function removeDragListeners() {
|
|
135
|
+
removeEventListener("mousemove", handleMouseMove);
|
|
136
|
+
removeEventListener("mouseup", handleMouseUp);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function handleMouseDown(e: MouseEvent) {
|
|
140
|
+
if (getDisabled()) return;
|
|
141
|
+
e.preventDefault();
|
|
142
|
+
dragging = true;
|
|
143
|
+
setValue(valueFromPointer(e));
|
|
144
|
+
thumb.focus();
|
|
145
|
+
addEventListener("mousemove", handleMouseMove);
|
|
146
|
+
addEventListener("mouseup", handleMouseUp);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function handleMouseMove(e: MouseEvent) {
|
|
150
|
+
if (!dragging || getDisabled()) return;
|
|
151
|
+
setValue(valueFromPointer(e));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function handleMouseUp() {
|
|
155
|
+
if (!dragging) return;
|
|
156
|
+
dragging = false;
|
|
157
|
+
removeDragListeners();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Stop listening if the slider is unmounted mid-drag
|
|
161
|
+
$onmount(() => {
|
|
162
|
+
return () => removeDragListeners();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
function handleKey(e: KeyboardEvent) {
|
|
166
|
+
if (getDisabled()) return;
|
|
167
|
+
|
|
168
|
+
switch (e.key) {
|
|
169
|
+
case "ArrowRight":
|
|
170
|
+
case "ArrowUp": {
|
|
171
|
+
e.preventDefault();
|
|
172
|
+
setValue($state.value + getStep());
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
case "ArrowLeft":
|
|
176
|
+
case "ArrowDown": {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
setValue($state.value - getStep());
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
case "PageUp": {
|
|
182
|
+
e.preventDefault();
|
|
183
|
+
setValue($state.value + getStep() * 10);
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
case "PageDown": {
|
|
187
|
+
e.preventDefault();
|
|
188
|
+
setValue($state.value - getStep() * 10);
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case "Home": {
|
|
192
|
+
e.preventDefault();
|
|
193
|
+
setValue(getMin());
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
case "End": {
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
setValue(getMax());
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@render {
|
|
205
|
+
<div
|
|
206
|
+
{$props?.id}
|
|
207
|
+
class={["torp-slider", getDisabled() ? "torp-slider-disabled" : undefined, $props?.class]}
|
|
208
|
+
{$props?.style}
|
|
209
|
+
onmousedown={handleMouseDown}
|
|
210
|
+
&ref={track}
|
|
211
|
+
>
|
|
212
|
+
<div class="torp-slider-fill" style={`${getOrientation() === "vertical" ? "height" : "width"}: ${getPct()}%;`} />
|
|
213
|
+
<div
|
|
214
|
+
class="torp-slider-thumb"
|
|
215
|
+
style={`${getOrientation() === "vertical" ? "top" : "left"}: ${getPct()}%;`}
|
|
216
|
+
role="slider"
|
|
217
|
+
tabindex={getDisabled() ? -1 : 0}
|
|
218
|
+
aria-orientation={getOrientation()}
|
|
219
|
+
aria-valuemin={getMin()}
|
|
220
|
+
aria-valuemax={getMax()}
|
|
221
|
+
aria-valuenow={getValue()}
|
|
222
|
+
aria-label={$props?.ariaLabel}
|
|
223
|
+
aria-disabled={getDisabled() ? "true" : undefined}
|
|
224
|
+
data-valid={form.valid ? "valid" : "invalid"}
|
|
225
|
+
aria-describedby={form.messageId}
|
|
226
|
+
aria-invalid={form.inField && !form.valid ? "true" : undefined}
|
|
227
|
+
onblur={form.handleBlur}
|
|
228
|
+
onkeydown={handleKey}
|
|
229
|
+
&ref={thumb}
|
|
230
|
+
/>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<FormValues name={form.name} value={getValue()} />
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
@style {
|
|
237
|
+
/* Headless: positioning only, so the thumb sits at the value along
|
|
238
|
+
the track. All appearance is up to the consuming theme */
|
|
239
|
+
.torp-slider {
|
|
240
|
+
position: relative;
|
|
241
|
+
touch-action: none;
|
|
242
|
+
}
|
|
243
|
+
.torp-slider-thumb {
|
|
244
|
+
position: absolute;
|
|
245
|
+
transform: translateX(-50%);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type SlotRender } from "@torpor/view";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
interface SliderProps {
|
|
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 minimum allowed value */
|
|
12
|
+
min?: number;
|
|
13
|
+
/** The maximum allowed value */
|
|
14
|
+
max?: number;
|
|
15
|
+
/** The amount by which the value is increased or decreased */
|
|
16
|
+
step?: number;
|
|
17
|
+
/** The current value of the slider */
|
|
18
|
+
value?: Bindable<number>;
|
|
19
|
+
/** The orientation of the slider */
|
|
20
|
+
orientation?: "horizontal" | "vertical";
|
|
21
|
+
/** If set to true, the user cannot interact with this slider */
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
/** An accessible name for the slider */
|
|
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: number) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* An input where the user selects a value from within a given range, by
|
|
33
|
+
* dragging a thumb along a track or by using the keyboard.
|
|
34
|
+
*
|
|
35
|
+
* See [the WAI ARIA guidelines for Sliders](https://www.w3.org/WAI/ARIA/apg/patterns/slider/).
|
|
36
|
+
*/
|
|
37
|
+
declare function Slider(
|
|
38
|
+
$parent: ParentNode,
|
|
39
|
+
$anchor: Node | null,
|
|
40
|
+
$props: SliderProps | undefined,
|
|
41
|
+
$context?: Record<PropertyKey, any>,
|
|
42
|
+
$slots?: Record<string, SlotRender>,
|
|
43
|
+
): void;
|
|
44
|
+
|
|
45
|
+
export { Slider };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { Bindable, ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import $handle from "../utils/$handle";
|
|
3
|
+
import { type SplitterContext, SplitterContextName } from "./SplitterTypes";
|
|
4
|
+
|
|
5
|
+
interface SplitterProps {
|
|
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
|
+
/** The orientation of the splitter (default horizontal) */
|
|
13
|
+
orientation?: "horizontal" | "vertical";
|
|
14
|
+
/** The percentage of the container taken up by the primary (first) pane */
|
|
15
|
+
value?: Bindable<number>;
|
|
16
|
+
/** The minimum percentage for the primary pane (default 0) */
|
|
17
|
+
min?: number;
|
|
18
|
+
/** The maximum percentage for the primary pane (default 100) */
|
|
19
|
+
max?: number;
|
|
20
|
+
/** The percentage by which keyboard interaction changes the value (default 1) */
|
|
21
|
+
step?: number;
|
|
22
|
+
/** If set to true, the user cannot interact with this splitter */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
/** Raised when the value has changed */
|
|
25
|
+
onchange?: (value: number) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Two resizable panes with a separator between them, which the user can drag
|
|
30
|
+
* or move with the keyboard to resize the panes.
|
|
31
|
+
*
|
|
32
|
+
* See [the WAI ARIA guidelines for Window Splitters](https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/).
|
|
33
|
+
*
|
|
34
|
+
* The structure of a Splitter is:
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* <Splitter>
|
|
38
|
+
* <SplitterPane>...</SplitterPane>
|
|
39
|
+
* <SplitterHandle ariaLabel="Resize" />
|
|
40
|
+
* <SplitterPane>...</SplitterPane>
|
|
41
|
+
* </Splitter>
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export default function Splitter($props: SplitterProps | undefined) {
|
|
45
|
+
let container: HTMLElement;
|
|
46
|
+
|
|
47
|
+
let $state = $watch({
|
|
48
|
+
value: 50,
|
|
49
|
+
panes: [] as SplitterPaneStateLite[],
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
interface SplitterPaneStateLite {
|
|
53
|
+
index: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
$bind($state, $props, "value");
|
|
57
|
+
|
|
58
|
+
// Keep the stored value clamped, so that out-of-range initial values or
|
|
59
|
+
// min/max changes don't later surface as user changes
|
|
60
|
+
$run(() => {
|
|
61
|
+
const clamped = getValue();
|
|
62
|
+
if (clamped !== $state.value) {
|
|
63
|
+
$state.value = clamped;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Set the context to pass down to panes and handles
|
|
68
|
+
const context: SplitterContext = {
|
|
69
|
+
registerPane,
|
|
70
|
+
removePane,
|
|
71
|
+
getOrientation,
|
|
72
|
+
getValue,
|
|
73
|
+
getMin,
|
|
74
|
+
getMax,
|
|
75
|
+
getStep,
|
|
76
|
+
getDisabled,
|
|
77
|
+
setValue,
|
|
78
|
+
count: () => $state.panes.length,
|
|
79
|
+
getContainer: () => container,
|
|
80
|
+
};
|
|
81
|
+
$context[SplitterContextName] = context;
|
|
82
|
+
|
|
83
|
+
// Dispatch the change event when the value changes
|
|
84
|
+
$handle((first) => {
|
|
85
|
+
const value = getValue();
|
|
86
|
+
if (!first) $props?.onchange?.(value);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
function getOrientation(): "horizontal" | "vertical" {
|
|
90
|
+
return $props?.orientation === "vertical" ? "vertical" : "horizontal";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getMin(): number {
|
|
94
|
+
return clampPercent($props?.min ?? 0);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getMax(): number {
|
|
98
|
+
return clampPercent($props?.max ?? 100);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function clampPercent(value: number): number {
|
|
102
|
+
return Math.min(100, Math.max(0, value));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getStep(): number {
|
|
106
|
+
const step = $props?.step ?? 1;
|
|
107
|
+
return step > 0 ? step : 1;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getDisabled(): boolean {
|
|
111
|
+
return $props?.disabled === true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// The clamped value that is applied to the primary pane
|
|
115
|
+
function getValue(): number {
|
|
116
|
+
return Math.min(getMax(), Math.max(getMin(), $state.value));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function setValue(value: number) {
|
|
120
|
+
$state.value = Math.min(getMax(), Math.max(getMin(), value));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function registerPane(): SplitterPaneStateLite {
|
|
124
|
+
const state: SplitterPaneStateLite = $watch({ index: $state.panes.length });
|
|
125
|
+
$state.panes.push(state);
|
|
126
|
+
return state;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function removePane(index: number) {
|
|
130
|
+
if (index >= 0 && index < $state.panes.length) {
|
|
131
|
+
$state.panes.splice(index, 1);
|
|
132
|
+
}
|
|
133
|
+
$state.panes.forEach((pane, i) => (pane.index = i));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@render {
|
|
137
|
+
<div
|
|
138
|
+
{$props?.id}
|
|
139
|
+
class={["torp-splitter", $props?.class]}
|
|
140
|
+
{$props?.style}
|
|
141
|
+
data-orientation={getOrientation()}
|
|
142
|
+
style={`flex-direction: ${getOrientation()};`}
|
|
143
|
+
&ref={container}
|
|
144
|
+
>
|
|
145
|
+
<slot />
|
|
146
|
+
</div>
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@style {
|
|
150
|
+
/* Headless: layout only, so the panes split the container. All other
|
|
151
|
+
appearance is up to the consuming theme */
|
|
152
|
+
.torp-splitter {
|
|
153
|
+
display: flex;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import { type SplitterContext, SplitterContextName } from "./SplitterTypes";
|
|
3
|
+
|
|
4
|
+
interface SplitterHandleProps {
|
|
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
|
+
/** An accessible name for the separator */
|
|
12
|
+
ariaLabel?: string;
|
|
13
|
+
/** If set to true, the user cannot interact with this handle */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The separator in a Splitter that a user can drag, or move with the
|
|
19
|
+
* keyboard, to resize the panes on either side of it.
|
|
20
|
+
*
|
|
21
|
+
* See [the WAI ARIA guidelines for Window Splitters](https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/).
|
|
22
|
+
*/
|
|
23
|
+
export default function SplitterHandle($props: SplitterHandleProps | undefined) {
|
|
24
|
+
// Get stuff out of the Splitter context
|
|
25
|
+
const context = $context[SplitterContextName] as SplitterContext;
|
|
26
|
+
if (!context) {
|
|
27
|
+
throw new Error("SplitterHandle must be contained within a Splitter");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getDisabled(): boolean {
|
|
31
|
+
return $props?.disabled === true || context.getDisabled();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let dragging = false;
|
|
35
|
+
|
|
36
|
+
function removeDragListeners() {
|
|
37
|
+
removeEventListener("mousemove", handleMouseMove);
|
|
38
|
+
removeEventListener("mouseup", handleMouseUp);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Convert a pointer position into a percentage of the container
|
|
42
|
+
function valueFromPointer(e: { clientX: number; clientY: number }): number {
|
|
43
|
+
const container = context.getContainer();
|
|
44
|
+
if (!container) return context.getValue();
|
|
45
|
+
const rect = container.getBoundingClientRect();
|
|
46
|
+
const horizontal = context.getOrientation() === "horizontal";
|
|
47
|
+
const pos = horizontal ? e.clientX - rect.left : e.clientY - rect.top;
|
|
48
|
+
const size = horizontal ? rect.width : rect.height;
|
|
49
|
+
if (!(size > 0)) return context.getValue();
|
|
50
|
+
const ratio = Math.min(1, Math.max(0, pos / size));
|
|
51
|
+
return ratio * 100;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function handleMouseDown(e: MouseEvent) {
|
|
55
|
+
if (getDisabled()) return;
|
|
56
|
+
e.preventDefault();
|
|
57
|
+
dragging = true;
|
|
58
|
+
context.setValue(valueFromPointer(e));
|
|
59
|
+
addEventListener("mousemove", handleMouseMove);
|
|
60
|
+
addEventListener("mouseup", handleMouseUp);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function handleMouseMove(e: MouseEvent) {
|
|
64
|
+
if (!dragging || getDisabled()) return;
|
|
65
|
+
context.setValue(valueFromPointer(e));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function handleMouseUp() {
|
|
69
|
+
if (!dragging) return;
|
|
70
|
+
dragging = false;
|
|
71
|
+
removeDragListeners();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Stop listening if the handle is unmounted mid-drag
|
|
75
|
+
$onmount(() => {
|
|
76
|
+
return () => removeDragListeners();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
function handleKey(e: KeyboardEvent) {
|
|
80
|
+
if (getDisabled()) return;
|
|
81
|
+
const step = context.getStep();
|
|
82
|
+
const vertical = context.getOrientation() === "vertical";
|
|
83
|
+
|
|
84
|
+
switch (e.key) {
|
|
85
|
+
case "ArrowRight": {
|
|
86
|
+
if (!vertical) {
|
|
87
|
+
e.preventDefault();
|
|
88
|
+
context.setValue(context.getValue() + step);
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case "ArrowLeft": {
|
|
93
|
+
if (!vertical) {
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
context.setValue(context.getValue() - step);
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "ArrowUp": {
|
|
100
|
+
if (vertical) {
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
context.setValue(context.getValue() + step);
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
case "ArrowDown": {
|
|
107
|
+
if (vertical) {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
context.setValue(context.getValue() - step);
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case "PageUp": {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
context.setValue(context.getValue() + step * 10);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
case "PageDown": {
|
|
119
|
+
e.preventDefault();
|
|
120
|
+
context.setValue(context.getValue() - step * 10);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case "Home": {
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
context.setValue(context.getMin());
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case "End": {
|
|
129
|
+
e.preventDefault();
|
|
130
|
+
context.setValue(context.getMax());
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@render {
|
|
137
|
+
<div
|
|
138
|
+
{$props?.id}
|
|
139
|
+
class={["torp-splitter-handle", $props?.class]}
|
|
140
|
+
{$props?.style}
|
|
141
|
+
role="separator"
|
|
142
|
+
tabindex={getDisabled() ? -1 : 0}
|
|
143
|
+
aria-label={$props?.ariaLabel ?? "Resize"}
|
|
144
|
+
aria-orientation={context.getOrientation()}
|
|
145
|
+
aria-valuemin={Math.round(context.getMin())}
|
|
146
|
+
aria-valuemax={Math.round(context.getMax())}
|
|
147
|
+
aria-valuenow={Math.round(context.getValue())}
|
|
148
|
+
aria-disabled={getDisabled() ? "true" : undefined}
|
|
149
|
+
onmousedown={handleMouseDown}
|
|
150
|
+
onkeydown={handleKey}
|
|
151
|
+
/>
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@style {
|
|
155
|
+
/* Headless: behavior only, so the handle can be dragged on touch
|
|
156
|
+
devices. All appearance is up to the consuming theme */
|
|
157
|
+
.torp-splitter-handle {
|
|
158
|
+
touch-action: none;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import { type SplitterContext, SplitterContextName } from "./SplitterTypes";
|
|
3
|
+
|
|
4
|
+
interface SplitterPaneProps {
|
|
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 pane in a Splitter. When there is more than one pane, the first pane is
|
|
15
|
+
* sized to the splitter's value and the others share what's left.
|
|
16
|
+
*/
|
|
17
|
+
export default function SplitterPane($props: SplitterPaneProps | undefined) {
|
|
18
|
+
// Get stuff out of the Splitter context
|
|
19
|
+
const context = $context[SplitterContextName] as SplitterContext;
|
|
20
|
+
if (!context) {
|
|
21
|
+
throw new Error("SplitterPane must be contained within a Splitter");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Register this pane with the parent Splitter
|
|
25
|
+
let $state = context.registerPane();
|
|
26
|
+
|
|
27
|
+
$run(() => {
|
|
28
|
+
return () => {
|
|
29
|
+
context.removePane($state.index);
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// The primary (first) pane is sized to the value when there are other
|
|
34
|
+
// panes to share the rest of the space with
|
|
35
|
+
function getFlex(): string {
|
|
36
|
+
if ($state.index === 0 && context.count() > 1) {
|
|
37
|
+
return `0 0 ${context.getValue()}%;`;
|
|
38
|
+
}
|
|
39
|
+
return "1 1 0;";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@render {
|
|
43
|
+
<div
|
|
44
|
+
{$props?.id}
|
|
45
|
+
class={["torp-splitter-pane", $props?.class]}
|
|
46
|
+
{$props?.style}
|
|
47
|
+
data-orientation={context.getOrientation()}
|
|
48
|
+
style={`flex: ${getFlex()}`}
|
|
49
|
+
>
|
|
50
|
+
<slot />
|
|
51
|
+
</div>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@style {
|
|
55
|
+
/* Headless: min sizes only, so the panes can shrink below their
|
|
56
|
+
content. All other appearance is up to the consuming theme */
|
|
57
|
+
.torp-splitter-pane {
|
|
58
|
+
min-height: 0;
|
|
59
|
+
min-width: 0;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const SplitterContextName: unique symbol = Symbol.for("torp.Splitter");
|
|
2
|
+
|
|
3
|
+
export interface SplitterPaneState {
|
|
4
|
+
/** The index of the pane */
|
|
5
|
+
index: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface SplitterContext {
|
|
9
|
+
/** Called from a pane when it is added */
|
|
10
|
+
registerPane: () => SplitterPaneState;
|
|
11
|
+
/** Called from a pane when it is removed */
|
|
12
|
+
removePane: (index: number) => void;
|
|
13
|
+
/** The orientation of the splitter */
|
|
14
|
+
getOrientation: () => "horizontal" | "vertical";
|
|
15
|
+
/** The clamped percentage of the container taken up by the primary pane */
|
|
16
|
+
getValue: () => number;
|
|
17
|
+
/** The minimum percentage for the primary pane */
|
|
18
|
+
getMin: () => number;
|
|
19
|
+
/** The maximum percentage for the primary pane */
|
|
20
|
+
getMax: () => number;
|
|
21
|
+
/** The amount by which keyboard interaction changes the value */
|
|
22
|
+
getStep: () => number;
|
|
23
|
+
/** Whether the user cannot interact with the splitter */
|
|
24
|
+
getDisabled: () => boolean;
|
|
25
|
+
/** Sets the value, clamped to the min and max */
|
|
26
|
+
setValue: (value: number) => void;
|
|
27
|
+
/** The number of registered panes */
|
|
28
|
+
count: () => number;
|
|
29
|
+
/** The splitter's root element, used to convert pointer positions into values */
|
|
30
|
+
getContainer: () => HTMLElement | undefined;
|
|
31
|
+
}
|