layerchart 2.0.0-next.1 → 2.0.0-next.11

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 (275) hide show
  1. package/dist/actions/movable.d.ts +28 -0
  2. package/dist/actions/movable.js +91 -0
  3. package/dist/components/AnnotationLine.svelte +143 -0
  4. package/dist/components/AnnotationLine.svelte.d.ts +30 -0
  5. package/dist/components/AnnotationPoint.svelte +119 -0
  6. package/dist/components/AnnotationPoint.svelte.d.ts +34 -0
  7. package/dist/components/AnnotationRange.svelte +147 -0
  8. package/dist/components/AnnotationRange.svelte.d.ts +40 -0
  9. package/dist/components/Arc.svelte +344 -151
  10. package/dist/components/Arc.svelte.d.ts +138 -0
  11. package/dist/components/Area.svelte +165 -149
  12. package/dist/components/Area.svelte.d.ts +45 -0
  13. package/dist/components/Axis.svelte +320 -179
  14. package/dist/components/Axis.svelte.d.ts +127 -0
  15. package/dist/components/Bar.svelte +166 -107
  16. package/dist/components/Bar.svelte.d.ts +51 -0
  17. package/dist/components/Bars.svelte +56 -67
  18. package/dist/components/Bars.svelte.d.ts +27 -0
  19. package/dist/components/Blur.svelte +42 -12
  20. package/dist/components/Blur.svelte.d.ts +23 -21
  21. package/dist/components/Bounds.svelte +49 -19
  22. package/dist/components/Bounds.svelte.d.ts +24 -50
  23. package/dist/components/BrushContext.svelte +296 -168
  24. package/dist/components/BrushContext.svelte.d.ts +97 -65
  25. package/dist/components/Calendar.svelte +116 -59
  26. package/dist/components/Calendar.svelte.d.ts +50 -31
  27. package/dist/components/Chart.svelte +1289 -398
  28. package/dist/components/Chart.svelte.d.ts +535 -410
  29. package/dist/components/ChartClipPath.svelte +37 -15
  30. package/dist/components/ChartClipPath.svelte.d.ts +21 -19
  31. package/dist/components/Circle.svelte +124 -85
  32. package/dist/components/Circle.svelte.d.ts +52 -0
  33. package/dist/components/CircleClipPath.svelte +76 -16
  34. package/dist/components/CircleClipPath.svelte.d.ts +46 -0
  35. package/dist/components/ClipPath.svelte +71 -21
  36. package/dist/components/ClipPath.svelte.d.ts +40 -27
  37. package/dist/components/ColorRamp.svelte +75 -9
  38. package/dist/components/ColorRamp.svelte.d.ts +37 -19
  39. package/dist/components/ComputedStyles.svelte +17 -5
  40. package/dist/components/ComputedStyles.svelte.d.ts +11 -19
  41. package/dist/components/Connector.svelte +149 -0
  42. package/dist/components/Connector.svelte.d.ts +51 -0
  43. package/dist/components/Dagre.svelte +211 -122
  44. package/dist/components/Dagre.svelte.d.ts +119 -56
  45. package/dist/components/ForceSimulation.svelte +215 -90
  46. package/dist/components/ForceSimulation.svelte.d.ts +82 -35
  47. package/dist/components/Frame.svelte +33 -13
  48. package/dist/components/Frame.svelte.d.ts +13 -17
  49. package/dist/components/GeoCircle.svelte +29 -16
  50. package/dist/components/GeoCircle.svelte.d.ts +22 -24
  51. package/dist/components/GeoContext.svelte +113 -72
  52. package/dist/components/GeoContext.svelte.d.ts +49 -41
  53. package/dist/components/GeoEdgeFade.svelte +49 -13
  54. package/dist/components/GeoEdgeFade.svelte.d.ts +17 -19
  55. package/dist/components/GeoPath.svelte +157 -127
  56. package/dist/components/GeoPath.svelte.d.ts +48 -36
  57. package/dist/components/GeoPoint.svelte +52 -20
  58. package/dist/components/GeoPoint.svelte.d.ts +25 -22
  59. package/dist/components/GeoSpline.svelte +75 -26
  60. package/dist/components/GeoSpline.svelte.d.ts +29 -20
  61. package/dist/components/GeoTile.svelte +100 -49
  62. package/dist/components/GeoTile.svelte.d.ts +38 -23
  63. package/dist/components/GeoVisible.svelte +17 -9
  64. package/dist/components/GeoVisible.svelte.d.ts +10 -18
  65. package/dist/components/Graticule.svelte +30 -14
  66. package/dist/components/Graticule.svelte.d.ts +11 -52
  67. package/dist/components/Grid.svelte +230 -117
  68. package/dist/components/Grid.svelte.d.ts +71 -0
  69. package/dist/components/Group.svelte +173 -106
  70. package/dist/components/Group.svelte.d.ts +81 -0
  71. package/dist/components/Highlight.svelte +410 -308
  72. package/dist/components/Highlight.svelte.d.ts +107 -0
  73. package/dist/components/Hull.svelte +97 -46
  74. package/dist/components/Hull.svelte.d.ts +40 -30
  75. package/dist/components/Labels.svelte +127 -47
  76. package/dist/components/Labels.svelte.d.ts +70 -27
  77. package/dist/components/Legend.svelte +374 -190
  78. package/dist/components/Legend.svelte.d.ts +95 -44
  79. package/dist/components/Line.svelte +163 -125
  80. package/dist/components/Line.svelte.d.ts +75 -0
  81. package/dist/components/LinearGradient.svelte +153 -78
  82. package/dist/components/LinearGradient.svelte.d.ts +66 -31
  83. package/dist/components/Link.svelte +160 -104
  84. package/dist/components/Link.svelte.d.ts +54 -0
  85. package/dist/components/Marker.svelte +100 -39
  86. package/dist/components/Marker.svelte.d.ts +59 -27
  87. package/dist/components/MarkerWrapper.svelte +35 -0
  88. package/dist/components/MarkerWrapper.svelte.d.ts +18 -0
  89. package/dist/components/MonthPath.svelte +65 -20
  90. package/dist/components/MonthPath.svelte.d.ts +23 -17
  91. package/dist/components/MotionPath.svelte +80 -24
  92. package/dist/components/MotionPath.svelte.d.ts +46 -27
  93. package/dist/components/Pack.svelte +53 -17
  94. package/dist/components/Pack.svelte.d.ts +42 -21
  95. package/dist/components/Partition.svelte +64 -22
  96. package/dist/components/Partition.svelte.d.ts +49 -26
  97. package/dist/components/Pattern.svelte +297 -11
  98. package/dist/components/Pattern.svelte.d.ts +103 -19
  99. package/dist/components/Pie.svelte +122 -76
  100. package/dist/components/Pie.svelte.d.ts +65 -51
  101. package/dist/components/Point.svelte +20 -9
  102. package/dist/components/Point.svelte.d.ts +16 -20
  103. package/dist/components/Points.svelte +148 -137
  104. package/dist/components/Points.svelte.d.ts +45 -34
  105. package/dist/components/RadialGradient.svelte +148 -77
  106. package/dist/components/RadialGradient.svelte.d.ts +69 -31
  107. package/dist/components/Rect.svelte +121 -102
  108. package/dist/components/Rect.svelte.d.ts +36 -0
  109. package/dist/components/RectClipPath.svelte +82 -18
  110. package/dist/components/RectClipPath.svelte.d.ts +55 -0
  111. package/dist/components/Rule.svelte +107 -63
  112. package/dist/components/Rule.svelte.d.ts +40 -19
  113. package/dist/components/Sankey.svelte +132 -55
  114. package/dist/components/Sankey.svelte.d.ts +61 -31
  115. package/dist/components/Spline.svelte +281 -218
  116. package/dist/components/Spline.svelte.d.ts +95 -0
  117. package/dist/components/Text.svelte +463 -197
  118. package/dist/components/Text.svelte.d.ts +136 -0
  119. package/dist/components/Threshold.svelte +48 -16
  120. package/dist/components/Threshold.svelte.d.ts +29 -31
  121. package/dist/components/TileImage.svelte +103 -30
  122. package/dist/components/TileImage.svelte.d.ts +48 -23
  123. package/dist/components/TransformContext.svelte +365 -171
  124. package/dist/components/TransformControls.svelte +50 -26
  125. package/dist/components/TransformControls.svelte.d.ts +27 -19
  126. package/dist/components/Tree.svelte +74 -33
  127. package/dist/components/Tree.svelte.d.ts +42 -30
  128. package/dist/components/Treemap.svelte +119 -42
  129. package/dist/components/Treemap.svelte.d.ts +75 -27
  130. package/dist/components/Voronoi.svelte +153 -103
  131. package/dist/components/Voronoi.svelte.d.ts +42 -41
  132. package/dist/components/charts/ArcChart.svelte +464 -0
  133. package/dist/components/charts/ArcChart.svelte.d.ts +90 -0
  134. package/dist/components/charts/AreaChart.svelte +444 -393
  135. package/dist/components/charts/AreaChart.svelte.d.ts +61 -0
  136. package/dist/components/charts/BarChart.svelte +463 -389
  137. package/dist/components/charts/BarChart.svelte.d.ts +76 -0
  138. package/dist/components/charts/ChartAnnotations.svelte +37 -0
  139. package/dist/components/charts/ChartAnnotations.svelte.d.ts +10 -0
  140. package/dist/components/charts/DefaultTooltip.svelte +60 -0
  141. package/dist/components/charts/DefaultTooltip.svelte.d.ts +10 -0
  142. package/dist/components/charts/LineChart.svelte +366 -315
  143. package/dist/components/charts/LineChart.svelte.d.ts +53 -0
  144. package/dist/components/charts/PieChart.svelte +458 -316
  145. package/dist/components/charts/PieChart.svelte.d.ts +137 -353
  146. package/dist/components/charts/ScatterChart.svelte +332 -296
  147. package/dist/components/charts/ScatterChart.svelte.d.ts +39 -0
  148. package/dist/components/charts/index.d.ts +8 -0
  149. package/dist/components/charts/index.js +7 -0
  150. package/dist/components/charts/types.d.ts +253 -0
  151. package/dist/components/charts/utils.svelte.d.ts +30 -0
  152. package/dist/components/charts/utils.svelte.js +59 -0
  153. package/dist/components/index.d.ts +76 -4
  154. package/dist/components/index.js +76 -5
  155. package/dist/components/layout/Canvas.svelte +347 -171
  156. package/dist/components/layout/Canvas.svelte.d.ts +110 -55
  157. package/dist/components/layout/Html.svelte +82 -42
  158. package/dist/components/layout/Html.svelte.d.ts +39 -28
  159. package/dist/components/layout/Layer.svelte +39 -0
  160. package/dist/components/layout/Layer.svelte.d.ts +17 -0
  161. package/dist/components/layout/Svg.svelte +122 -70
  162. package/dist/components/layout/Svg.svelte.d.ts +53 -34
  163. package/dist/components/layout/WebGL.svelte +135 -0
  164. package/dist/components/layout/WebGL.svelte.d.ts +50 -0
  165. package/dist/components/tooltip/Tooltip.svelte +253 -78
  166. package/dist/components/tooltip/Tooltip.svelte.d.ts +149 -31
  167. package/dist/components/tooltip/TooltipContext.svelte +426 -271
  168. package/dist/components/tooltip/TooltipContext.svelte.d.ts +86 -55
  169. package/dist/components/tooltip/TooltipHeader.svelte +100 -11
  170. package/dist/components/tooltip/TooltipHeader.svelte.d.ts +43 -23
  171. package/dist/components/tooltip/TooltipItem.svelte +167 -27
  172. package/dist/components/tooltip/TooltipItem.svelte.d.ts +63 -31
  173. package/dist/components/tooltip/TooltipList.svelte +22 -3
  174. package/dist/components/tooltip/TooltipList.svelte.d.ts +6 -17
  175. package/dist/components/tooltip/TooltipSeparator.svelte +27 -1
  176. package/dist/components/tooltip/TooltipSeparator.svelte.d.ts +6 -15
  177. package/dist/components/tooltip/index.d.ts +6 -0
  178. package/dist/components/tooltip/index.js +6 -0
  179. package/dist/components/tooltip/tooltipMetaContext.d.ts +79 -0
  180. package/dist/components/tooltip/tooltipMetaContext.js +139 -0
  181. package/dist/components/types.d.ts +1 -0
  182. package/dist/components/types.js +1 -0
  183. package/dist/docs/Blockquote.svelte +3 -1
  184. package/dist/docs/Blockquote.svelte.d.ts +5 -16
  185. package/dist/docs/Code.svelte +20 -12
  186. package/dist/docs/Code.svelte.d.ts +12 -22
  187. package/dist/docs/ConnectorSweepMenuField.svelte +17 -0
  188. package/dist/docs/ConnectorSweepMenuField.svelte.d.ts +7 -0
  189. package/dist/docs/ConnectorTypeMenuField.svelte +17 -0
  190. package/dist/docs/ConnectorTypeMenuField.svelte.d.ts +7 -0
  191. package/dist/docs/CurveMenuField.svelte +14 -3
  192. package/dist/docs/CurveMenuField.svelte.d.ts +9 -18
  193. package/dist/docs/GeoDebug.svelte +47 -42
  194. package/dist/docs/GeoDebug.svelte.d.ts +4 -16
  195. package/dist/docs/Header1.svelte +4 -2
  196. package/dist/docs/Header1.svelte.d.ts +5 -18
  197. package/dist/docs/Json.svelte +11 -3
  198. package/dist/docs/Json.svelte.d.ts +9 -17
  199. package/dist/docs/Layout.svelte +10 -7
  200. package/dist/docs/Layout.svelte.d.ts +5 -15
  201. package/dist/docs/Link.svelte +7 -3
  202. package/dist/docs/Link.svelte.d.ts +5 -27
  203. package/dist/docs/PathDataMenuField.svelte +14 -10
  204. package/dist/docs/PathDataMenuField.svelte.d.ts +8 -18
  205. package/dist/docs/Preview.svelte +20 -7
  206. package/dist/docs/Preview.svelte.d.ts +12 -22
  207. package/dist/docs/TilesetField.svelte +20 -19
  208. package/dist/docs/TilesetField.svelte.d.ts +6 -19
  209. package/dist/docs/TransformDebug.svelte +5 -6
  210. package/dist/docs/TransformDebug.svelte.d.ts +18 -14
  211. package/dist/docs/ViewSourceButton.svelte +7 -4
  212. package/dist/docs/ViewSourceButton.svelte.d.ts +8 -18
  213. package/dist/types/d3-shape-extentions.d.ts +7 -0
  214. package/dist/utils/afterTick.d.ts +5 -0
  215. package/dist/utils/afterTick.js +8 -0
  216. package/dist/utils/arcText.svelte.d.ts +57 -0
  217. package/dist/utils/arcText.svelte.js +262 -0
  218. package/dist/utils/array.d.ts +9 -1
  219. package/dist/utils/array.js +13 -0
  220. package/dist/utils/attributes.d.ts +29 -0
  221. package/dist/utils/attributes.js +40 -0
  222. package/dist/utils/canvas.js +47 -10
  223. package/dist/utils/chart.d.ts +78 -0
  224. package/dist/utils/chart.js +512 -0
  225. package/dist/utils/color.d.ts +1 -0
  226. package/dist/utils/color.js +8 -0
  227. package/dist/utils/common.d.ts +3 -5
  228. package/dist/utils/common.js +3 -2
  229. package/dist/utils/connectorUtils.d.ts +21 -0
  230. package/dist/utils/connectorUtils.js +111 -0
  231. package/dist/utils/createId.d.ts +7 -0
  232. package/dist/utils/createId.js +9 -0
  233. package/dist/utils/debug.d.ts +1 -0
  234. package/dist/utils/debug.js +84 -0
  235. package/dist/utils/filterObject.d.ts +9 -0
  236. package/dist/utils/filterObject.js +12 -0
  237. package/dist/utils/graph/dagre.d.ts +34 -0
  238. package/dist/utils/graph/dagre.js +78 -0
  239. package/dist/utils/graph/dagre.test.d.ts +1 -0
  240. package/dist/utils/{graph.test.js → graph/dagre.test.js} +19 -33
  241. package/dist/utils/graph/sankey.d.ts +28 -0
  242. package/dist/utils/{graph.js → graph/sankey.js} +13 -41
  243. package/dist/utils/index.d.ts +3 -1
  244. package/dist/utils/index.js +3 -1
  245. package/dist/utils/key.svelte.d.ts +3 -0
  246. package/dist/utils/key.svelte.js +11 -0
  247. package/dist/utils/legendPayload.d.ts +7 -0
  248. package/dist/utils/legendPayload.js +8 -0
  249. package/dist/utils/motion.svelte.d.ts +140 -0
  250. package/dist/utils/motion.svelte.js +180 -0
  251. package/dist/utils/motion.test.d.ts +1 -0
  252. package/dist/utils/motion.test.js +213 -0
  253. package/dist/utils/{rect.d.ts → rect.svelte.d.ts} +7 -4
  254. package/dist/utils/rect.svelte.js +105 -0
  255. package/dist/utils/scales.svelte.d.ts +91 -0
  256. package/dist/utils/scales.svelte.js +201 -0
  257. package/dist/utils/stack.d.ts +2 -3
  258. package/dist/utils/stack.js +1 -1
  259. package/dist/utils/string.js +87 -0
  260. package/dist/utils/ticks.d.ts +9 -3
  261. package/dist/utils/ticks.js +122 -147
  262. package/dist/utils/ticks.test.d.ts +1 -0
  263. package/dist/utils/ticks.test.js +57 -0
  264. package/dist/utils/types.d.ts +81 -0
  265. package/package.json +28 -24
  266. package/dist/components/ChartContext.svelte +0 -295
  267. package/dist/components/ChartContext.svelte.d.ts +0 -139
  268. package/dist/components/TransformContext.svelte.d.ts +0 -158
  269. package/dist/stores/motionStore.d.ts +0 -30
  270. package/dist/stores/motionStore.js +0 -62
  271. package/dist/utils/graph.d.ts +0 -37
  272. package/dist/utils/rect.js +0 -107
  273. package/dist/utils/scales.d.ts +0 -66
  274. package/dist/utils/scales.js +0 -136
  275. /package/dist/{utils/graph.test.d.ts → components/charts/types.js} +0 -0
