tekivex-ui 2.2.0 → 2.5.1

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 (129) hide show
  1. package/dist/TkxForm-BWK4LqY3.js +1461 -0
  2. package/dist/TkxForm-ljQjX7KD.cjs +15 -0
  3. package/dist/charts.cjs +1 -0
  4. package/dist/charts.js +480 -0
  5. package/dist/headless.cjs +1 -0
  6. package/dist/headless.js +258 -0
  7. package/dist/index-BINBzXuY.cjs +2 -0
  8. package/dist/index-eT_U4qB2.js +512 -0
  9. package/dist/index.cjs +63 -14
  10. package/dist/index.d.ts +33 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +11396 -6336
  13. package/dist/src/charts/TkxAreaChart.d.ts +29 -0
  14. package/dist/src/charts/TkxAreaChart.d.ts.map +1 -0
  15. package/dist/src/charts/TkxBarChart.d.ts +28 -0
  16. package/dist/src/charts/TkxBarChart.d.ts.map +1 -0
  17. package/dist/src/charts/TkxDonutChart.d.ts +23 -0
  18. package/dist/src/charts/TkxDonutChart.d.ts.map +1 -0
  19. package/dist/src/charts/TkxLineChart.d.ts +35 -0
  20. package/dist/src/charts/TkxLineChart.d.ts.map +1 -0
  21. package/dist/src/charts/TkxPieChart.d.ts +19 -0
  22. package/dist/src/charts/TkxPieChart.d.ts.map +1 -0
  23. package/dist/src/charts/TkxRadarChart.d.ts +20 -0
  24. package/dist/src/charts/TkxRadarChart.d.ts.map +1 -0
  25. package/dist/src/charts/TkxScatterChart.d.ts +28 -0
  26. package/dist/src/charts/TkxScatterChart.d.ts.map +1 -0
  27. package/dist/src/charts/index.d.ts +15 -0
  28. package/dist/src/charts/index.d.ts.map +1 -0
  29. package/dist/src/charts/shared.d.ts +33 -0
  30. package/dist/src/charts/shared.d.ts.map +1 -0
  31. package/dist/src/components/TkxAffix.d.ts +10 -0
  32. package/dist/src/components/TkxAffix.d.ts.map +1 -0
  33. package/dist/src/components/TkxAnchor.d.ts +14 -0
  34. package/dist/src/components/TkxAnchor.d.ts.map +1 -0
  35. package/dist/src/components/TkxCascader.d.ts +16 -0
  36. package/dist/src/components/TkxCascader.d.ts.map +1 -0
  37. package/dist/src/components/TkxDataGrid.d.ts +19 -1
  38. package/dist/src/components/TkxDataGrid.d.ts.map +1 -1
  39. package/dist/src/components/TkxForm.d.ts +33 -3
  40. package/dist/src/components/TkxForm.d.ts.map +1 -1
  41. package/dist/src/components/TkxImage.d.ts.map +1 -1
  42. package/dist/src/components/TkxList.d.ts +24 -0
  43. package/dist/src/components/TkxList.d.ts.map +1 -0
  44. package/dist/src/components/TkxMentions.d.ts +15 -0
  45. package/dist/src/components/TkxMentions.d.ts.map +1 -0
  46. package/dist/src/components/TkxQRCode.d.ts +11 -0
  47. package/dist/src/components/TkxQRCode.d.ts.map +1 -0
  48. package/dist/src/components/TkxResult.d.ts +11 -0
  49. package/dist/src/components/TkxResult.d.ts.map +1 -0
  50. package/dist/src/components/TkxSegmented.d.ts +16 -0
  51. package/dist/src/components/TkxSegmented.d.ts.map +1 -0
  52. package/dist/src/components/TkxSelect.d.ts +5 -1
  53. package/dist/src/components/TkxSelect.d.ts.map +1 -1
  54. package/dist/src/components/TkxTour.d.ts +15 -0
  55. package/dist/src/components/TkxTour.d.ts.map +1 -0
  56. package/dist/src/components/TkxTypography.d.ts +1 -1
  57. package/dist/src/components/TkxTypography.d.ts.map +1 -1
  58. package/dist/src/components/TkxWatermark.d.ts +12 -0
  59. package/dist/src/components/TkxWatermark.d.ts.map +1 -0
  60. package/dist/src/components/index.d.ts +10 -0
  61. package/dist/src/components/index.d.ts.map +1 -1
  62. package/dist/src/engine/tkx.d.ts +38 -0
  63. package/dist/src/engine/tkx.d.ts.map +1 -1
  64. package/dist/src/headless/index.d.ts +18 -0
  65. package/dist/src/headless/index.d.ts.map +1 -0
  66. package/dist/src/headless/useControllable.d.ts +20 -0
  67. package/dist/src/headless/useControllable.d.ts.map +1 -0
  68. package/dist/src/headless/useDebounce.d.ts +14 -0
  69. package/dist/src/headless/useDebounce.d.ts.map +1 -0
  70. package/dist/src/headless/useDisclosure.d.ts +20 -0
  71. package/dist/src/headless/useDisclosure.d.ts.map +1 -0
  72. package/dist/src/headless/useFormState.d.ts +44 -0
  73. package/dist/src/headless/useFormState.d.ts.map +1 -0
  74. package/dist/src/headless/useIntersectionObserver.d.ts +23 -0
  75. package/dist/src/headless/useIntersectionObserver.d.ts.map +1 -0
  76. package/dist/src/headless/useListSelection.d.ts +29 -0
  77. package/dist/src/headless/useListSelection.d.ts.map +1 -0
  78. package/dist/src/headless/useLocalStorage.d.ts +9 -0
  79. package/dist/src/headless/useLocalStorage.d.ts.map +1 -0
  80. package/dist/src/headless/useMediaQuery.d.ts +21 -0
  81. package/dist/src/headless/useMediaQuery.d.ts.map +1 -0
  82. package/dist/src/headless/useRovingTabIndex.d.ts +33 -0
  83. package/dist/src/headless/useRovingTabIndex.d.ts.map +1 -0
  84. package/dist/src/headless/useThrottle.d.ts +9 -0
  85. package/dist/src/headless/useThrottle.d.ts.map +1 -0
  86. package/dist/src/i18n/index.d.ts +47 -2
  87. package/dist/src/i18n/index.d.ts.map +1 -1
  88. package/dist/src/themes/index.d.ts.map +1 -1
  89. package/package.json +61 -7
  90. package/src/charts/TkxAreaChart.tsx +119 -0
  91. package/src/charts/TkxBarChart.tsx +115 -0
  92. package/src/charts/TkxDonutChart.tsx +145 -0
  93. package/src/charts/TkxLineChart.tsx +135 -0
  94. package/src/charts/TkxPieChart.tsx +82 -0
  95. package/src/charts/TkxRadarChart.tsx +90 -0
  96. package/src/charts/TkxScatterChart.tsx +102 -0
  97. package/src/charts/index.ts +20 -0
  98. package/src/charts/shared.ts +45 -0
  99. package/src/components/TkxAffix.tsx +138 -0
  100. package/src/components/TkxAnchor.tsx +193 -0
  101. package/src/components/TkxCascader.tsx +276 -0
  102. package/src/components/TkxDataGrid.tsx +557 -230
  103. package/src/components/TkxForm.tsx +67 -15
  104. package/src/components/TkxImage.tsx +1 -1
  105. package/src/components/TkxList.tsx +242 -0
  106. package/src/components/TkxMentions.tsx +210 -0
  107. package/src/components/TkxQRCode.tsx +174 -0
  108. package/src/components/TkxResult.tsx +128 -0
  109. package/src/components/TkxSegmented.tsx +169 -0
  110. package/src/components/TkxSelect.tsx +209 -6
  111. package/src/components/TkxTour.tsx +239 -0
  112. package/src/components/TkxTypography.tsx +4 -2
  113. package/src/components/TkxWatermark.tsx +143 -0
  114. package/src/components/index.ts +10 -0
  115. package/src/engine/tkx.ts +122 -3
  116. package/src/headless/index.ts +44 -0
  117. package/src/headless/useControllable.ts +40 -0
  118. package/src/headless/useDebounce.ts +35 -0
  119. package/src/headless/useDisclosure.ts +28 -0
  120. package/src/headless/useFormState.ts +111 -0
  121. package/src/headless/useIntersectionObserver.ts +51 -0
  122. package/src/headless/useListSelection.ts +101 -0
  123. package/src/headless/useLocalStorage.ts +46 -0
  124. package/src/headless/useMediaQuery.ts +37 -0
  125. package/src/headless/useRovingTabIndex.ts +84 -0
  126. package/src/headless/useThrottle.ts +46 -0
  127. package/src/i18n/I18nProvider.tsx +1 -1
  128. package/src/i18n/index.ts +678 -38
  129. package/src/themes/index.ts +31 -4
