@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,120 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import { chartColors } from "../utils/chartColors";
|
|
3
|
+
import { createChartLayout, measureChartText, type ChartLayout } from "../utils/chartLayout";
|
|
4
|
+
import type DataSeries from "./DataSeries";
|
|
5
|
+
import { type ChartContext, ChartContextName } from "./ChartTypes";
|
|
6
|
+
|
|
7
|
+
interface ChartProps {
|
|
8
|
+
/** An ID for the root element */
|
|
9
|
+
id?: string;
|
|
10
|
+
/** Classes for the root element */
|
|
11
|
+
class?: ClassValue;
|
|
12
|
+
/** Styles for the root element */
|
|
13
|
+
style?: StyleValue;
|
|
14
|
+
/** An ARIA label describing the chart for screen readers */
|
|
15
|
+
ariaLabel?: string;
|
|
16
|
+
/** The series that layers draw from (layers reference them by index) */
|
|
17
|
+
series: DataSeries[];
|
|
18
|
+
/** Fixed height in pixels (default 200) */
|
|
19
|
+
height?: number;
|
|
20
|
+
/** Fixed width in pixels (default: fills the container, minimum 400) */
|
|
21
|
+
width?: number;
|
|
22
|
+
/** Label under the x axis */
|
|
23
|
+
xLabel?: string;
|
|
24
|
+
/** Label beside the y axis */
|
|
25
|
+
yLabel?: string;
|
|
26
|
+
/** The value at the top of the value axis, instead of the series' maximum */
|
|
27
|
+
maxValue?: number;
|
|
28
|
+
/** The number of steps between 0 and the maximum value (default 2) */
|
|
29
|
+
stepCount?: number;
|
|
30
|
+
/** The size of each step, instead of being calculated */
|
|
31
|
+
stepValue?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A container for layered chart components. It measures its text, works out
|
|
36
|
+
* the plot geometry and provides it to whatever axes, grid lines and marks
|
|
37
|
+
* are slotted inside:
|
|
38
|
+
*
|
|
39
|
+
* ```
|
|
40
|
+
* <Chart ariaLabel="Revenue" series={revenue}>
|
|
41
|
+
* <ChartYAxis side="right" />
|
|
42
|
+
* <ChartGrid />
|
|
43
|
+
* <ChartXAxis />
|
|
44
|
+
* <ChartColumns />
|
|
45
|
+
* <ChartLines seriesIndex={1} showPoints />
|
|
46
|
+
* </Chart>
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* Layers can be mixed freely -- columns with a line overlaid, an axis on
|
|
50
|
+
* either side, only a y axis with no x axis -- so a preset like LineChart is
|
|
51
|
+
* just one such combination.
|
|
52
|
+
*
|
|
53
|
+
* See also the ready-made presets: BarChart, ColumnChart, LineChart,
|
|
54
|
+
* ScatterChart.
|
|
55
|
+
*/
|
|
56
|
+
export default function Chart($props: ChartProps | undefined) {
|
|
57
|
+
let container: HTMLElement;
|
|
58
|
+
let measurer: SVGTextElement;
|
|
59
|
+
|
|
60
|
+
const $layout = createChartLayout({
|
|
61
|
+
series: () => $props?.series ?? [],
|
|
62
|
+
height: () => $props?.height,
|
|
63
|
+
width: () => $props?.width,
|
|
64
|
+
xLabel: () => $props?.xLabel,
|
|
65
|
+
yLabel: () => $props?.yLabel,
|
|
66
|
+
maxValue: () => $props?.maxValue,
|
|
67
|
+
stepCount: () => $props?.stepCount,
|
|
68
|
+
stepValue: () => $props?.stepValue,
|
|
69
|
+
getContainer: () => container,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
$onmount(() => {
|
|
73
|
+
// HACK: Wait until the measurer is really available
|
|
74
|
+
setTimeout(() => measureChartText($layout, measurer), 1);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
function colorAt(i: number): string {
|
|
78
|
+
const series = $props?.series[i];
|
|
79
|
+
return series?.color || chartColors[i % chartColors.length];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let $chartState = $watch({
|
|
83
|
+
horizontal: false,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const context: ChartContext = {
|
|
87
|
+
layout: $layout as unknown as ChartLayout,
|
|
88
|
+
series: () => $props?.series ?? [],
|
|
89
|
+
xLabel: () => $props?.xLabel ?? "",
|
|
90
|
+
yLabel: () => $props?.yLabel ?? "",
|
|
91
|
+
horizontal: () => $chartState.horizontal,
|
|
92
|
+
registerMark: (orientation) => {
|
|
93
|
+
if (orientation === "horizontal") {
|
|
94
|
+
$chartState.horizontal = true;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
colorAt,
|
|
98
|
+
};
|
|
99
|
+
$context[ChartContextName] = context;
|
|
100
|
+
|
|
101
|
+
@render {
|
|
102
|
+
<div {$props?.id} class={["torp-chart", $props?.class]} {$props?.style} &ref={container}>
|
|
103
|
+
<svg
|
|
104
|
+
role={$props?.ariaLabel ? "img" : undefined}
|
|
105
|
+
aria-label={$props?.ariaLabel}
|
|
106
|
+
version="1.1"
|
|
107
|
+
width={$layout.calculatedWidth}
|
|
108
|
+
height={$layout.calculatedHeight}
|
|
109
|
+
>
|
|
110
|
+
<g>
|
|
111
|
+
<text &ref={measurer} x="-999" y="-999">8</text>
|
|
112
|
+
|
|
113
|
+
@if ($layout.textWidth && $layout.textHeight) {
|
|
114
|
+
<slot />
|
|
115
|
+
}
|
|
116
|
+
</g>
|
|
117
|
+
</svg>
|
|
118
|
+
</div>
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type ChartContext, ChartContextName } from "./ChartTypes";
|
|
2
|
+
|
|
3
|
+
interface ChartBarsProps {
|
|
4
|
+
/**
|
|
5
|
+
* The index of the series to draw, when the Chart has other layers
|
|
6
|
+
* reading different series. Defaults to all series.
|
|
7
|
+
*/
|
|
8
|
+
seriesIndex?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Horizontal bar marks for a Chart: one thick horizontal bar per series,
|
|
13
|
+
* drawn from the left edge of the plot towards its value.
|
|
14
|
+
*
|
|
15
|
+
* For a column per value grouped under each label, use ChartColumns --
|
|
16
|
+
* those run vertically.
|
|
17
|
+
*/
|
|
18
|
+
export default function ChartBars($props?: ChartBarsProps) {
|
|
19
|
+
const context = $context[ChartContextName] as ChartContext;
|
|
20
|
+
if (!context) {
|
|
21
|
+
throw new Error("This layer must be contained within a Chart");
|
|
22
|
+
}
|
|
23
|
+
const { layout, series, colorAt } = context;
|
|
24
|
+
context.registerMark("horizontal");
|
|
25
|
+
|
|
26
|
+
function indexes(): number[] {
|
|
27
|
+
if ($props?.seriesIndex !== undefined) {
|
|
28
|
+
return [$props.seriesIndex];
|
|
29
|
+
}
|
|
30
|
+
return series().map((_, i) => i);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// The vertical band each series occupies within the plot
|
|
34
|
+
function bandHeight(): number {
|
|
35
|
+
const count = Math.max(1, layout.labels.length);
|
|
36
|
+
return (layout.chartBottom - topPad()) / count;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// A little space above for breathing room, matching ChartXAxis's labels
|
|
40
|
+
function topPad(): number {
|
|
41
|
+
return layout.textHeight / 3;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function barHeight(count: number): number {
|
|
45
|
+
return Math.max(1, (bandHeight() / count) * 0.7);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function valueWidth(value: number): number {
|
|
49
|
+
const plotWidth = layout.calculatedWidth - layout.chartLeft;
|
|
50
|
+
const max = Math.max(1, layout.maxValue);
|
|
51
|
+
return Math.max(1, (value / max) * plotWidth);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function barY(i: number): number {
|
|
55
|
+
const count = series().length || 1;
|
|
56
|
+
return (
|
|
57
|
+
topPad() +
|
|
58
|
+
i * bandHeight() +
|
|
59
|
+
(bandHeight() - barHeight(count)) / 2
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@render {
|
|
64
|
+
<svg>
|
|
65
|
+
@for (let i of indexes()) {
|
|
66
|
+
<rect
|
|
67
|
+
x={layout.chartLeft}
|
|
68
|
+
y={barY(i)}
|
|
69
|
+
width={valueWidth(series()[i].data[0])}
|
|
70
|
+
height={barHeight(Math.max(1, series().length))}
|
|
71
|
+
fill={colorAt(i)}
|
|
72
|
+
>
|
|
73
|
+
<title>{`${[layout.labels[i], series()[i].data[0]].filter(Boolean).join("\n")}`}</title>
|
|
74
|
+
</rect>
|
|
75
|
+
}
|
|
76
|
+
</svg>
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type ChartContext, ChartContextName } from "./ChartTypes";
|
|
2
|
+
|
|
3
|
+
interface ChartColumnsProps {
|
|
4
|
+
/**
|
|
5
|
+
* The index of the series to draw, when the Chart has other layers
|
|
6
|
+
* reading different series. Defaults to all series.
|
|
7
|
+
*/
|
|
8
|
+
seriesIndex?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Vertical column marks for a Chart: every value in the chosen series is
|
|
13
|
+
* drawn as a vertical column, grouped under its label.
|
|
14
|
+
*
|
|
15
|
+
* For one thick bar per series, use ChartBars -- those run horizontally.
|
|
16
|
+
*/
|
|
17
|
+
export default function ChartColumns($props?: ChartColumnsProps) {
|
|
18
|
+
const context = $context[ChartContextName] as ChartContext;
|
|
19
|
+
if (!context) {
|
|
20
|
+
throw new Error("This layer must be contained within a Chart");
|
|
21
|
+
}
|
|
22
|
+
const { layout, series, colorAt } = context;
|
|
23
|
+
context.registerMark("vertical");
|
|
24
|
+
|
|
25
|
+
function indexes(): number[] {
|
|
26
|
+
if ($props?.seriesIndex !== undefined) {
|
|
27
|
+
return [$props.seriesIndex];
|
|
28
|
+
}
|
|
29
|
+
return series().map((_, i) => i);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function columnWidth(count: number): number {
|
|
33
|
+
return Math.max(1, (layout.itemWidth / count) * 0.7);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function columnGap(count: number): number {
|
|
37
|
+
return (layout.itemWidth - columnWidth(count) * count) / (count + 1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function columnX(i: number, j: number): number {
|
|
41
|
+
return (
|
|
42
|
+
layout.chartLeft +
|
|
43
|
+
i * layout.itemWidth +
|
|
44
|
+
columnGap(series()[i].data.length) +
|
|
45
|
+
j * columnWidth(series()[i].data.length)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@render {
|
|
50
|
+
<svg>
|
|
51
|
+
@for (let i of indexes()) {
|
|
52
|
+
@for (let [j, value] of series()[i].data.entries()) {
|
|
53
|
+
<rect
|
|
54
|
+
x={columnX(i, j)}
|
|
55
|
+
y={layout.chartBottom - Math.max(1, value * layout.valueHeight)}
|
|
56
|
+
width={columnWidth(series()[i].data.length)}
|
|
57
|
+
height={Math.max(1, value * layout.valueHeight)}
|
|
58
|
+
fill={colorAt(i)}
|
|
59
|
+
>
|
|
60
|
+
<title>{`${[layout.labels[i], value].filter(Boolean).join("\n")}`}</title>
|
|
61
|
+
</rect>
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
</svg>
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type ChartContext, ChartContextName } from "./ChartTypes";
|
|
2
|
+
|
|
3
|
+
interface ChartGridProps {
|
|
4
|
+
/** Horizontal lines across the plot at each value step (default true) */
|
|
5
|
+
hLines?: boolean;
|
|
6
|
+
/** Vertical lines at each category slot (default false) */
|
|
7
|
+
vLines?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Grid lines for a Chart: horizontal lines at each value step and/or
|
|
12
|
+
* vertical lines through each category slot.
|
|
13
|
+
*/
|
|
14
|
+
export default function ChartGrid($props?: ChartGridProps) {
|
|
15
|
+
const context = $context[ChartContextName] as ChartContext;
|
|
16
|
+
if (!context) {
|
|
17
|
+
throw new Error("This layer must be contained within a Chart");
|
|
18
|
+
}
|
|
19
|
+
const { layout } = context;
|
|
20
|
+
|
|
21
|
+
function hLines(): boolean {
|
|
22
|
+
return $props?.hLines !== false;
|
|
23
|
+
}
|
|
24
|
+
function vLines(): boolean {
|
|
25
|
+
return $props?.vLines === true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@render {
|
|
29
|
+
<svg>
|
|
30
|
+
@if (hLines()) {
|
|
31
|
+
@for (let [i] of layout.stepLabels.entries()) {
|
|
32
|
+
<line
|
|
33
|
+
x1={layout.chartLeft}
|
|
34
|
+
y1={layout.chartBottom - i * layout.stepValue * layout.valueHeight}
|
|
35
|
+
x2={layout.calculatedWidth}
|
|
36
|
+
y2={layout.chartBottom - i * layout.stepValue * layout.valueHeight}
|
|
37
|
+
stroke="currentcolor"
|
|
38
|
+
/>
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
@if (vLines()) {
|
|
42
|
+
@for (let [i] of layout.labels.entries()) {
|
|
43
|
+
<line
|
|
44
|
+
x1={layout.chartLeft + i * layout.itemWidth + layout.itemWidth / 2}
|
|
45
|
+
y1={layout.chartBottom}
|
|
46
|
+
x2={layout.chartLeft + i * layout.itemWidth + layout.itemWidth / 2}
|
|
47
|
+
y2={0}
|
|
48
|
+
stroke="currentcolor"
|
|
49
|
+
/>
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
</svg>
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type ChartContext, ChartContextName } from "./ChartTypes";
|
|
2
|
+
import type DataSeries from "./DataSeries";
|
|
3
|
+
|
|
4
|
+
interface ChartLinesProps {
|
|
5
|
+
/** The index of the series to draw (default 0) */
|
|
6
|
+
seriesIndex?: number;
|
|
7
|
+
/** The color to draw with, instead of the series' own */
|
|
8
|
+
color?: string;
|
|
9
|
+
/** Draws a point at each value (default false) */
|
|
10
|
+
showPoints?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Line marks for a Chart: draws the chosen series as a connected line
|
|
15
|
+
* across the category slots.
|
|
16
|
+
*
|
|
17
|
+
* Add alongside ChartColumns or ChartBars for overlaid layers -- or use two
|
|
18
|
+
* ChartLines with different `seriesIndex` to compare series.
|
|
19
|
+
*/
|
|
20
|
+
export default function ChartLines($props?: ChartLinesProps) {
|
|
21
|
+
const context = $context[ChartContextName] as ChartContext;
|
|
22
|
+
if (!context) {
|
|
23
|
+
throw new Error("This layer must be contained within a Chart");
|
|
24
|
+
}
|
|
25
|
+
const { layout, series, colorAt } = context;
|
|
26
|
+
context.registerMark("vertical");
|
|
27
|
+
|
|
28
|
+
function index(): number {
|
|
29
|
+
return $props?.seriesIndex ?? 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function current(): DataSeries | undefined {
|
|
33
|
+
return series()[index()];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function points(): { x: number; y: number }[] {
|
|
37
|
+
const data = current()?.data ?? [];
|
|
38
|
+
return data.map((value, j) => ({
|
|
39
|
+
x: +(layout.chartLeft + j * layout.itemWidth + layout.itemWidth / 2).toFixed(2),
|
|
40
|
+
y: +(layout.chartBottom - value * layout.valueHeight).toFixed(2),
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function stroke(): string {
|
|
45
|
+
return $props?.color || colorAt(index());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@render {
|
|
49
|
+
<svg>
|
|
50
|
+
<polyline points={points().map((p) => `${p.x},${p.y}`).join(" ")} stroke={stroke()} stroke-width="2" fill="none" />
|
|
51
|
+
@if ($props?.showPoints) {
|
|
52
|
+
@for (let [j, point] of points().entries()) {
|
|
53
|
+
<circle cx={point.x} cy={point.y} r="4" stroke={stroke()} stroke-width="2" fill="white">
|
|
54
|
+
<title>{`${[current()?.name, layout.labels[j], current()?.data[j]].filter(Boolean).join("\n")}`}</title>
|
|
55
|
+
</circle>
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
</svg>
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type DataSeries from "./DataSeries";
|
|
2
|
+
import { type ChartLayout } from "../utils/chartLayout";
|
|
3
|
+
|
|
4
|
+
export const ChartContextName: unique symbol = Symbol.for("torp.Chart");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The shared context for layered chart components (ChartXAxis, ChartYAxis,
|
|
8
|
+
* ChartGrid, ChartColumns etc), published by the Chart container.
|
|
9
|
+
*/
|
|
10
|
+
export interface ChartContext {
|
|
11
|
+
/** The plot geometry: margins, scales and derived positions */
|
|
12
|
+
layout: ChartLayout;
|
|
13
|
+
/** The series that layers draw from */
|
|
14
|
+
series: () => DataSeries[];
|
|
15
|
+
/** The label under the x axis, if any */
|
|
16
|
+
xLabel: () => string;
|
|
17
|
+
/** The label beside the y axis, if any */
|
|
18
|
+
yLabel: () => string;
|
|
19
|
+
/** Whether the chart draws horizontal marks (bars), set by the mark layers */
|
|
20
|
+
horizontal: () => boolean;
|
|
21
|
+
/** Called by mark layers to announce their orientation */
|
|
22
|
+
registerMark: (orientation: "horizontal" | "vertical") => void;
|
|
23
|
+
/** The color to draw series i with, from its own color or the palette */
|
|
24
|
+
colorAt: (i: number) => string;
|
|
25
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type ChartContext, ChartContextName } from "./ChartTypes";
|
|
2
|
+
|
|
3
|
+
interface ChartXAxisProps {
|
|
4
|
+
/** Whether to draw the axis line itself (default true) */
|
|
5
|
+
showAxis?: boolean;
|
|
6
|
+
/** An axis title, overriding the Chart's x label */
|
|
7
|
+
label?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The x axis for a chart. For vertical marks (columns, lines) it runs along
|
|
12
|
+
* the bottom with each series' label centered under its slot; for
|
|
13
|
+
* horizontal marks (bars) it becomes the value scale, with a tick for each
|
|
14
|
+
* step along the bottom.
|
|
15
|
+
*/
|
|
16
|
+
export default function ChartXAxis($props?: ChartXAxisProps) {
|
|
17
|
+
const context = $context[ChartContextName] as ChartContext;
|
|
18
|
+
if (!context) {
|
|
19
|
+
throw new Error("ChartXAxis must be contained within a Chart");
|
|
20
|
+
}
|
|
21
|
+
const { layout, xLabel, horizontal } = context;
|
|
22
|
+
|
|
23
|
+
function showAxis(): boolean {
|
|
24
|
+
return $props?.showAxis !== false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function title(): string {
|
|
28
|
+
if ($props?.label !== undefined) return $props.label;
|
|
29
|
+
return xLabel();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function plotWidth(): number {
|
|
33
|
+
return layout.calculatedWidth - layout.chartLeft;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function unitWidth(): number {
|
|
37
|
+
return plotWidth() / Math.max(1, layout.maxValue);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function tickX(i: number): number {
|
|
41
|
+
return layout.chartLeft + i * layout.stepValue * unitWidth();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function tickY(): number {
|
|
45
|
+
return layout.calculatedHeight - layout.textHeight / 3;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@render {
|
|
49
|
+
<svg>
|
|
50
|
+
@if (showAxis()) {
|
|
51
|
+
<line x1={layout.chartLeft} y1={layout.chartBottom} x2={layout.calculatedWidth} y2={layout.chartBottom} stroke="currentcolor" />
|
|
52
|
+
}
|
|
53
|
+
@if (!horizontal()) {
|
|
54
|
+
@if (title()) {
|
|
55
|
+
<text
|
|
56
|
+
text-anchor="middle"
|
|
57
|
+
dominant-baseline="text-after-edge"
|
|
58
|
+
x={layout.calculatedWidth - (layout.calculatedWidth - layout.chartLeft) / 2}
|
|
59
|
+
y={layout.calculatedHeight}
|
|
60
|
+
>
|
|
61
|
+
{title()}
|
|
62
|
+
</text>
|
|
63
|
+
}
|
|
64
|
+
@for (let [i, label] of layout.labels.entries()) {
|
|
65
|
+
<text
|
|
66
|
+
text-anchor="middle"
|
|
67
|
+
dominant-baseline="text-after-edge"
|
|
68
|
+
x={layout.chartLeft + layout.itemWidth * i + layout.itemWidth / 2}
|
|
69
|
+
y={tickY()}
|
|
70
|
+
>
|
|
71
|
+
{label}
|
|
72
|
+
</text>
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
@for (let [i, label] of layout.stepLabels.entries()) {
|
|
76
|
+
<text text-anchor="middle" dominant-baseline="text-after-edge" x={tickX(i)} y={tickY()}>
|
|
77
|
+
{label}
|
|
78
|
+
</text>
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
</svg>
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { type ChartContext, ChartContextName } from "./ChartTypes";
|
|
2
|
+
|
|
3
|
+
interface ChartYAxisProps {
|
|
4
|
+
/** Whether to draw the axis line itself (default true) */
|
|
5
|
+
showAxis?: boolean;
|
|
6
|
+
/** Which side of the plot the value scale runs on (default "left") */
|
|
7
|
+
side?: "left" | "right";
|
|
8
|
+
/** An axis title, overriding the Chart's y label */
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The y axis for a chart. For vertical marks (columns, lines) it is the
|
|
14
|
+
* value scale -- a line down the `side` edge with a tick for each step
|
|
15
|
+
* between 0 and the maximum value. For horizontal marks (bars) it carries
|
|
16
|
+
* the category labels, one per band down the left.
|
|
17
|
+
*
|
|
18
|
+
* Drawing it on either side does not change the plot geometry; overlaying
|
|
19
|
+
* an axis is purely presentational.
|
|
20
|
+
*/
|
|
21
|
+
export default function ChartYAxis($props?: ChartYAxisProps) {
|
|
22
|
+
const context = $context[ChartContextName] as ChartContext;
|
|
23
|
+
if (!context) {
|
|
24
|
+
throw new Error("ChartYAxis must be contained within a Chart");
|
|
25
|
+
}
|
|
26
|
+
const { layout, yLabel, horizontal } = context;
|
|
27
|
+
|
|
28
|
+
function showAxis(): boolean {
|
|
29
|
+
return $props?.showAxis !== false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function side(): "left" | "right" {
|
|
33
|
+
return $props?.side ?? "left";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function title(): string {
|
|
37
|
+
if ($props?.label !== undefined) return $props.label;
|
|
38
|
+
return yLabel();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function bandHeight(): number {
|
|
42
|
+
const count = Math.max(1, layout.labels.length);
|
|
43
|
+
return (layout.chartBottom - topPad()) / count;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function topPad(): number {
|
|
47
|
+
return layout.textHeight / 3;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function bandCenter(i: number): number {
|
|
51
|
+
return topPad() + i * bandHeight() + bandHeight() / 2;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function axisX(): number {
|
|
55
|
+
return side() === "left" ? layout.chartLeft : layout.calculatedWidth;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function tickX(): number {
|
|
59
|
+
if (side() === "left") {
|
|
60
|
+
return layout.chartLeft - layout.textHeight / 4;
|
|
61
|
+
}
|
|
62
|
+
return layout.calculatedWidth + layout.textHeight / 4;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function anchor(): string {
|
|
66
|
+
return side() === "left" ? "end" : "start";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@render {
|
|
70
|
+
<svg>
|
|
71
|
+
@if (showAxis()) {
|
|
72
|
+
<line x1={axisX()} y1={layout.chartBottom} x2={axisX()} y2={0} stroke="currentcolor" />
|
|
73
|
+
}
|
|
74
|
+
@if (horizontal()) {
|
|
75
|
+
@for (let [i, label] of layout.labels.entries()) {
|
|
76
|
+
<text
|
|
77
|
+
text-anchor="end"
|
|
78
|
+
dominant-baseline="middle"
|
|
79
|
+
x={layout.chartLeft - layout.textHeight / 4}
|
|
80
|
+
y={bandCenter(i)}
|
|
81
|
+
>
|
|
82
|
+
{label}
|
|
83
|
+
</text>
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
@if (side() === "left" && title()) {
|
|
87
|
+
<text
|
|
88
|
+
text-anchor="middle"
|
|
89
|
+
dominant-baseline="hanging"
|
|
90
|
+
x={0}
|
|
91
|
+
y={layout.chartBottom / 2}
|
|
92
|
+
transform={`rotate(-90, 0, ${layout.chartBottom / 2})`}
|
|
93
|
+
>
|
|
94
|
+
{title()}
|
|
95
|
+
</text>
|
|
96
|
+
}
|
|
97
|
+
@for (let [i, label] of layout.stepLabels.entries()) {
|
|
98
|
+
<text text-anchor={anchor()} dominant-baseline="middle" x={tickX()} y={layout.chartBottom - i * layout.stepValue * layout.valueHeight}>
|
|
99
|
+
{label}
|
|
100
|
+
</text>
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
</svg>
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import Chart from "./Chart.torp";
|
|
3
|
+
import ChartXAxis from "./ChartXAxis.torp";
|
|
4
|
+
import ChartColumns from "./ChartColumns.torp";
|
|
5
|
+
import ChartGrid from "./ChartGrid.torp";
|
|
6
|
+
import ChartYAxis from "./ChartYAxis.torp";
|
|
7
|
+
import type DataSeries from "./DataSeries";
|
|
8
|
+
|
|
9
|
+
interface ColumnChartProps {
|
|
10
|
+
/** An ID for the root element */
|
|
11
|
+
id?: string;
|
|
12
|
+
/** Classes for the root element */
|
|
13
|
+
class?: ClassValue;
|
|
14
|
+
/** Styles for the root element */
|
|
15
|
+
style?: StyleValue;
|
|
16
|
+
/** An ARIA label describing the chart for screen readers */
|
|
17
|
+
ariaLabel?: string;
|
|
18
|
+
series: DataSeries[];
|
|
19
|
+
height?: number;
|
|
20
|
+
width?: number;
|
|
21
|
+
xLabel?: string;
|
|
22
|
+
yLabel?: string;
|
|
23
|
+
maxValue?: number;
|
|
24
|
+
stepCount?: number;
|
|
25
|
+
stepValue?: number;
|
|
26
|
+
showHLines?: boolean;
|
|
27
|
+
showVLines?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A column chart: each value in each series is drawn as a vertical column,
|
|
32
|
+
* grouped under the series' label along the bottom axis.
|
|
33
|
+
*
|
|
34
|
+
* This is a preset over the layered chart components:
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* <Chart>
|
|
38
|
+
* <ChartValueAxis />
|
|
39
|
+
* <ChartGrid />
|
|
40
|
+
* <ChartXAxis />
|
|
41
|
+
* <ChartColumns />
|
|
42
|
+
* </Chart>
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Compose those directly to customize -- e.g. the value axis on the right,
|
|
46
|
+
* or a line overlaid with ChartLines.
|
|
47
|
+
*/
|
|
48
|
+
export default function ColumnChart($props: ColumnChartProps) {
|
|
49
|
+
@render {
|
|
50
|
+
<Chart
|
|
51
|
+
{$props?.id}
|
|
52
|
+
class={["torp-column-chart", $props?.class]}
|
|
53
|
+
{$props?.style}
|
|
54
|
+
{$props?.ariaLabel}
|
|
55
|
+
{$props?.series}
|
|
56
|
+
{$props?.height}
|
|
57
|
+
{$props?.width}
|
|
58
|
+
{$props?.xLabel}
|
|
59
|
+
{$props?.yLabel}
|
|
60
|
+
{$props?.maxValue}
|
|
61
|
+
{$props?.stepCount}
|
|
62
|
+
{$props?.stepValue}
|
|
63
|
+
>
|
|
64
|
+
<ChartYAxis />
|
|
65
|
+
<ChartGrid hLines={$props.showHLines === true} vLines={$props.showVLines === true} />
|
|
66
|
+
<ChartXAxis />
|
|
67
|
+
<ChartColumns />
|
|
68
|
+
</Chart>
|
|
69
|
+
}
|
|
70
|
+
}
|