@@ -1,112 +1,311 @@
1
+ <script lang="ts" module>
2
+ import type { CommonStyleProps, Without } from '../utils/types.js';
3
+ import type { SVGAttributes } from 'svelte/elements';
4
+ import { createMotion, type MotionProp } from '../utils/motion.svelte.js';
5
+
6
+ export type TextPropsWithoutHTML = {
7
+ /**
8
+ * text value
9
+ * @default 0
10
+ */
11
+ value?: string | number;
12
+
13
+ /**
14
+ * The fill color of the text
15
+ */
16
+ fill?: string;
17
+
18
+ /**
19
+ * Maximum width to occupy (approximate as words are not split)
20
+ */
21
+ width?: number;
22
+
23
+ /**
24
+ * x position of the text
25
+ *
26
+ * @default 0
27
+ */
28
+ x?: string | number;
29
+
30
+ /**
31
+ * Initial x position of the text
32
+ *
33
+ * @default x
34
+ */
35
+ initialX?: string | number;
36
+
37
+ /**
38
+ * y position of the text
39
+ *
40
+ * @default 0
41
+ */
42
+ y?: string | number;
43
+
44
+ /**
45
+ * Initial y position of the text
46
+ *
47
+ * @default y
48
+ */
49
+ initialY?: string | number;
50
+
51
+ /**
52
+ * dx offset of the text
53
+ *
54
+ * @default 0
55
+ */
56
+ dx?: string | number;
57
+
58
+ /**
59
+ * dy offset of the text
60
+ *
61
+ * @default 0
62
+ */
63
+ dy?: string | number;
64
+
65
+ /**
66
+ * Desired "line height" of the text, implemented as y offsets
67
+ *
68
+ * @default "1em"
69
+ */
70
+ lineHeight?: string;
71
+
72
+ /**
73
+ * Cap height of the text
74
+ * @default '0.71em'
75
+ */
76
+ capHeight?: string;
77
+
78
+ /**
79
+ * Whether to scale the fontSize to accommodate the specified width
80
+ *
81
+ * @default false
82
+ */
83
+
84
+ scaleToFit?: boolean;
85
+
86
+ /**
87
+ * Horizontal text anchor
88
+ *
89
+ * @default 'start'
90
+ */
91
+ textAnchor?: 'start' | 'middle' | 'end' | 'inherit';
92
+
93
+ /**
94
+ * Vertical text anchor
95
+ *
96
+ * @default 'end'
97
+ */
98
+ verticalAnchor?: 'start' | 'middle' | 'end' | 'inherit';
99
+
100
+ /**
101
+ * The dominant baseline of the text. Useful for aligning text to the baseline of the axis.
102
+ *
103
+ * @default 'auto'
104
+ */
105
+ dominantBaseline?:
106
+ | 'auto'
107
+ | 'text-before-edge'
108
+ | 'text-after-edge'
109
+ | 'middle'
110
+ | 'hanging'
111
+ | 'ideographic'
112
+ | 'mathematical';
113
+
114
+ /**
115
+ * Rotational angle of the text
116
+ */
117
+ rotate?: number;
118
+
119
+ /**
120
+ * A bindable reference to the wrapping `<svg>` element.
121
+ *
122
+ * @bindable
123
+ */
124
+ svgRef?: SVGElement;
125
+
126
+ /**
127
+ * Props to pass to the wrapping `<svg>` element.
128
+ */
129
+ svgProps?: Omit<SVGAttributes<SVGElement>, 'children'>;
130
+
131
+ /**
132
+ * A bindable reference to the inner `<text>` element
133
+ *
134
+ * @bindable
135
+ */
136
+ ref?: SVGTextElement;
137
+ motion?: MotionProp;
138
+
139
+ /**
140
+ * Whether to enable text truncation
141
+ */
142
+ truncate?: boolean | TruncateTextOptions;
143
+
144
+ /**
145
+ * A unique identifier for the SVG path element.
146
+ * One is generated by default if not provided.
147
+ *
148
+ */
149
+ pathId?: string;
150
+
151
+ /**
152
+ * The path to render the text along.
153
+ */
154
+ path?: string | null;
155
+
156
+ /**
157
+ * Specify the offset for the start of the text along the path.
158
+ * Can be a percentage ('50%') or a length value.
159
+ *
160
+ * @default '0%'
161
+ */
162
+ startOffset?: string | number;
163
+ } & CommonStyleProps;
164
+
165
+ export type TextProps = TextPropsWithoutHTML &
166
+ Without<SVGAttributes<SVGTextElement>, TextPropsWithoutHTML>;
167
+
168
+ function getPathLength(pathRef: SVGPathElement | undefined) {
169
+ if (pathRef && typeof pathRef.getTotalLength === 'function') {
170
+ try {
171
+ return pathRef.getTotalLength();
172
+ } catch (e) {
173
+ console.error('Error getting path length:', e);
174
+ return 0;
175
+ }
176
+ }
177
+ return 0;
178
+ }
179
+ </script>
180
+
1
181
  <script lang="ts">
