tekivex-ui 2.5.2 → 2.5.4
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/TkxForm-DSMDo2Ol.js +388 -0
- package/dist/TkxForm-DwV_n-eE.cjs +12 -0
- package/dist/charts.cjs +1 -1
- package/dist/charts.js +324 -301
- package/dist/headless.cjs +1 -1
- package/dist/headless.js +396 -195
- package/dist/i18n.cjs +1 -0
- package/dist/i18n.js +757 -0
- package/dist/index.cjs +27 -22
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4994 -5089
- package/dist/quantum.cjs +91 -0
- package/dist/quantum.js +2312 -0
- package/dist/realtime.cjs +25 -0
- package/dist/realtime.js +944 -0
- package/dist/security-C369oOWH.cjs +1 -0
- package/dist/security-CzAmaMqa.js +97 -0
- package/dist/src/charts/TkxDonutChart.d.ts.map +1 -1
- package/dist/src/charts/TkxPieChart.d.ts.map +1 -1
- package/dist/src/components/TkxDataGrid.d.ts +9 -1
- package/dist/src/components/TkxDataGrid.d.ts.map +1 -1
- package/dist/src/components/TkxDropdown.d.ts +41 -0
- package/dist/src/components/TkxDropdown.d.ts.map +1 -0
- package/dist/src/components/TkxLiveFeed.d.ts +891 -0
- package/dist/src/components/TkxLiveFeed.d.ts.map +1 -0
- package/dist/src/components/TkxLiveLog.d.ts +892 -0
- package/dist/src/components/TkxLiveLog.d.ts.map +1 -0
- package/dist/src/components/TkxLiveMetrics.d.ts +24 -0
- package/dist/src/components/TkxLiveMetrics.d.ts.map +1 -0
- package/dist/src/components/TkxPlayground.d.ts +15 -0
- package/dist/src/components/TkxPlayground.d.ts.map +1 -0
- package/dist/src/components/TkxQuantumForm.d.ts +20 -0
- package/dist/src/components/TkxQuantumForm.d.ts.map +1 -0
- package/dist/src/components/TkxRealTimeChart.d.ts +27 -0
- package/dist/src/components/TkxRealTimeChart.d.ts.map +1 -0
- package/dist/src/components/TkxTable.d.ts +11 -1
- package/dist/src/components/TkxTable.d.ts.map +1 -1
- package/dist/src/components/TkxThemeBuilder.d.ts +9 -0
- package/dist/src/components/TkxThemeBuilder.d.ts.map +1 -0
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/engine/index.d.ts +2 -0
- package/dist/src/engine/index.d.ts.map +1 -1
- package/dist/src/engine/quantum-ai.d.ts +201 -0
- package/dist/src/engine/quantum-ai.d.ts.map +1 -0
- package/dist/src/engine/tkx.d.ts.map +1 -1
- package/dist/src/headless/index.d.ts +6 -0
- package/dist/src/headless/index.d.ts.map +1 -1
- package/dist/src/headless/useInfiniteQuery.d.ts +35 -0
- package/dist/src/headless/useInfiniteQuery.d.ts.map +1 -0
- package/dist/src/headless/useSSE.d.ts +36 -0
- package/dist/src/headless/useSSE.d.ts.map +1 -0
- package/dist/src/headless/useWebSocket.d.ts +33 -0
- package/dist/src/headless/useWebSocket.d.ts.map +1 -0
- package/dist/src/quantum/index.d.ts +9 -0
- package/dist/src/quantum/index.d.ts.map +1 -0
- package/dist/src/realtime/index.d.ts +9 -0
- package/dist/src/realtime/index.d.ts.map +1 -0
- package/dist/{TkxForm-BWK4LqY3.js → tkx-BJ10JtMv.js} +372 -721
- package/dist/tkx-BeNyC0w7.cjs +4 -0
- package/package.json +16 -1
- package/src/charts/TkxDonutChart.tsx +15 -2
- package/src/charts/TkxPieChart.tsx +17 -2
- package/src/components/TkxDataGrid.tsx +72 -0
- package/src/components/TkxDropdown.tsx +802 -0
- package/src/components/TkxLiveFeed.tsx +329 -0
- package/src/components/TkxLiveLog.tsx +470 -0
- package/src/components/TkxLiveMetrics.tsx +291 -0
- package/src/components/TkxPlayground.tsx +736 -0
- package/src/components/TkxQuantumForm.tsx +592 -0
- package/src/components/TkxRealTimeChart.tsx +381 -0
- package/src/components/TkxTable.tsx +351 -77
- package/src/components/TkxThemeBuilder.tsx +685 -0
- package/src/components/index.ts +4 -0
- package/src/engine/index.ts +2 -0
- package/src/engine/quantum-ai.ts +1045 -0
- package/src/engine/tkx.ts +95 -4
- package/src/headless/index.ts +6 -0
- package/src/headless/useInfiniteQuery.ts +153 -0
- package/src/headless/useSSE.ts +165 -0
- package/src/headless/useWebSocket.ts +229 -0
- package/src/quantum/index.ts +39 -0
- package/src/realtime/index.ts +21 -0
- package/dist/TkxForm-ljQjX7KD.cjs +0 -15
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useState,
|
|
3
|
+
useEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
useCallback,
|
|
6
|
+
useMemo,
|
|
7
|
+
createElement,
|
|
8
|
+
type CSSProperties,
|
|
9
|
+
} from 'react';
|
|
10
|
+
import { useTheme } from '../themes';
|
|
11
|
+
import { sanitizeString } from '../engine/security';
|
|
12
|
+
import { tkx } from '../engine/tkx';
|
|
13
|
+
|
|
14
|
+
// ── Types ────────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export interface ChartDataPoint {
|
|
17
|
+
timestamp: number;
|
|
18
|
+
value: number;
|
|
19
|
+
label?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TkxRealTimeChartProps {
|
|
23
|
+
data: ChartDataPoint[];
|
|
24
|
+
maxPoints?: number;
|
|
25
|
+
height?: number;
|
|
26
|
+
width?: number | string;
|
|
27
|
+
color?: string;
|
|
28
|
+
fill?: boolean;
|
|
29
|
+
showGrid?: boolean;
|
|
30
|
+
showLabels?: boolean;
|
|
31
|
+
showTooltip?: boolean;
|
|
32
|
+
label?: string;
|
|
33
|
+
unit?: string;
|
|
34
|
+
animate?: boolean;
|
|
35
|
+
yMin?: number;
|
|
36
|
+
yMax?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
function formatTime(ts: number): string {
|
|
42
|
+
const d = new Date(ts);
|
|
43
|
+
const hh = String(d.getHours()).padStart(2, '0');
|
|
44
|
+
const mm = String(d.getMinutes()).padStart(2, '0');
|
|
45
|
+
const ss = String(d.getSeconds()).padStart(2, '0');
|
|
46
|
+
return `${hh}:${mm}:${ss}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function formatYLabel(val: number): string {
|
|
50
|
+
if (Math.abs(val) >= 1_000_000) return `${(val / 1_000_000).toFixed(1)}M`;
|
|
51
|
+
if (Math.abs(val) >= 1_000) return `${(val / 1_000).toFixed(1)}k`;
|
|
52
|
+
if (Number.isInteger(val)) return String(val);
|
|
53
|
+
return val.toFixed(2);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── TkxRealTimeChart ─────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
export function TkxRealTimeChart({
|
|
59
|
+
data,
|
|
60
|
+
maxPoints = 60,
|
|
61
|
+
height = 200,
|
|
62
|
+
width = '100%',
|
|
63
|
+
color,
|
|
64
|
+
fill = true,
|
|
65
|
+
showGrid = true,
|
|
66
|
+
showLabels = true,
|
|
67
|
+
showTooltip = true,
|
|
68
|
+
label,
|
|
69
|
+
unit,
|
|
70
|
+
animate = true,
|
|
71
|
+
yMin,
|
|
72
|
+
yMax,
|
|
73
|
+
}: TkxRealTimeChartProps) {
|
|
74
|
+
const theme = useTheme();
|
|
75
|
+
const lineColor = color ?? theme.primary;
|
|
76
|
+
|
|
77
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
78
|
+
const [containerWidth, setContainerWidth] = useState(600);
|
|
79
|
+
const [tooltip, setTooltip] = useState<{ x: number; y: number; point: ChartDataPoint } | null>(null);
|
|
80
|
+
const [animKey, setAnimKey] = useState(0);
|
|
81
|
+
const prevDataLenRef = useRef(data.length);
|
|
82
|
+
|
|
83
|
+
// Measure container width
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (!containerRef.current) return;
|
|
86
|
+
const ro = new ResizeObserver((entries) => {
|
|
87
|
+
const entry = entries[0];
|
|
88
|
+
if (entry) setContainerWidth(entry.contentRect.width);
|
|
89
|
+
});
|
|
90
|
+
ro.observe(containerRef.current);
|
|
91
|
+
setContainerWidth(containerRef.current.offsetWidth);
|
|
92
|
+
return () => ro.disconnect();
|
|
93
|
+
}, []);
|
|
94
|
+
|
|
95
|
+
// Trim to maxPoints
|
|
96
|
+
const points = useMemo(
|
|
97
|
+
() => data.slice(-maxPoints),
|
|
98
|
+
[data, maxPoints],
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Trigger animate key when new point arrives
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (points.length > prevDataLenRef.current) {
|
|
104
|
+
setAnimKey((k) => k + 1);
|
|
105
|
+
}
|
|
106
|
+
prevDataLenRef.current = points.length;
|
|
107
|
+
}, [points.length]);
|
|
108
|
+
|
|
109
|
+
const PADDING_LEFT = showLabels ? 48 : 12;
|
|
110
|
+
const PADDING_RIGHT = 12;
|
|
111
|
+
const PADDING_TOP = label ? 28 : 12;
|
|
112
|
+
const PADDING_BOTTOM = 28;
|
|
113
|
+
|
|
114
|
+
const chartW = Math.max(containerWidth - PADDING_LEFT - PADDING_RIGHT, 1);
|
|
115
|
+
const chartH = Math.max(height - PADDING_TOP - PADDING_BOTTOM, 1);
|
|
116
|
+
|
|
117
|
+
// Scales
|
|
118
|
+
const rawMinY = points.length ? Math.min(...points.map((p) => p.value)) : 0;
|
|
119
|
+
const rawMaxY = points.length ? Math.max(...points.map((p) => p.value)) : 1;
|
|
120
|
+
const dataRange = rawMaxY - rawMinY || 1;
|
|
121
|
+
const minY = yMin !== undefined ? yMin : rawMinY - dataRange * 0.1;
|
|
122
|
+
const maxY = yMax !== undefined ? yMax : rawMaxY + dataRange * 0.1;
|
|
123
|
+
const yRange = maxY - minY || 1;
|
|
124
|
+
|
|
125
|
+
function toSvgX(i: number): number {
|
|
126
|
+
if (points.length < 2) return chartW / 2;
|
|
127
|
+
return (i / (points.length - 1)) * chartW;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function toSvgY(v: number): number {
|
|
131
|
+
return chartH - ((v - minY) / yRange) * chartH;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Paths
|
|
135
|
+
const linePath = points.length >= 2
|
|
136
|
+
? points.map((p, i) => `${i === 0 ? 'M' : 'L'}${toSvgX(i).toFixed(2)},${toSvgY(p.value).toFixed(2)}`).join(' ')
|
|
137
|
+
: '';
|
|
138
|
+
|
|
139
|
+
const areaPath = linePath
|
|
140
|
+
? `${linePath} L${toSvgX(points.length - 1).toFixed(2)},${chartH} L${toSvgX(0).toFixed(2)},${chartH} Z`
|
|
141
|
+
: '';
|
|
142
|
+
|
|
143
|
+
// Grid lines
|
|
144
|
+
const GRID_LINES = 4;
|
|
145
|
+
const gridLines: number[] = [];
|
|
146
|
+
for (let i = 0; i <= GRID_LINES; i++) {
|
|
147
|
+
gridLines.push(i);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Y labels
|
|
151
|
+
const yLabels = gridLines.map((i) => {
|
|
152
|
+
const v = minY + (i / GRID_LINES) * yRange;
|
|
153
|
+
const y = toSvgY(v);
|
|
154
|
+
return { y, text: formatYLabel(v) };
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// X labels (last 4 timestamps)
|
|
158
|
+
const xLabelCount = Math.min(4, points.length);
|
|
159
|
+
const xLabelIndices = xLabelCount > 0
|
|
160
|
+
? Array.from({ length: xLabelCount }, (_, k) =>
|
|
161
|
+
Math.round((k / Math.max(xLabelCount - 1, 1)) * (points.length - 1)),
|
|
162
|
+
)
|
|
163
|
+
: [];
|
|
164
|
+
|
|
165
|
+
// Tooltip on mouse move
|
|
166
|
+
const handleMouseMove = useCallback(
|
|
167
|
+
(e: React.MouseEvent<SVGSVGElement>) => {
|
|
168
|
+
if (!showTooltip || points.length === 0) return;
|
|
169
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
170
|
+
const mx = e.clientX - rect.left - PADDING_LEFT;
|
|
171
|
+
// Find nearest point
|
|
172
|
+
let nearest = 0;
|
|
173
|
+
let nearestDist = Infinity;
|
|
174
|
+
points.forEach((p, i) => {
|
|
175
|
+
const dx = Math.abs(toSvgX(i) - mx);
|
|
176
|
+
if (dx < nearestDist) { nearestDist = dx; nearest = i; }
|
|
177
|
+
});
|
|
178
|
+
const pt = points[nearest];
|
|
179
|
+
if (!pt) return;
|
|
180
|
+
const svgX = toSvgX(nearest) + PADDING_LEFT;
|
|
181
|
+
const svgY = toSvgY(pt.value) + PADDING_TOP;
|
|
182
|
+
setTooltip({ x: svgX, y: svgY, point: pt });
|
|
183
|
+
},
|
|
184
|
+
[points, showTooltip, PADDING_LEFT, PADDING_TOP],
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const handleMouseLeave = useCallback(() => setTooltip(null), []);
|
|
188
|
+
|
|
189
|
+
const svgW = containerWidth;
|
|
190
|
+
const svgH = height;
|
|
191
|
+
|
|
192
|
+
// Animate: clip rect that expands from right
|
|
193
|
+
const animId = `tkx-chart-clip-${animKey}`;
|
|
194
|
+
|
|
195
|
+
const wrapStyle: CSSProperties = {
|
|
196
|
+
position: 'relative',
|
|
197
|
+
width: typeof width === 'number' ? `${width}px` : width,
|
|
198
|
+
background: theme.surface,
|
|
199
|
+
border: `1px solid ${theme.border}`,
|
|
200
|
+
borderRadius: 8,
|
|
201
|
+
overflow: 'hidden',
|
|
202
|
+
boxSizing: 'border-box',
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
return createElement(
|
|
206
|
+
'div',
|
|
207
|
+
{ ref: containerRef, style: wrapStyle },
|
|
208
|
+
createElement(
|
|
209
|
+
'svg',
|
|
210
|
+
{
|
|
211
|
+
width: svgW,
|
|
212
|
+
height: svgH,
|
|
213
|
+
viewBox: `0 0 ${svgW} ${svgH}`,
|
|
214
|
+
onMouseMove: handleMouseMove,
|
|
215
|
+
onMouseLeave: handleMouseLeave,
|
|
216
|
+
style: { display: 'block', userSelect: 'none' },
|
|
217
|
+
'aria-label': label ? sanitizeString(label) : 'Real-time chart',
|
|
218
|
+
},
|
|
219
|
+
// Defs: clip for animation
|
|
220
|
+
animate && points.length >= 2 && createElement(
|
|
221
|
+
'defs',
|
|
222
|
+
null,
|
|
223
|
+
createElement(
|
|
224
|
+
'clipPath',
|
|
225
|
+
{ id: animId },
|
|
226
|
+
createElement('rect', {
|
|
227
|
+
x: 0,
|
|
228
|
+
y: 0,
|
|
229
|
+
width: chartW,
|
|
230
|
+
height: svgH,
|
|
231
|
+
},
|
|
232
|
+
createElement('animate', {
|
|
233
|
+
attributeName: 'width',
|
|
234
|
+
from: Math.max(0, chartW - chartW / Math.max(points.length - 1, 1)),
|
|
235
|
+
to: chartW,
|
|
236
|
+
dur: '0.3s',
|
|
237
|
+
fill: 'freeze',
|
|
238
|
+
}),
|
|
239
|
+
),
|
|
240
|
+
),
|
|
241
|
+
),
|
|
242
|
+
|
|
243
|
+
// Chart title
|
|
244
|
+
label && createElement(
|
|
245
|
+
'text',
|
|
246
|
+
{
|
|
247
|
+
x: PADDING_LEFT,
|
|
248
|
+
y: 18,
|
|
249
|
+
fill: theme.textMuted,
|
|
250
|
+
fontSize: 12,
|
|
251
|
+
fontWeight: 500,
|
|
252
|
+
},
|
|
253
|
+
sanitizeString(label),
|
|
254
|
+
),
|
|
255
|
+
|
|
256
|
+
// Grid lines
|
|
257
|
+
showGrid && gridLines.map((i) => {
|
|
258
|
+
const yPos = toSvgY(minY + (i / GRID_LINES) * yRange) + PADDING_TOP;
|
|
259
|
+
return createElement('line', {
|
|
260
|
+
key: `grid-${i}`,
|
|
261
|
+
x1: PADDING_LEFT,
|
|
262
|
+
y1: yPos,
|
|
263
|
+
x2: PADDING_LEFT + chartW,
|
|
264
|
+
y2: yPos,
|
|
265
|
+
stroke: theme.border,
|
|
266
|
+
strokeWidth: 1,
|
|
267
|
+
strokeDasharray: '4 4',
|
|
268
|
+
});
|
|
269
|
+
}),
|
|
270
|
+
|
|
271
|
+
// Y axis labels
|
|
272
|
+
showLabels && yLabels.map((yl, i) => createElement(
|
|
273
|
+
'text',
|
|
274
|
+
{
|
|
275
|
+
key: `yl-${i}`,
|
|
276
|
+
x: PADDING_LEFT - 4,
|
|
277
|
+
y: yl.y + PADDING_TOP + 4,
|
|
278
|
+
fill: theme.textMuted,
|
|
279
|
+
fontSize: 10,
|
|
280
|
+
textAnchor: 'end',
|
|
281
|
+
},
|
|
282
|
+
yl.text,
|
|
283
|
+
)),
|
|
284
|
+
|
|
285
|
+
// Y axis unit
|
|
286
|
+
unit && createElement(
|
|
287
|
+
'text',
|
|
288
|
+
{ x: 4, y: PADDING_TOP + 10, fill: theme.textMuted, fontSize: 9 },
|
|
289
|
+
sanitizeString(unit),
|
|
290
|
+
),
|
|
291
|
+
|
|
292
|
+
// Area fill
|
|
293
|
+
fill && areaPath && createElement('path', {
|
|
294
|
+
d: areaPath,
|
|
295
|
+
fill: `${lineColor}22`,
|
|
296
|
+
clipPath: animate ? `url(#${animId})` : undefined,
|
|
297
|
+
transform: `translate(${PADDING_LEFT}, ${PADDING_TOP})`,
|
|
298
|
+
}),
|
|
299
|
+
|
|
300
|
+
// Data line
|
|
301
|
+
linePath && createElement('path', {
|
|
302
|
+
d: linePath,
|
|
303
|
+
fill: 'none',
|
|
304
|
+
stroke: lineColor,
|
|
305
|
+
strokeWidth: 2,
|
|
306
|
+
strokeLinejoin: 'round',
|
|
307
|
+
strokeLinecap: 'round',
|
|
308
|
+
clipPath: animate ? `url(#${animId})` : undefined,
|
|
309
|
+
transform: `translate(${PADDING_LEFT}, ${PADDING_TOP})`,
|
|
310
|
+
}),
|
|
311
|
+
|
|
312
|
+
// X axis labels
|
|
313
|
+
xLabelIndices.map((idx) => {
|
|
314
|
+
const pt = points[idx];
|
|
315
|
+
if (!pt) return null;
|
|
316
|
+
return createElement(
|
|
317
|
+
'text',
|
|
318
|
+
{
|
|
319
|
+
key: `xl-${idx}`,
|
|
320
|
+
x: PADDING_LEFT + toSvgX(idx),
|
|
321
|
+
y: svgH - 8,
|
|
322
|
+
fill: theme.textMuted,
|
|
323
|
+
fontSize: 9,
|
|
324
|
+
textAnchor: 'middle',
|
|
325
|
+
},
|
|
326
|
+
formatTime(pt.timestamp),
|
|
327
|
+
);
|
|
328
|
+
}),
|
|
329
|
+
|
|
330
|
+
// Tooltip vertical line
|
|
331
|
+
tooltip && createElement('line', {
|
|
332
|
+
x1: tooltip.x,
|
|
333
|
+
y1: PADDING_TOP,
|
|
334
|
+
x2: tooltip.x,
|
|
335
|
+
y2: PADDING_TOP + chartH,
|
|
336
|
+
stroke: lineColor,
|
|
337
|
+
strokeWidth: 1,
|
|
338
|
+
strokeDasharray: '3 3',
|
|
339
|
+
opacity: 0.6,
|
|
340
|
+
}),
|
|
341
|
+
|
|
342
|
+
// Tooltip dot
|
|
343
|
+
tooltip && createElement('circle', {
|
|
344
|
+
cx: tooltip.x,
|
|
345
|
+
cy: tooltip.y,
|
|
346
|
+
r: 4,
|
|
347
|
+
fill: lineColor,
|
|
348
|
+
stroke: theme.surface,
|
|
349
|
+
strokeWidth: 2,
|
|
350
|
+
}),
|
|
351
|
+
|
|
352
|
+
// Tooltip box
|
|
353
|
+
tooltip && (() => {
|
|
354
|
+
const tx = Math.min(tooltip.x + 8, svgW - 100);
|
|
355
|
+
const ty = Math.max(tooltip.y - 36, 4);
|
|
356
|
+
const valText = `${formatYLabel(tooltip.point.value)}${unit ? unit : ''}`;
|
|
357
|
+
const tsText = formatTime(tooltip.point.timestamp);
|
|
358
|
+
return createElement(
|
|
359
|
+
'g',
|
|
360
|
+
null,
|
|
361
|
+
createElement('rect', {
|
|
362
|
+
x: tx - 4,
|
|
363
|
+
y: ty - 2,
|
|
364
|
+
width: 90,
|
|
365
|
+
height: 34,
|
|
366
|
+
rx: 4,
|
|
367
|
+
fill: theme.surfaceAlt,
|
|
368
|
+
stroke: theme.border,
|
|
369
|
+
strokeWidth: 1,
|
|
370
|
+
}),
|
|
371
|
+
createElement('text', { x: tx, y: ty + 11, fill: theme.text, fontSize: 12, fontWeight: 700 }, valText),
|
|
372
|
+
createElement('text', { x: tx, y: ty + 26, fill: theme.textMuted, fontSize: 10 }, tsText),
|
|
373
|
+
);
|
|
374
|
+
})(),
|
|
375
|
+
),
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Satisfy import requirement
|
|
380
|
+
void tkx;
|
|
381
|
+
void sanitizeString;
|