@torpor/ui 0.3.6 → 0.4.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.
Files changed (89) hide show
  1. package/dist/Accordion/Accordion.torp +26 -25
  2. package/dist/Accordion/AccordionItem.torp +3 -3
  3. package/dist/Accordion/AccordionTypes.ts +5 -5
  4. package/dist/Accordion/index.d.ts +2 -2
  5. package/dist/Calendar/CalendarTypes.ts +1 -1
  6. package/dist/Charts/BarChart.torp +159 -0
  7. package/dist/Charts/DataSeries.ts +5 -0
  8. package/dist/Charts/GridLines.torp +65 -0
  9. package/dist/Charts/LineChart.torp +196 -0
  10. package/dist/Charts/PieChart.torp +145 -0
  11. package/dist/Charts/ScatterChart.torp +215 -0
  12. package/dist/Charts/SparkLine.torp +71 -0
  13. package/dist/Charts/XAxis.torp +66 -0
  14. package/dist/Charts/YAxis.torp +47 -0
  15. package/dist/Charts/index.d.ts +220 -0
  16. package/dist/Charts/index.js +34 -0
  17. package/dist/Charts/index.js.map +1 -0
  18. package/dist/Contextual/Contextual.torp +8 -3
  19. package/dist/Contextual/ContextualTypes.ts +1 -1
  20. package/dist/Dialog/Dialog.torp +12 -16
  21. package/dist/Dialog/DialogButton.torp +3 -0
  22. package/dist/Dialog/DialogTypes.ts +2 -2
  23. package/dist/Dialog/index.d.ts +2 -0
  24. package/dist/Disclosure/Disclosure.torp +3 -3
  25. package/dist/Disclosure/DisclosureTypes.ts +1 -1
  26. package/dist/Drawer/Drawer.torp +2 -2
  27. package/dist/Drawer/DrawerTypes.ts +1 -1
  28. package/dist/Form/CheckBox.torp +1 -2
  29. package/dist/Form/Input.torp +1 -1
  30. package/dist/Form/MessageSummary.torp +38 -0
  31. package/dist/Form/TextArea.torp +3 -0
  32. package/dist/Form/index.d.ts +25 -1
  33. package/dist/Form/index.js +4 -0
  34. package/dist/ListBox/ListBox.torp +271 -0
  35. package/dist/ListBox/ListBoxItem.torp +107 -0
  36. package/dist/ListBox/ListBoxSeparator.torp +26 -0
  37. package/dist/ListBox/ListBoxTypes.ts +23 -0
  38. package/dist/ListBox/index.d.ts +97 -0
  39. package/dist/ListBox/index.js +14 -0
  40. package/dist/ListBox/index.js.map +1 -0
  41. package/dist/Menu/Menu.torp +19 -14
  42. package/dist/Menu/MenuTypes.ts +5 -4
  43. package/dist/MenuBar/MenuBarItem.torp +5 -8
  44. package/dist/MenuBar/MenuBarTypes.ts +2 -2
  45. package/dist/Modal/Modal.torp +7 -2
  46. package/dist/Modal/ModalTypes.ts +1 -1
  47. package/dist/NavMenu/NavMenu.torp +7 -0
  48. package/dist/NavMenu/NavMenuButton.torp +5 -2
  49. package/dist/NavMenu/NavMenuPopout.torp +1 -1
  50. package/dist/NavMenu/{NavMenuPopoutButton.torp → NavMenuPopoutTrigger.torp} +4 -4
  51. package/dist/NavMenu/NavMenuSeparator.torp +2 -1
  52. package/dist/NavMenu/NavMenuTypes.ts +4 -3
  53. package/dist/NavMenu/index.d.ts +22 -18
  54. package/dist/NavMenu/index.js +4 -4
  55. package/dist/Pagination/Pagination.torp +145 -0
  56. package/dist/Pagination/PaginationItem.torp +100 -0
  57. package/dist/Pagination/PaginationTypes.ts +25 -0
  58. package/dist/Pagination/buildPages.ts +57 -0
  59. package/dist/Pagination/index.d.ts +53 -0
  60. package/dist/Pagination/index.js +40 -0
  61. package/dist/Pagination/index.js.map +1 -0
  62. package/dist/Popover/Popover.torp +8 -3
  63. package/dist/Popover/PopoverTypes.ts +1 -1
  64. package/dist/SelectBox/SelectBox.torp +136 -0
  65. package/dist/SelectBox/SelectBoxContent.torp +81 -0
  66. package/dist/SelectBox/SelectBoxInput.torp +95 -0
  67. package/dist/SelectBox/SelectBoxTrigger.torp +41 -0
  68. package/dist/SelectBox/SelectBoxTypes.ts +18 -0
  69. package/dist/SelectBox/index.d.ts +127 -0
  70. package/dist/SelectBox/index.js +18 -0
  71. package/dist/SelectBox/index.js.map +1 -0
  72. package/dist/TabGroup/TabGroup.torp +3 -3
  73. package/dist/TabGroup/TabGroupTypes.ts +2 -2
  74. package/dist/ToolBar/ToolBar.torp +198 -0
  75. package/dist/ToolBar/ToolBarButton.torp +57 -0
  76. package/dist/ToolBar/ToolBarElement.torp +55 -0
  77. package/dist/ToolBar/ToolBarPopout.torp +97 -0
  78. package/dist/ToolBar/ToolBarPopoutContent.torp +92 -0
  79. package/dist/ToolBar/ToolBarPopoutTrigger.torp +138 -0
  80. package/dist/ToolBar/ToolBarSeparator.torp +25 -0
  81. package/dist/ToolBar/ToolBarTypes.ts +35 -0
  82. package/dist/ToolBar/index.d.ts +212 -0
  83. package/dist/ToolBar/index.js +30 -0
  84. package/dist/ToolBar/index.js.map +1 -0
  85. package/dist/metafile-esm.json +1 -1
  86. package/dist/utils/PopoutTypes.ts +35 -0
  87. package/dist/utils/chartColors.ts +17 -0
  88. package/dist/utils/chartUtils.ts +71 -0
  89. package/package.json +22 -2