2
- import { onDestroy, tick } from 'svelte';
3
- import type { spring as springStore, tweened as tweenedStore } from 'svelte/motion';
4
182
  import { cls } from '@layerstack/tailwind';
5
- import { objectId } from '@layerstack/utils/object';
6
183
  import { merge } from 'lodash-es';
7
184
 
8
185
  import { getRenderContext } from './Chart.svelte';
9
- import { getCanvasContext } from './layout/Canvas.svelte';
10
- import { getStringWidth } from '../utils/string.js';
11
- import { motionStore } from '../stores/motionStore.js';
12
- import { renderText, type ComputedStylesOptions } from '../utils/canvas.js';
13
-
14
- /*
15
- TODO:
16
- - [ ] Handle styled text (use <slot /> to measure?)
17
- - [ ] Simplify by using `alignment-baseline` / `dominant-baseline`, rework multiline or drop support, etc
18
- - https://svelte.dev/repl/f12d3003313a43ba8a0be53e5786f1c7?version=3.44.3
19
- - https://observablehq.com/@neocartocnrs/cheat-sheet-on-texts-in-svg
20
-
21
- Reference:
22
- - https://bl.ocks.org/mbostock/7555321
23
- - https://github.com/airbnb/visx/blob/master/packages/visx-text/src/Text.tsx
24
- - https://airbnb.io/visx/text
25
- - https://github.com/airbnb/visx/blob/master/packages/visx-demo/src/pages/text.tsx
26
- */
27
-
28
- /** text value */
29
- export let value: string | number = 0;
30
-
31
- /** Maximum width to occupy (approximate as words are not split) */
32
- export let width: number | undefined = undefined;
33
-
34
- /** x position of the text */
35
- export let x: string | number = 0;
36
- export let initialX = x;
37
-
38
- /** y position of the text */
39
- export let y: string | number = 0;
40
- export let initialY = y;
41
-
42
- /** dx offset of the text */
43
- export let dx: string | number = 0;
44
-
45
- /** dy offset of the text */
46
- export let dy: string | number = 0;
47
-
48
- /** Desired "line height" of the text, implemented as y offsets */
49
- export let lineHeight = '1em';
50
-
51
- /** Cap height of the text */
52
- export let capHeight = '0.71em'; // Magic number from d3
53
-
54
- /** Whether to scale the fontSize to accommodate the specified width */
55
- export let scaleToFit: boolean = false;
56
-
57
- /** Horizontal text anchor */
58
- export let textAnchor: 'start' | 'middle' | 'end' | 'inherit' = 'start';
59
-
60
- /** Vertical text anchor */
61
- export let verticalAnchor: 'start' | 'middle' | 'end' | 'inherit' = 'end'; // default SVG behavior
62
-
63
- /** Rotational angle of the text */
64
- export let rotate: number | undefined = undefined;
65
-
66
- export let fill: string | undefined = undefined;
67
- export let fillOpacity: number | undefined = undefined;
68
- export let stroke: string | undefined = undefined;
69
- export let strokeWidth: number | undefined = undefined;
70
- export let opacity: number | undefined = undefined;
186
+ import { registerCanvasComponent } from './layout/Canvas.svelte';
187
+ import { getStringWidth, truncateText, type TruncateTextOptions } from '../utils/string.js';
188
+ import { getComputedStyles, renderText, type ComputedStylesOptions } from '../utils/canvas.js';
189
+
190
+ import { createKey } from '../utils/key.svelte.js';
191
+ import { layerClass } from '../utils/attributes.js';
192
+ import { degreesToRadians } from '../utils/math.js';
193
+ import { createId } from '../utils/createId.js';
194
+
195
+ const uid = $props.id();
196
+
197
+ let {
198
+ value,
199
+ x = 0,
200
+ initialX = x,
201
+ y = 0,
202
+ initialY = y,
203
+ dx = 0,
204
+ dy = 0,
205
+ lineHeight = '1em',
206
+ capHeight = '0.71em',
207
+ width,
208
+ scaleToFit = false,
209
+ textAnchor = 'start',
210
+ verticalAnchor = 'end',
211
+ dominantBaseline = 'auto',
212
+ rotate,
213
+ opacity = 1,
214
+ strokeWidth = 0,
215
+ stroke,
216
+ fill,
217
+ fillOpacity,
218
+ motion,
219
+ svgRef: svgRefProp = $bindable(),
220
+ ref: refProp = $bindable(),
221
+ class: className,
222
+ svgProps = {},
223
+ truncate = false,
224
+ path,
225
+ pathId = createId('text-path', uid),
226
+ startOffset = '0%',
227
+ transform: transformProp,
228
+ ...restProps
229
+ }: TextProps = $props();
230
+
231
+ const renderCtx = getRenderContext();
232
+
233
+ let ref = $state<SVGTextElement>();
234
+ let svgRef = $state<SVGElement>();
235
+ let pathRef = $state<SVGPathElement>();
236
+
237
+ $effect.pre(() => {
238
+ refProp = ref;
239
+ });
71
240
 