@@ -0,0 +1,276 @@
1
+ import { useState, useRef, useEffect, useCallback } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { useTheme } from '../themes';
4
+ import { sanitizeString } from '../engine/security';
5
+ import { useReducedMotion } from '../hooks';
6
+ import { tkx } from '../engine/tkx';
7
+
8
+ // ── Types ────────────────────────────────────────────────────────────────────
9
+
10
+ export interface CascaderOption {
11
+ value: string;
12
+ label: string;
13
+ children?: CascaderOption[];
14
+ disabled?: boolean;
15
+ }
16
+
17
+ export interface TkxCascaderProps {
18
+ options: CascaderOption[];
19
+ value?: string[];
20
+ onChange?: (value: string[], selectedOptions: CascaderOption[]) => void;
21
+ placeholder?: string;
22
+ label?: string;
23
+ multiple?: boolean;
24
+ }
25
+
26
+ // ── Helpers ──────────────────────────────────────────────────────────────────
27
+
28
+ function getColumns(
29
+ options: CascaderOption[],
30
+ selected: string[],
31
+ ): CascaderOption[][] {
32
+ const columns: CascaderOption[][] = [options];
33
+ let current = options;
34
+ for (const val of selected) {
35
+ const found = current.find((o) => o.value === val);
36
+ if (found?.children?.length) {
37
+ columns.push(found.children);
38
+ current = found.children;
39
+ } else {
40
+ break;
41
+ }
42
+ }
43
+ return columns;
44
+ }
45
+
46
+ function getSelectedOptions(
47
+ options: CascaderOption[],
48
+ values: string[],
49
+ ): CascaderOption[] {
50
+ const result: CascaderOption[] = [];
51
+ let current = options;
52
+ for (const val of values) {
53
+ const found = current.find((o) => o.value === val);
54
+ if (found) {
55
+ result.push(found);
56
+ current = found.children ?? [];
57
+ } else break;
58
+ }
59
+ return result;
60
+ }
61
+
62
+ function getLabelPath(options: CascaderOption[], values: string[]): string {
63
+ return getSelectedOptions(options, values)
64
+ .map((o) => sanitizeString(o.label))
65
+ .join(' / ');
66
+ }
67
+
68
+ // ── Component ────────────────────────────────────────────────────────────────
69
+
70
+ export function TkxCascader({
71
+ options,
72
+ value = [],
73
+ onChange,
74
+ placeholder = 'Select...',
75
+ label,
76
+ multiple = false,
77
+ }: TkxCascaderProps) {
78
+ const theme = useTheme();
79
+ const reducedMotion = useReducedMotion();
80
+ const triggerRef = useRef<HTMLButtonElement>(null);
81
+ const dropdownRef = useRef<HTMLDivElement>(null);
82
+ const [open, setOpen] = useState(false);
83
+ const [hoverPath, setHoverPath] = useState<string[]>(value);
84
+ const [dropdownPos, setDropdownPos] = useState({ top: 0, left: 0, width: 0 });
85
+
86
+ const safeLabel = label ? sanitizeString(label) : undefined;
87
+ const safePlaceholder = sanitizeString(placeholder);
88
+ const displayText = value.length > 0 ? getLabelPath(options, value) : '';
89
+
90
+ // Position dropdown
91
+ useEffect(() => {
92
+ if (!open || !triggerRef.current) return;
93
+ const rect = triggerRef.current.getBoundingClientRect();
94
+ setDropdownPos({
95
+ top: rect.bottom + 4 + window.scrollY,
96
+ left: rect.left + window.scrollX,
97
+ width: Math.max(rect.width, 200),
98
+ });
99
+ }, [open]);
100
+
101
+ // Close on outside click
102
+ useEffect(() => {
103
+ if (!open) return;
104
+ const handler = (e: MouseEvent) => {
105
+ if (
106
+ !triggerRef.current?.contains(e.target as Node) &&
107
+ !dropdownRef.current?.contains(e.target as Node)
108
+ ) {
109
+ setOpen(false);
110
+ }
111
+ };
112
+ document.addEventListener('mousedown', handler);
113
+ return () => document.removeEventListener('mousedown', handler);
114
+ }, [open]);
115
+
116
+ const handleSelect = useCallback(
117
+ (colIdx: number, opt: CascaderOption) => {
118
+ if (opt.disabled) return;
119
+ const newPath = [...hoverPath.slice(0, colIdx), opt.value];
120
+ setHoverPath(newPath);
121
+
122
+ // If leaf node (no children), commit selection
123
+ if (!opt.children?.length) {
124
+ const selected = getSelectedOptions(options, newPath);
125
+ onChange?.(newPath, selected);
126
+ if (!multiple) setOpen(false);
127
+ }
128
+ },
129
+ [hoverPath, options, onChange, multiple],
130
+ );
131
+
132
+ const handleKeyDown = useCallback(
133
+ (e: React.KeyboardEvent) => {
134
+ if (e.key === 'Escape') {
135
+ setOpen(false);
136
+ triggerRef.current?.focus();
137
+ } else if (e.key === 'Enter' || e.key === ' ') {
138
+ if (!open) {
139
+ e.preventDefault();
140
+ setOpen(true);
141
+ }
142
+ }
143
+ },
144
+ [open],
145
+ );
146
+
147
+ const columns = getColumns(options, hoverPath);
148
+
149
+ const dropdown = open
150
+ ? createPortal(
151
+ <div
152
+ ref={dropdownRef}
153
+ role="tree"
154
+ aria-label={safeLabel ?? 'Cascader options'}
155
+ className={tkx('flex rounded-lg border overflow-hidden')}
156
+ style={{
157
+ position: 'absolute',
158
+ zIndex: 9999,
159
+ top: dropdownPos.top,
160
+ left: dropdownPos.left,
161
+ backgroundColor: theme.surface,
162
+ borderColor: theme.border,
163
+ boxShadow: `0 4px 16px ${theme.bg}80`,
164
+ animation: reducedMotion ? 'none' : 'tkxFadeIn 0.15s ease',
165
+ }}
166
+ >
167
+ {columns.map((col, colIdx) => (
168
+ <ul
169
+ key={colIdx}
170
+ role="group"
171
+ className={tkx('m-0 p-0 overflow-auto')}
172
+ style={{
173
+ listStyle: 'none',
174
+ minWidth: 160,
175
+ maxHeight: 260,
176
+ borderRight: colIdx < columns.length - 1 ? `1px solid ${theme.border}` : 'none',
177
+ }}
178
+ >
179
+ {col.map((opt) => {
180
+ const isSelected = hoverPath[colIdx] === opt.value;
181
+ const safeOptLabel = sanitizeString(opt.label);
182
+ return (
183
+ <li
184
+ key={opt.value}
185
+ role="treeitem"
186
+ aria-selected={isSelected}
187
+ aria-disabled={opt.disabled || undefined}
188
+ aria-expanded={opt.children?.length ? isSelected : undefined}
189
+ className={tkx('flex items-center justify-between px-3 py-2 cursor-pointer text-sm')}
190
+ style={{
191
+ backgroundColor: isSelected ? theme.surfaceAlt : 'transparent',
192
+ color: opt.disabled ? theme.textMuted : theme.text,
193
+ opacity: opt.disabled ? 0.5 : 1,
194
+ cursor: opt.disabled ? 'not-allowed' : 'pointer',
195
+ }}
196
+ onClick={() => handleSelect(colIdx, opt)}
197
+ onMouseEnter={() => {
198
+ if (!opt.disabled) {
199
+ setHoverPath((prev) => [...prev.slice(0, colIdx), opt.value]);
200
+ }
201
+ }}
202
+ >
203
+ <span>{safeOptLabel}</span>
204
+ {opt.children && opt.children.length > 0 && (
205
+ <svg
206
+ width="12"
207
+ height="12"
208
+ viewBox="0 0 24 24"
209
+ fill="currentColor"
210
+ aria-hidden="true"
211
+ style={{ color: theme.textMuted, flexShrink: 0 }}
212
+ >
213
+ <path d="M10 6l6 6-6 6V6z" />
214
+ </svg>
215
+ )}
216
+ </li>
217
+ );
218
+ })}
219
+ </ul>
220
+ ))}
221
+ </div>,
222
+ document.body,
223
+ )
224
+ : null;
225
+
226
+ return (
227
+ <div className={tkx('relative font-sans')} onKeyDown={handleKeyDown}>
228
+ {safeLabel && (
229
+ <label
230
+ className={tkx('block text-sm font-medium mb-1')}
231
+ style={{ color: theme.text }}
232
+ >
233
+ {safeLabel}
234
+ </label>
235
+ )}
236
+
237
+ <button
238
+ ref={triggerRef}
239
+ type="button"
240
+ role="combobox"
241
+ aria-expanded={open}
242
+ aria-haspopup="tree"
243
+ aria-label={safeLabel ?? 'Cascader'}
244
+ className={tkx('w-full flex items-center justify-between rounded-lg border px-3 py-2 text-sm cursor-pointer')}
245
+ style={{
246
+ backgroundColor: theme.surface,
247
+ borderColor: open ? theme.primary : theme.border,
248
+ color: displayText ? theme.text : theme.textMuted,
249
+ outline: 'none',
250
+ minHeight: 38,
251
+ textAlign: 'left',
252
+ }}
253
+ onClick={() => setOpen((v) => !v)}
254
+ >
255
+ <span className={tkx('truncate')}>{displayText || safePlaceholder}</span>
256
+ <svg
257
+ width="16"
258
+ height="16"
259
+ viewBox="0 0 24 24"
260
+ fill="currentColor"
261
+ aria-hidden="true"
262
+ style={{
263
+ color: theme.textMuted,
264
+ transform: open ? 'rotate(180deg)' : 'rotate(0deg)',
265
+ transition: reducedMotion ? 'none' : 'transform 0.2s ease',
266
+ flexShrink: 0,
267
+ }}
268
+ >
269
+ <path d="M7 10l5 5 5-5H7z" />
270
+ </svg>
271
+ </button>
272
+
273
+ {dropdown}
274
+ </div>
275
+ );
276
+ }