@@ -0,0 +1,145 @@
1
+ import type { ClassValue, StyleValue } from "@torpor/view";
2
+ import { chartColors } from "../utils/chartColors";
3
+
4
+ interface PieData {
5
+ name: string;
6
+ value: number;
7
+ color: string;
8
+ }
9
+
10
+ interface SegmentData {
11
+ name: string;
12
+ value: number;
13
+ color: string;
14
+ path: string;
15
+ }
16
+
17
+ interface PieChartProps {
18
+ /** An ID for the root element */
19
+ id?: string;
20
+ /** Classes for the root element */
21
+ class?: ClassValue,
22
+ /** Styles for the root element */
23
+ style?: StyleValue,
24
+ data: PieData[],
25
+ size?: number,
26
+ radius?: number,
27
+ cutout?: number,
28
+ }
29
+
30
+ export default function PieChart($props: PieChartProps) {
31
+ // Default values
32
+ $props.size ||= 200;
33
+ $props.radius ||= 50;
34
+
35
+ // NOTE: Take two pixels off the radius so that the stroke doesn't go outside the border
36
+ let $state = $watch({
37
+ get radius() {
38
+ return ($props.radius! / 100) * $props.size! - 2;
39
+ },
40
+ get cutout() {
41
+ return ($props.cutout! / 100) * $props.size! - 2;
42
+ },
43
+ get segments() {
44
+ return buildSegments($props.size!, $props.data, $props.radius!, $props.cutout!);
45
+ }
46
+ });
47
+
48
+ function buildSegments(
49
+ theSize: number,
50
+ theData: PieData[],
51
+ theRadiusSize: number,
52
+ theCutoutSize: number
53
+ ): SegmentData[] {
54
+ const segments = [];
55
+ const x = theSize / 2;
56
+ const y = theSize / 2;
57
+ const totalValue = theData.reduce((a, b) => a + b.value, 0);
58
+
59
+ // Start at the top of the chart, at 180 + 360 so that we don't have to
60
+ // take wrapping angles into account
61
+ let startAngle = 540;
62
+ for (let i = 0; i < theData.length; i++) {
63
+ // Subtract the angle to go clockwise
64
+ const endAngle = startAngle - (theData[i].value / totalValue) * 360;
65
+ const largeArc = startAngle - endAngle > 180 ? 1 : 0;
66
+ segments.push({
67
+ name: theData[i].name,
68
+ value: theData[i].value,
69
+ color: theData[i].color,
70
+ path: buildPath(x, y, theRadiusSize, theCutoutSize, startAngle, endAngle, largeArc)
71
+ });
72
+ startAngle = endAngle;
73
+ }
74
+ return segments;
75
+ }
76
+
77
+ function buildPath(
78
+ x: number,
79
+ y: number,
80
+ theRadiusSize: number,
81
+ theCutoutSize: number,
82
+ startAngle: number,
83
+ endAngle: number,
84
+ largeArc: number
85
+ ): string {
86
+ // HACK: Drawing an arc of 360 degrees doesn't work
87
+ if (startAngle - endAngle === 360) {
88
+ endAngle = 359.9999;
89
+ }
90
+
91
+ // Use some trig to find the points on the circumference at the start and end angles
92
+ const startx = x + theRadiusSize * Math.sin((Math.PI * startAngle) / 180);
93
+ const starty = x + theRadiusSize * Math.cos((Math.PI * startAngle) / 180);
94
+
95
+ const endx = x + theRadiusSize * Math.sin((Math.PI * endAngle) / 180);
96
+ const endy = y + theRadiusSize * Math.cos((Math.PI * endAngle) / 180);
97
+
98
+ if ($props.cutout) {
99
+ // Use some trig to find the points on the cutout at the start and end angles
100
+ const cutoutstartx = x + theCutoutSize * Math.sin((Math.PI * startAngle) / 180);
101
+ const cutoutstarty = x + theCutoutSize * Math.cos((Math.PI * startAngle) / 180);
102
+
103
+ const cutoutendx = x + theCutoutSize * Math.sin((Math.PI * endAngle) / 180);
104
+ const cutoutendy = y + theCutoutSize * Math.cos((Math.PI * endAngle) / 180);
105
+
106
+ // Build the path - move to the cutout start, line to the start angle on the circumference,
107
+ // arc to the end angle on the circumference, line to the cutout end, and then arc to the
108
+ // cutout start
109
+ const path = `
110
+ M${cutoutstartx},${cutoutstarty}
111
+ L${startx},${starty}
112
+ A${theRadiusSize},${theRadiusSize} 0 ${largeArc} 1 ${endx},${endy}
113
+ L${cutoutendx},${cutoutendy}
114
+ A${theCutoutSize},${theCutoutSize} 0 ${largeArc} 0 ${cutoutstartx},${cutoutstarty}`.trim();
115
+ return path;
116
+ } else {
117
+ // Build the path - move to the origin, line to the start angle on the circumference,
118
+ // arc to the end angle on the circumference, and then close back to the start
119
+ const path = `M${x},${y} L${startx},${starty} A${theRadiusSize},${theRadiusSize} 0 ${largeArc} 1 ${endx},${endy} Z`;
120
+ return path;
121
+ }
122
+ }
123
+
124
+ @render {
125
+ <div
126
+ {$props?.id}
127
+ class={["torp-pie-chart", $props?.class]}
128
+ {$props?.style}
129
+ >
130
+ <svg
131
+ version="1.1"
132
+ width={$props.size}
133
+ height={$props.size}
134
+ >
135
+ <g>
136
+ @for (let [i, segment] of $state.segments.entries()) {
137
+ <path d={segment.path} fill={segment.color || chartColors[i % chartColors.length]}>
138
+ <title>{`${segment.name}\n${segment.value}`}</title>
139
+ </path>
140
+ }
141
+ </g>
142
+ </svg>
143
+ </div>
144
+ }
145
+ }
@@ -0,0 +1,215 @@
1
+ import type { ClassValue, StyleValue } from "@torpor/view";
2
+ import XAxis from "./XAxis.torp";
3
+ import YAxis from "./YAxis.torp";
4
+ import GridLines from "./GridLines.torp";
5
+ import {
6
+ calculateStepValue,
7
+ calculateStepLabels,
8
+ calculateValueHeight,
9
+ calculateChartBottom,
10
+ calculateChartLeft
11
+ } from "../utils/chartUtils";
12
+ import { chartColors } from "../utils/chartColors";
13
+
14
+ interface Pair {
15
+ x: number;
16
+ y: number;
17
+ label: string;
18
+ }
19
+
20
+ interface DataSeries {
21
+ name: string;
22
+ color: string;
23
+ data: Pair[];
24
+ }
25
+
26
+ interface ScatterChartProps {
27
+ /** An ID for the root element */
28
+ id?: string;
29
+ /** Classes for the root element */
30
+ class?: ClassValue,
31
+ /** Styles for the root element */
32
+ style?: StyleValue,
33
+ series: DataSeries[],
34
+ height?: number,
35
+ width?: number,
36
+ xLabel?: string,
37
+ yLabel?: string,
38
+ xStepCount?: number,
39
+ xStepValue?: number,
40
+ yStepCount?: number,
41
+ yStepValue?: number,
42
+ hideXAxis?: boolean,
43
+ hideYAxis?: boolean,
44
+ showHLines?: boolean,
45
+ showVLines?: boolean,
46
+ }
47
+
48
+ export default function ScatterChart($props: ScatterChartProps) {
49
+ const defaultHeight = 200;
50
+ const defaultWidth = 400;
51
+
52
+ let container: HTMLElement;
53
+ let measurer: SVGTextElement;
54
+
55
+ let $state = $watch({
56
+ textWidth: 0,
57
+ textHeight: 0,
58
+ get calculatedHeight() {
59
+ return $props.height || defaultHeight;
60
+ },
61
+ get calculatedWidth() {
62
+ return $props.width || container?.clientWidth || defaultWidth;
63
+ },
64
+ get maxXValue() {
65
+ return calculateMaxValue($props.series, "x");
66
+ },
67
+ get calculatedXStepValue() {
68
+ return $props.xStepValue || calculateStepValue($state.maxXValue, $props.xStepCount);
69
+ },
70
+ get xStepLabels() {
71
+ return calculateStepLabels($props.xStepCount || 2, $state.calculatedXStepValue);
72
+ },
73
+ get maxYValue() {
74
+ return calculateMaxValue($props.series, "y");
75
+ },
76
+ get calculatedYStepValue() {
77
+ return $props.yStepValue || calculateStepValue($state.maxYValue, $props.yStepCount);
78
+ },
79
+ get yStepLabels() {
80
+ return calculateStepLabels($props.yStepCount || 2, $state.calculatedYStepValue);
81
+ },
82
+ get itemWidth() {
83
+ return calculateValueHeight(
84
+ $state.calculatedWidth - $state.chartLeft,
85
+ $state.textHeight,
86
+ $state.calculatedXStepValue,
87
+ $props.xStepCount || 2
88
+ );
89
+ },
90
+ get valueHeight() {
91
+ return calculateValueHeight($state.chartBottom, $state.textHeight, $state.calculatedYStepValue, $props.yStepCount || 2);
92
+ },
93
+ get chartBottom() {
94
+ return calculateChartBottom($props.xLabel ?? "", $state.calculatedHeight, $state.textHeight);
95
+ },
96
+ get chartLeft() {
97
+ return calculateChartLeft($state.yStepLabels, $props.yLabel ?? "", $state.textHeight, $state.textWidth);
98
+ },
99
+ get pointSeries() {
100
+ return buildPointSeries(
101
+ $props.series,
102
+ $state.chartLeft,
103
+ $state.chartBottom,
104
+ $state.itemWidth,
105
+ $state.valueHeight
106
+ );
107
+ },
108
+ });
109
+
110
+ $mount(() => {
111
+ // HACK: Wait until the measurer is really available
112
+ setTimeout(() => {
113
+ const bbox = measurer.getBBox();
114
+ $state.textWidth = bbox.width;
115
+ $state.textHeight = bbox.height * 1.5;
116
+ measurer.remove();
117
+ }, 1);
118
+ });
119
+
120
+ function calculateMaxValue(series: DataSeries[], field: "x" | "y") {
121
+ // HACK: Yeah, nested reduces
122
+ return series.reduce((a, b) => {
123
+ return Math.max(
124
+ a,
125
+ b.data.reduce((c, d) => Math.max(c, field === "x" ? d.x : d.y), 0)
126
+ );
127
+ }, 0);
128
+ }
129
+
130
+ function buildPointSeries(
131
+ theSeries: DataSeries[],
132
+ theChartLeft: number,
133
+ theChartBottom: number,
134
+ theItemWidth: number,
135
+ theValueHeight: number
136
+ ) {
137
+ return theSeries.map((series) => {
138
+ return series.data.map((item) => {
139
+ const x = +(theChartLeft + item.x * theItemWidth).toFixed(2);
140
+ const y = +(theChartBottom - item.y * theValueHeight).toFixed(2);
141
+ return { x, y };
142
+ });
143
+ });
144
+ }
145
+
146
+ @render {
147
+ <div
148
+ {$props?.id}
149
+ class={["torp-scatter-chart", $props?.class]}
150
+ {$props?.style}
151
+ &ref={container}
152
+ >
153
+ <svg version="1.1" width={$state.calculatedWidth} height={$state.calculatedHeight}>
154
+ <g>
155
+ <text &ref={measurer} x="-999" y="-999">8</text>
156
+
157
+ @if ($state.textWidth && $state.textHeight) {
158
+ <XAxis
159
+ showAxis={!$props.hideXAxis}
160
+ height={$state.calculatedHeight}
161
+ width={$state.calculatedWidth}
162
+ {$state.itemWidth}
163
+ {$state.textHeight}
164
+ xLabel={$props.xLabel ?? ""}
165
+ labels={[]}
166
+ stepLabels={$state.xStepLabels}
167
+ stepValue={$state.calculatedXStepValue}
168
+ {$state.chartLeft}
169
+ {$state.chartBottom}
170
+ />
171
+ <YAxis
172
+ showAxis={!$props.hideYAxis}
173
+ {$state.valueHeight}
174
+ {$state.textHeight}
175
+ yLabel={$props.yLabel ?? ""}
176
+ stepLabels={$state.yStepLabels}
177
+ stepValue={$state.calculatedYStepValue}
178
+ {$state.chartLeft}
179
+ {$state.chartBottom}
180
+ />
181
+ <GridLines
182
+ {$props.showHLines}
183
+ {$props.showVLines}
184
+ width={$state.calculatedWidth}
185
+ labels={[]}
186
+ xStepValue={$state.calculatedXStepValue}
187
+ {$state.xStepLabels}
188
+ yStepValue={$state.calculatedYStepValue}
189
+ {$state.yStepLabels}
190
+ {$state.itemWidth}
191
+ {$state.valueHeight}
192
+ {$state.chartLeft}
193
+ {$state.chartBottom}
194
+ type="line"
195
+ />
196
+ @for (let [i, series] of $props.series.entries()) {
197
+ @for (let [j, point] of $state.pointSeries[i].entries()) {
198
+ <circle
199
+ cx={point.x}
200
+ cy={point.y}
201
+ r="4"
202
+ fill={series.color || chartColors[i % chartColors.length]}
203
+ >
204
+ <title>{
205
+ `${[series.name, series.data[j].label, series.data[j].x].filter(Boolean).join("\n")}, ${series.data[j].y}`
206
+ }</title>
207
+ </circle>
208
+ }
209
+ }
210
+ }
211
+ </g>
212
+ </svg>
213
+ </div>
214
+ }
215
+ }
@@ -0,0 +1,71 @@
1
+ import type { ClassValue, StyleValue } from "@torpor/view";
2
+ import { calculateMaxValue } from "../utils/chartUtils";
3
+ import { chartColors } from "../utils/chartColors";
4
+
5
+ interface SparkLineProps {
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
+ data: number[];
13
+ color?: string,
14
+ height?: number,
15
+ width?: number,
16
+ }
17
+
18
+ export default function SparkLine($props: SparkLineProps) {
19
+ const defaultHeight = 20;
20
+ const defaultWidth = 100;
21
+
22
+ let container: HTMLElement;
23
+
24
+ let $state = $watch({
25
+ get calculatedHeight() {
26
+ return $props.height || defaultHeight;
27
+ },
28
+ get calculatedWidth() {
29
+ return $props.width || container?.clientWidth || defaultWidth;
30
+ },
31
+ get calculatedSeries() {
32
+ return [{ name: "", color: $props.color ?? "", data: $props.data }];
33
+ },
34
+ get maxValue() {
35
+ return calculateMaxValue($state.calculatedSeries);
36
+ },
37
+ get itemWidth() {
38
+ return $state.calculatedWidth / $props.data.length;
39
+ },
40
+ get valueHeight() {
41
+ return ($state.calculatedHeight - 4) / $state.maxValue;
42
+ },
43
+ get pointSeries() {
44
+ return $props.data.map((value, i) => {
45
+ const x = +(i * $state.itemWidth).toFixed(2);
46
+ const y = +($state.calculatedHeight - 2 - value * $state.valueHeight).toFixed(2);
47
+ return { x, y };
48
+ });
49
+ }
50
+ });
51
+
52
+ @render {
53
+ <div
54
+ {$props?.id}
55
+ class={["torp-spark-line", $props?.class]}
56
+ {$props?.style}
57
+ &ref={container}
58
+ >
59
+ <svg version="1.1" width={$state.calculatedWidth} height={$state.calculatedHeight}>
60
+ <g>
61
+ <polyline
62
+ points={$state.pointSeries.map((p) => `${p.x},${p.y}`).join(" ")}
63
+ stroke={$props.color || chartColors[0]}
64
+ stroke-width="2"
65
+ fill="none"
66
+ />
67
+ </g>
68
+ </svg>
69
+ </div>
70
+ }
71
+ }
@@ -0,0 +1,66 @@
1
+ interface XAxisProps {
2
+ showAxis: boolean,
3
+ height: number,
4
+ width: number,
5
+ itemWidth: number,
6
+ textHeight: number,
7
+ xLabel: string,
8
+ labels: string[] ,
9
+ stepLabels:number[] ,
10
+ stepValue: number,
11
+ chartLeft: number,
12
+ chartBottom: number,
13
+ }
14
+
15
+ export default function XAxis($props: XAxisProps) {
16
+ let $state = $watch({
17
+ chartLabelBottom: $props.xLabel ? $props.height - $props.textHeight : $props.height,
18
+ });
19
+
20
+ @render {
21
+ <svg>
22
+ @if ($props.showAxis) {
23
+ <line
24
+ x1={$props.chartLeft}
25
+ y1={$props.chartBottom}
26
+ x2={$props.width}
27
+ y2={$props.chartBottom}
28
+ stroke="currentcolor"
29
+ />
30
+ }
31
+ @if ($props.xLabel) {
32
+ <text
33
+ text-anchor="middle"
34
+ dominant-baseline="text-after-edge"
35
+ x={$props.width - ($props.width - $props.chartLeft) / 2}
36
+ y={$props.height}
37
+ >
38
+ {$props.xLabel}
39
+ </text>
40
+ }
41
+ @if ($props.stepLabels.length && $props.stepValue) {
42
+ @for (let [i, label] of $props.stepLabels.entries()) {
43
+ <text
44
+ text-anchor="middle"
45
+ dominant-baseline="text-after-edge"
46
+ x={$props.chartLeft + i * $props.stepValue * $props.itemWidth}
47
+ y={$state.chartLabelBottom}
48
+ >
49
+ {label}
50
+ </text>
51
+ }
52
+ } else {
53
+ @for (let [i, label] of $props.labels.entries()) {
54
+ <text
55
+ text-anchor="middle"
56
+ dominant-baseline="text-after-edge"
57
+ x={$props.chartLeft + $props.itemWidth * i + $props.itemWidth / 2}
58
+ y={$state.chartLabelBottom}
59
+ >
60
+ {label}
61
+ </text>
62
+ }
63
+ }
64
+ </svg>
65
+ }
66
+ }
@@ -0,0 +1,47 @@
1
+ interface YAxisProps {
2
+ showAxis: boolean,
3
+ valueHeight: number,
4
+ textHeight: number,
5
+ yLabel: string,
6
+ stepLabels: number[],
7
+ stepValue: number,
8
+ chartLeft: number,
9
+ chartBottom: number,
10
+ }
11
+
12
+ export default function YAxis($props: YAxisProps) {
13
+ @render {
14
+ <svg>
15
+ @if ($props.showAxis) {
16
+ <line
17
+ x1={$props.chartLeft}
18
+ y1={$props.chartBottom}
19
+ x2={$props.chartLeft}
20
+ y2={0}
21
+ stroke="currentcolor"
22
+ />
23
+ }
24
+ @if ($props.yLabel) {
25
+ <text
26
+ text-anchor="middle"
27
+ dominant-baseline="hanging"
28
+ x={0}
29
+ y={$props.chartBottom / 2}
30
+ transform={`rotate(-90, ${0}, ${$props.chartBottom / 2})`}
31
+ >
32
+ {$props.yLabel}
33
+ </text>
34
+ }
35
+ @for (let [i, label] of $props.stepLabels.entries()) {
36
+ <text
37
+ text-anchor="end"
38
+ dominant-baseline="middle"
39
+ x={$props.chartLeft - $props.textHeight / 4}
40
+ y={$props.chartBottom - i * $props.stepValue * $props.valueHeight}
41
+ >
42
+ {label}
43
+ </text>
44
+ }
45
+ </svg>
46
+ }
47
+ }