72
- let className: string | undefined = undefined;
73
- export { className as class };
241
+ $effect.pre(() => {
242
+ svgRefProp = svgRef;
243
+ });
74
244
 
75
- let wordsByLines: { words: string[]; width?: number }[] = [];
76
- let wordsWithWidth: { word: string; width: number }[] = [];
77
- let spaceWidth: number = 0;
245
+ let style = $state<CSSStyleDeclaration>(); // TODO: read from DOM?
78
246
 
79
- let style: CSSStyleDeclaration | undefined = undefined; // TODO: read from DOM?
247
+ const resolvedWidth = $derived(path ? getPathLength(pathRef) : width);
80
248
 
81
- $: words = value != null ? value.toString().split(/(?:(?!\u00A0+)\s+)/) : [];
249
+ const defaultTruncateOptions: TruncateTextOptions = $derived({
250
+ maxChars: undefined,
251
+ position: 'end',
252
+ maxWidth: resolvedWidth,
253
+ });
82
254
 
83
- $: wordsWithWidth = words.map((word) => ({
84
- word,
85
- width: getStringWidth(word, style) || 0,
86
- }));
255
+ const truncateConfig: TruncateTextOptions | boolean = $derived.by(() => {
256
+ if (typeof truncate === 'boolean') {
257
+ if (truncate) return defaultTruncateOptions;
258
+ return false;
259
+ }
260
+ return {
261
+ ...defaultTruncateOptions,
262
+ ...truncate,
263
+ };
264
+ });
87
265
 
