tekivex-ui 2.5.2 → 2.5.3

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 (79) hide show
  1. package/dist/TkxForm-DSMDo2Ol.js +388 -0
  2. package/dist/TkxForm-DwV_n-eE.cjs +12 -0
  3. package/dist/headless.cjs +1 -1
  4. package/dist/headless.js +396 -195
  5. package/dist/i18n.cjs +1 -0
  6. package/dist/i18n.js +757 -0
  7. package/dist/index.cjs +27 -22
  8. package/dist/index.d.ts +2 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +4994 -5089
  11. package/dist/quantum.cjs +91 -0
  12. package/dist/quantum.js +2312 -0
  13. package/dist/realtime.cjs +25 -0
  14. package/dist/realtime.js +944 -0
  15. package/dist/security-C369oOWH.cjs +1 -0
  16. package/dist/security-CzAmaMqa.js +97 -0
  17. package/dist/src/components/TkxDataGrid.d.ts +9 -1
  18. package/dist/src/components/TkxDataGrid.d.ts.map +1 -1
  19. package/dist/src/components/TkxDropdown.d.ts +41 -0
  20. package/dist/src/components/TkxDropdown.d.ts.map +1 -0
  21. package/dist/src/components/TkxLiveFeed.d.ts +891 -0
  22. package/dist/src/components/TkxLiveFeed.d.ts.map +1 -0
  23. package/dist/src/components/TkxLiveLog.d.ts +892 -0
  24. package/dist/src/components/TkxLiveLog.d.ts.map +1 -0
  25. package/dist/src/components/TkxLiveMetrics.d.ts +24 -0
  26. package/dist/src/components/TkxLiveMetrics.d.ts.map +1 -0
  27. package/dist/src/components/TkxPlayground.d.ts +15 -0
  28. package/dist/src/components/TkxPlayground.d.ts.map +1 -0
  29. package/dist/src/components/TkxQuantumForm.d.ts +20 -0
  30. package/dist/src/components/TkxQuantumForm.d.ts.map +1 -0
  31. package/dist/src/components/TkxRealTimeChart.d.ts +27 -0
  32. package/dist/src/components/TkxRealTimeChart.d.ts.map +1 -0
  33. package/dist/src/components/TkxTable.d.ts +11 -1
  34. package/dist/src/components/TkxTable.d.ts.map +1 -1
  35. package/dist/src/components/TkxThemeBuilder.d.ts +9 -0
  36. package/dist/src/components/TkxThemeBuilder.d.ts.map +1 -0
  37. package/dist/src/components/index.d.ts +1 -0
  38. package/dist/src/components/index.d.ts.map +1 -1
  39. package/dist/src/engine/index.d.ts +2 -0
  40. package/dist/src/engine/index.d.ts.map +1 -1
  41. package/dist/src/engine/quantum-ai.d.ts +201 -0
  42. package/dist/src/engine/quantum-ai.d.ts.map +1 -0
  43. package/dist/src/engine/tkx.d.ts.map +1 -1
  44. package/dist/src/headless/index.d.ts +6 -0
  45. package/dist/src/headless/index.d.ts.map +1 -1
  46. package/dist/src/headless/useInfiniteQuery.d.ts +35 -0
  47. package/dist/src/headless/useInfiniteQuery.d.ts.map +1 -0
  48. package/dist/src/headless/useSSE.d.ts +36 -0
  49. package/dist/src/headless/useSSE.d.ts.map +1 -0
  50. package/dist/src/headless/useWebSocket.d.ts +33 -0
  51. package/dist/src/headless/useWebSocket.d.ts.map +1 -0
  52. package/dist/src/quantum/index.d.ts +9 -0
  53. package/dist/src/quantum/index.d.ts.map +1 -0
  54. package/dist/src/realtime/index.d.ts +9 -0
  55. package/dist/src/realtime/index.d.ts.map +1 -0
  56. package/dist/{TkxForm-BWK4LqY3.js → tkx-BJ10JtMv.js} +372 -721
  57. package/dist/tkx-BeNyC0w7.cjs +4 -0
  58. package/package.json +16 -1
  59. package/src/components/TkxDataGrid.tsx +72 -0
  60. package/src/components/TkxDropdown.tsx +802 -0
  61. package/src/components/TkxLiveFeed.tsx +329 -0
  62. package/src/components/TkxLiveLog.tsx +470 -0
  63. package/src/components/TkxLiveMetrics.tsx +291 -0
  64. package/src/components/TkxPlayground.tsx +736 -0
  65. package/src/components/TkxQuantumForm.tsx +592 -0
  66. package/src/components/TkxRealTimeChart.tsx +381 -0
  67. package/src/components/TkxTable.tsx +351 -77
  68. package/src/components/TkxThemeBuilder.tsx +685 -0
  69. package/src/components/index.ts +4 -0
  70. package/src/engine/index.ts +2 -0
  71. package/src/engine/quantum-ai.ts +1045 -0
  72. package/src/engine/tkx.ts +95 -4
  73. package/src/headless/index.ts +6 -0
  74. package/src/headless/useInfiniteQuery.ts +153 -0
  75. package/src/headless/useSSE.ts +165 -0
  76. package/src/headless/useWebSocket.ts +229 -0
  77. package/src/quantum/index.ts +39 -0
  78. package/src/realtime/index.ts +21 -0
  79. 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;