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