88
- $: spaceWidth = getStringWidth('\u00A0', style) || 0;
266
+ // Handle null and convert `\n` strings back to newline characters
267
+ const rawText = $derived(value != null ? value.toString().replace(/\\n/g, '\n') : '');
89
268
 
90
- $: wordsByLines = wordsWithWidth.reduce((result: typeof wordsByLines, item) => {
91
- const currentLine = result[result.length - 1];
269
+ const textValue = $derived.by(() => {
270
+ if (!truncateConfig) return rawText;
271
+ return truncateText(rawText, truncateConfig);
272
+ });
92
273
 
93
- if (
94
- currentLine &&
95
- (width == null || scaleToFit || (currentLine.width || 0) + item.width + spaceWidth < width)
96
- ) {
97
- // Word can be added to an existing line
98
- currentLine.words.push(item.word);
99
- currentLine.width = currentLine.width || 0;
100
- currentLine.width += item.width + spaceWidth;
101
- } else {
102
- // Add first word to line or word is too long to scaleToFit on existing line
103
- const newLine = { words: [item.word], width: item.width };
104
- result.push(newLine);
105
- }
274
+ const spaceWidth = $derived(getStringWidth('\u00A0', style) || 0);
275
+
276
+ const wordsByLines = $derived.by(() => {
277
+ // Split by newlines to preserve explicit line breaks
278
+ const lines = textValue.split('\n');
279
+
280
+ return lines.flatMap((line) => {
281
+ // Split each line into words
282
+ const words = line.split(/(?:(?!\u00A0+)\s+)/);
283
+
284
+ // Handle word wrapping within each line
285
+ return words.reduce((result: { words: string[]; width?: number }[], item) => {
286
+ const currentLine = result[result.length - 1];
287
+ const itemWidth = getStringWidth(item, style) || 0;
288
+
289
+ if (
290
+ currentLine &&
291
+ (width == null || scaleToFit || (currentLine.width || 0) + itemWidth + spaceWidth < width)
292
+ ) {
293
+ // Word can be added to an existing line
294
+ currentLine.words.push(item);
295
+ currentLine.width = currentLine.width || 0;
296
+ currentLine.width += itemWidth + spaceWidth;
297
+ } else {
298
+ // Add first word to line or word is too long to scaleToFit on existing line
299
+ const newLine = { words: [item], width: itemWidth };
300
+ result.push(newLine);
301
+ }
302
+
303
+ return result;
304
+ }, []);
305
+ });
306
+ });
106
307
 
107
- return result;
108
- }, []);
109
- $: lines = wordsByLines.length;
308
+ const lineCount = $derived(wordsByLines.length);
110
309
 
111
310
  /**
112
311
  * Convert css value to pixel value (ex. 0.71em => 11.36)
@@ -114,14 +313,11 @@
114
313
  function getPixelValue(cssValue: number | string) {
115
314
  // TODO: Properly measure pixel values using DOM (handle inherited font size, zoom, etc)
116
315
 
117
- if (typeof cssValue === 'number') {
118
- return cssValue;
119
- }
316
+ if (typeof cssValue === 'number') return cssValue;
120
317
 
121
- // @ts-expect-error
122
- const [match, value, units] = cssValue.match(/([\d.]+)(\D+)/);
123
- const number = Number(value);
124
- switch (units) {
318
+ const result = cssValue.match(/([\d.]+)(\D+)/);
319
+ const number = Number(result?.[1]);
320
+ switch (result?.[2]) {
125
321
  case 'px':
126
322
  return number;
127
323
  case 'em':
@@ -132,35 +328,47 @@
132
328
  }
133
329
  }
134
330
 
135
- let startDy = 0;
136
- $: if (verticalAnchor === 'start') {
137
- startDy = getPixelValue(capHeight);
138
- } else if (verticalAnchor === 'middle') {
139
- startDy = ((lines - 1) / 2) * -getPixelValue(lineHeight) + getPixelValue(capHeight) / 2;
140
- } else {
141
- startDy = (lines - 1) * -getPixelValue(lineHeight);
142
- }
331
+ const startDy = $derived.by(() => {
332
+ if (verticalAnchor === 'start') {
333
+ return getPixelValue(capHeight);
334
+ } else if (verticalAnchor === 'middle') {
335
+ return ((lineCount - 1) / 2) * -getPixelValue(lineHeight) + getPixelValue(capHeight) / 2;
336
+ } else {
337
+ return (lineCount - 1) * -getPixelValue(lineHeight);
338
+ }
339
+ });
143
340
 
144
- let scaleTransform = '';
145
- $: if (
146
- scaleToFit &&
147
- lines > 0 &&
148
- typeof x == 'number' &&
149
- typeof y == 'number' &&
150
- typeof width == 'number'
151
- ) {
152
- const lineWidth = wordsByLines[0].width || 1;
153
- const sx = width / lineWidth;
154
- const sy = sx;
155
- const originX = x - sx * x;
156
- const originY = y - sy * y;
157
- scaleTransform = `matrix(${sx}, 0, 0, ${sy}, ${originX}, ${originY})`;
158
- } else {
159
- scaleTransform = '';
160
- }
161
- $: rotateTransform = rotate ? `rotate(${rotate}, ${x}, ${y})` : '';
341
+ const pathStartDy = $derived.by(() => {
342
+ if (verticalAnchor === 'start') {
343
+ return getPixelValue(capHeight);
344
+ } else if (verticalAnchor === 'middle') {
345
+ return (0 / 2) * -getPixelValue(lineHeight) + getPixelValue(capHeight) / 2;
346
+ } else {
347
+ return 0 * -getPixelValue(lineHeight);
348
+ }
349
+ });
350
+
351
+ const scaleTransform = $derived.by(() => {
352
+ if (
353
+ scaleToFit &&
354
+ lineCount > 0 &&
355
+ typeof x == 'number' &&
356
+ typeof y == 'number' &&
357
+ typeof width == 'number'
358
+ ) {
359
+ const lineWidth = wordsByLines[0].width || 1;
360
+ const sx = width / lineWidth;
361
+ const sy = sx;
362
+ const originX = x - sx * x;
363
+ const originY = y - sy * y;
364
+ return `matrix(${sx}, 0, 0, ${sy}, ${originX}, ${originY})`;
365
+ } else {
366
+ return '';
367
+ }
368
+ });
162
369
 
163
- $: transform = `${scaleTransform} ${rotateTransform}`;
370
+ const rotateTransform = $derived(rotate ? `rotate(${rotate}, ${x}, ${y})` : '');
371
+ const transform = $derived(transformProp ?? `${scaleTransform} ${rotateTransform}`);
164
372
 
165
373
  function isValidXOrY(xOrY: string | number | undefined) {
166
374
  return (
@@ -171,102 +379,160 @@
171
379
  );
172
380
  }
173
381
 
174
- export let spring: boolean | Parameters<typeof springStore>[1] = undefined;
175
- export let tweened: boolean | Parameters<typeof tweenedStore>[1] = undefined;
176
-
177
- let tweened_x = motionStore(initialX, { spring, tweened });
178
- let tweened_y = motionStore(initialY, { spring, tweened });
179
-
180
- $: tick().then(() => {
181
- tweened_x.set(x);
182
- tweened_y.set(y);
183
- });
184
-
185
- const renderContext = getRenderContext();
186
- const canvasContext = getCanvasContext();
382
+ const motionX = createMotion(initialX, () => x, motion);
383
+ const motionY = createMotion(initialY, () => y, motion);
187
384
 
188
385
  function render(
189
386
  ctx: CanvasRenderingContext2D,
190
387
  styleOverrides: ComputedStylesOptions | undefined
191
388
  ) {
192
- wordsByLines.forEach((line, index) => {
389
+ const effectiveLineHeight = getPixelValue(lineHeight);
390
+ const baseY = getPixelValue(motionY.current) + getPixelValue(dy) + getPixelValue(startDy);
391
+ const baseX = getPixelValue(motionX.current) + getPixelValue(dx);
392
+
393
+ ctx.save();
394
+
395
+ if (rotate !== undefined) {
396
+ const centerX = getPixelValue(x);
397
+ const centerY = getPixelValue(y);
398
+ const radians = degreesToRadians(rotate);
399
+
400
+ ctx.translate(centerX, centerY);
401
+ ctx.rotate(radians);
402
+ ctx.translate(-centerX, -centerY);
403
+ }
404
+
405
+ const styles = styleOverrides
406
+ ? merge({ styles: { strokeWidth } }, styleOverrides)
407
+ : {
408
+ styles: {
409
+ fill,
410
+ fillOpacity,
411
+ stroke,
412
+ strokeWidth,
413
+ opacity,
414
+ paintOrder: 'stroke',
415
+ textAnchor,
416
+ },
417
+ classes: cls(fill === undefined && 'fill-surface-content', className),
418
+ };
419
+
420
+ const computedStyles = getComputedStyles(ctx.canvas, styles);
421
+
422
+ ctx.font = `${computedStyles.fontSize} ${computedStyles.fontFamily}`;
423
+
424
+ const textAlign = textAnchor === 'middle' ? 'center' : textAnchor === 'end' ? 'end' : 'start';
425
+ ctx.textAlign = textAlign;
426
+
427
+ for (let index = 0; index < wordsByLines.length; index++) {
428
+ const line = wordsByLines[index];
429
+ const text = line.words.join(' ');
430
+
431
+ // no need to manually adjust x for textAnchor since ctx.textAlign handles it
432
+ const xPos = baseX;
433
+ const yPos = baseY + index * effectiveLineHeight;
434
+
193
435
  renderText(
194
436
  ctx,
195
- line.words.join(' '),
437
+ text,
196
438
  {
197
- x: getPixelValue($tweened_x) + getPixelValue(dx),
198
- y:
199
- getPixelValue($tweened_y) +
200
- getPixelValue(dy) +
201
- (index === 0 ? startDy : getPixelValue(lineHeight)),
439
+ x: xPos,
440
+ y: yPos,
202
441
  },
203
- styleOverrides
204
- ? merge({ styles: { strokeWidth } }, styleOverrides)
205
- : {
206
- styles: {
207
- fill,
208
- fillOpacity,
209
- stroke,
210
- strokeWidth,
211
- opacity,
212
- paintOrder: 'stroke',
213
- textAnchor,
214
- },
215
- classes: cls(fill === undefined && 'fill-surface-content', className),
216
- }
442
+ styles
217
443
  );
218
- });
219
- }
444
+ }
220
445
 
221
- // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
222
- $: fillKey = fill && typeof fill === 'object' ? objectId(fill) : fill;
223
- $: strokeKey = stroke && typeof stroke === 'object' ? objectId(stroke) : stroke;
224
-
225
- $: if (renderContext === 'canvas') {
226
- // Redraw when props change
227
- value &&
228
- $tweened_x &&
229
- $tweened_y &&
230
- fillKey &&
231
- strokeKey &&
232
- strokeWidth &&
233
- opacity &&
234
- className;
235
- canvasContext.invalidate();
446
+ ctx.restore();
236
447
  }
237
448
 
238
- let canvasUnregister: ReturnType<typeof canvasContext.register>;
239
- $: if (renderContext === 'canvas') {
240
- canvasUnregister = canvasContext.register({ name: 'Text', render });
449
+ // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
450
+ const fillKey = createKey(() => fill);
451
+ const strokeKey = createKey(() => stroke);
452
+
453
+ if (renderCtx === 'canvas') {
454
+ registerCanvasComponent({
455
+ name: 'Text',
456
+ render,
457
+ deps: () => [
458
+ value,
459
+ motionX.current,
460
+ motionY.current,
461
+ fillKey.current,
462
+ strokeKey.current,
463
+ strokeWidth,
464
+ opacity,
465
+ className,
466
+ truncateConfig,
467
+ rotate,
468
+ lineHeight,
469
+ textAnchor,
470
+ verticalAnchor,
471
+ ],
472
+ });
241
473
  }
242
-
243
- onDestroy(() => {
244
- if (renderContext === 'canvas') {
245
- canvasUnregister();
246
- }
247
- });
248
474
  </script>
249
475
 
250
- {#if renderContext === 'svg'}
476
+ {#if renderCtx === 'svg'}
251
477
  <!-- `overflow: visible` allow contents to be shown outside element -->
252
478
  <!-- `paint-order: stroke` supports stroke outlining text -->
253
- <svg x={dx} y={dy} class="overflow-visible [paint-order:stroke]">
254
- {#if isValidXOrY(x) && isValidXOrY(y)}
479
+ <svg
480
+ x={dx}
481
+ y={dy}
482
+ {...svgProps}
483
+ class={cls(layerClass('text-svg'), 'overflow-visible [paint-order:stroke]', svgProps?.class)}
484
+ bind:this={svgRef}
485
+ >
486
+ {#if path}
487
+ <defs>
488
+ {#key path}
489
+ <path bind:this={pathRef} id={pathId} d={path} />
490
+ {/key}
491
+ </defs>
492
+ <text
493
+ bind:this={ref}
494
+ {dy}
495
+ {...restProps}
496
+ {fill}
497
+ fill-opacity={fillOpacity}
498
+ {stroke}
499
+ stroke-width={strokeWidth}
500
+ {opacity}
501
+ transform={transformProp}
502
+ class={cls(layerClass('text'), fill === undefined && 'fill-surface-content', className)}
503
+ >
504
+ <textPath
505
+ style="text-anchor: {textAnchor};"
506
+ dominant-baseline={dominantBaseline}
507
+ href="#{pathId}"
508
+ {startOffset}
509
+ class={cls(layerClass('text-path'))}
510
+ >
511
+ {wordsByLines.map((line) => line.words.join(' ')).join()}
512
+ </textPath>
513
+ </text>
514
+ {:else if isValidXOrY(x) && isValidXOrY(y)}
255
515
  <text
256
- x={$tweened_x}
257
- y={$tweened_y}
516
+ bind:this={ref}
517
+ x={motionX.current}
518
+ y={motionY.current}
258
519
  {transform}
259
520
  text-anchor={textAnchor}
260
- {...$$restProps}
521
+ dominant-baseline={dominantBaseline}
522
+ {...restProps}
261
523
  {fill}
262
524
  fill-opacity={fillOpacity}
263
525
  {stroke}
264
526
  stroke-width={strokeWidth}
265
527
  {opacity}
266
- class={cls(fill === undefined && 'fill-surface-content', className)}
528
+ class={cls(layerClass('text'), fill === undefined && 'fill-surface-content', className)}
267
529
  >
268
530
  {#each wordsByLines as line, index}
269
- <tspan x={$tweened_x} dy={index === 0 ? startDy : lineHeight}>
531
+ <tspan
532
+ x={motionX.current}
533
+ dy={index === 0 ? startDy : lineHeight}
534
+ class={layerClass('text-tspan')}
535
+ >
270
536
  {line.words.join(' ')}
271
537
  </tspan>
272
538
  {/each}