tekivex-ui 2.3.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 (92) 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 +20 -32
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +7942 -8903
  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/TkxDataGrid.d.ts +16 -2
  32. package/dist/src/components/TkxDataGrid.d.ts.map +1 -1
  33. package/dist/src/components/TkxForm.d.ts +33 -3
  34. package/dist/src/components/TkxForm.d.ts.map +1 -1
  35. package/dist/src/components/TkxImage.d.ts.map +1 -1
  36. package/dist/src/components/TkxTypography.d.ts +1 -1
  37. package/dist/src/components/TkxTypography.d.ts.map +1 -1
  38. package/dist/src/engine/tkx.d.ts.map +1 -1
  39. package/dist/src/headless/index.d.ts +18 -0
  40. package/dist/src/headless/index.d.ts.map +1 -0
  41. package/dist/src/headless/useControllable.d.ts +20 -0
  42. package/dist/src/headless/useControllable.d.ts.map +1 -0
  43. package/dist/src/headless/useDebounce.d.ts +14 -0
  44. package/dist/src/headless/useDebounce.d.ts.map +1 -0
  45. package/dist/src/headless/useDisclosure.d.ts +20 -0
  46. package/dist/src/headless/useDisclosure.d.ts.map +1 -0
  47. package/dist/src/headless/useFormState.d.ts +44 -0
  48. package/dist/src/headless/useFormState.d.ts.map +1 -0
  49. package/dist/src/headless/useIntersectionObserver.d.ts +23 -0
  50. package/dist/src/headless/useIntersectionObserver.d.ts.map +1 -0
  51. package/dist/src/headless/useListSelection.d.ts +29 -0
  52. package/dist/src/headless/useListSelection.d.ts.map +1 -0
  53. package/dist/src/headless/useLocalStorage.d.ts +9 -0
  54. package/dist/src/headless/useLocalStorage.d.ts.map +1 -0
  55. package/dist/src/headless/useMediaQuery.d.ts +21 -0
  56. package/dist/src/headless/useMediaQuery.d.ts.map +1 -0
  57. package/dist/src/headless/useRovingTabIndex.d.ts +33 -0
  58. package/dist/src/headless/useRovingTabIndex.d.ts.map +1 -0
  59. package/dist/src/headless/useThrottle.d.ts +9 -0
  60. package/dist/src/headless/useThrottle.d.ts.map +1 -0
  61. package/dist/src/i18n/index.d.ts +47 -2
  62. package/dist/src/i18n/index.d.ts.map +1 -1
  63. package/dist/src/themes/index.d.ts.map +1 -1
  64. package/package.json +61 -7
  65. package/src/charts/TkxAreaChart.tsx +119 -0
  66. package/src/charts/TkxBarChart.tsx +115 -0
  67. package/src/charts/TkxDonutChart.tsx +145 -0
  68. package/src/charts/TkxLineChart.tsx +135 -0
  69. package/src/charts/TkxPieChart.tsx +82 -0
  70. package/src/charts/TkxRadarChart.tsx +90 -0
  71. package/src/charts/TkxScatterChart.tsx +102 -0
  72. package/src/charts/index.ts +20 -0
  73. package/src/charts/shared.ts +45 -0
  74. package/src/components/TkxDataGrid.tsx +532 -268
  75. package/src/components/TkxForm.tsx +67 -15
  76. package/src/components/TkxImage.tsx +1 -1
  77. package/src/components/TkxTypography.tsx +4 -2
  78. package/src/engine/tkx.ts +60 -3
  79. package/src/headless/index.ts +44 -0
  80. package/src/headless/useControllable.ts +40 -0
  81. package/src/headless/useDebounce.ts +35 -0
  82. package/src/headless/useDisclosure.ts +28 -0
  83. package/src/headless/useFormState.ts +111 -0
  84. package/src/headless/useIntersectionObserver.ts +51 -0
  85. package/src/headless/useListSelection.ts +101 -0
  86. package/src/headless/useLocalStorage.ts +46 -0
  87. package/src/headless/useMediaQuery.ts +37 -0
  88. package/src/headless/useRovingTabIndex.ts +84 -0
  89. package/src/headless/useThrottle.ts +46 -0
  90. package/src/i18n/I18nProvider.tsx +1 -1
  91. package/src/i18n/index.ts +678 -38
  92. package/src/themes/index.ts +31 -4
@@ -0,0 +1,46 @@
1
+ import { useState, useCallback } from 'react';
2
+
3
+ /**
4
+ * Persists state to localStorage. Falls back to in-memory on parse errors or
5
+ * SSR (where localStorage is unavailable).
6
+ *
7
+ * @example
8
+ * const [theme, setTheme] = useLocalStorage('theme', 'dark');
9
+ */
10
+ export function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((prev: T) => T)) => void, () => void] {
11
+ const [storedValue, setStoredValue] = useState<T>(() => {
12
+ if (typeof window === 'undefined') return initialValue;
13
+ try {
14
+ const item = window.localStorage.getItem(key);
15
+ return item !== null ? (JSON.parse(item) as T) : initialValue;
16
+ } catch {
17
+ return initialValue;
18
+ }
19
+ });
20
+
21
+ const setValue = useCallback(
22
+ (value: T | ((prev: T) => T)) => {
23
+ setStoredValue(prev => {
24
+ const next = typeof value === 'function' ? (value as (p: T) => T)(prev) : value;
25
+ if (typeof window !== 'undefined') {
26
+ try {
27
+ window.localStorage.setItem(key, JSON.stringify(next));
28
+ } catch {
29
+ // Quota exceeded or private browsing — silently ignore
30
+ }
31
+ }
32
+ return next;
33
+ });
34
+ },
35
+ [key],
36
+ );
37
+
38
+ const removeValue = useCallback(() => {
39
+ setStoredValue(initialValue);
40
+ if (typeof window !== 'undefined') {
41
+ try { window.localStorage.removeItem(key); } catch { /* ignore */ }
42
+ }
43
+ }, [key, initialValue]);
44
+
45
+ return [storedValue, setValue, removeValue];
46
+ }
@@ -0,0 +1,37 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ /**
4
+ * Reactive media query hook. Returns true while the query matches.
5
+ *
6
+ * @example
7
+ * const isMobile = useMediaQuery('(max-width: 767px)');
8
+ * const isDark = useMediaQuery('(prefers-color-scheme: dark)');
9
+ * const isTouch = useMediaQuery('(hover: none) and (pointer: coarse)');
10
+ */
11
+ export function useMediaQuery(query: string): boolean {
12
+ const [matches, setMatches] = useState(() => {
13
+ if (typeof window === 'undefined') return false;
14
+ return window.matchMedia(query).matches;
15
+ });
16
+
17
+ useEffect(() => {
18
+ if (typeof window === 'undefined') return;
19
+ const mq = window.matchMedia(query);
20
+ setMatches(mq.matches);
21
+ const handler = (e: MediaQueryListEvent) => setMatches(e.matches);
22
+ mq.addEventListener('change', handler);
23
+ return () => mq.removeEventListener('change', handler);
24
+ }, [query]);
25
+
26
+ return matches;
27
+ }
28
+
29
+ /** Preset breakpoint hooks matching TekiVex's breakpoint scale. */
30
+ export function useBreakpoint() {
31
+ const sm = useMediaQuery('(min-width: 640px)');
32
+ const md = useMediaQuery('(min-width: 768px)');
33
+ const lg = useMediaQuery('(min-width: 1024px)');
34
+ const xl = useMediaQuery('(min-width: 1280px)');
35
+ const xxl = useMediaQuery('(min-width: 1536px)');
36
+ return { sm, md, lg, xl, xxl, isMobile: !sm, isTablet: sm && !lg, isDesktop: lg };
37
+ }
@@ -0,0 +1,84 @@
1
+ import { useState, useCallback, useRef, type KeyboardEvent } from 'react';
2
+
3
+ export interface UseRovingTabIndexOptions {
4
+ count: number;
5
+ initialIndex?: number;
6
+ orientation?: 'horizontal' | 'vertical' | 'both';
7
+ /** Wrap around at boundaries. Default: true */
8
+ loop?: boolean;
9
+ }
10
+
11
+ export interface UseRovingTabIndexReturn {
12
+ focusedIndex: number;
13
+ setFocusedIndex: (index: number) => void;
14
+ getItemProps: (index: number) => {
15
+ tabIndex: number;
16
+ onKeyDown: (e: KeyboardEvent) => void;
17
+ onFocus: () => void;
18
+ 'data-focused': boolean;
19
+ };
20
+ }
21
+
22
+ /**
23
+ * Implements the WAI-ARIA roving tabIndex pattern for composite widgets
24
+ * (toolbars, tab lists, radio groups, menu items, etc.)
25
+ *
26
+ * Only one item in the group is in the tab sequence at a time.
27
+ * Arrow keys move focus within the group.
28
+ *
29
+ * @example
30
+ * const { getItemProps } = useRovingTabIndex({ count: tabs.length });
31
+ * return tabs.map((tab, i) => (
32
+ * <button key={tab.id} {...getItemProps(i)}>{tab.label}</button>
33
+ * ));
34
+ */
35
+ export function useRovingTabIndex({
36
+ count,
37
+ initialIndex = 0,
38
+ orientation = 'horizontal',
39
+ loop = true,
40
+ }: UseRovingTabIndexOptions): UseRovingTabIndexReturn {
41
+ const [focusedIndex, setFocusedIndex] = useState(initialIndex);
42
+ const itemRefs = useRef<(HTMLElement | null)[]>([]);
43
+
44
+ const moveFocus = useCallback(
45
+ (delta: number) => {
46
+ setFocusedIndex(prev => {
47
+ let next = prev + delta;
48
+ if (loop) {
49
+ next = ((next % count) + count) % count;
50
+ } else {
51
+ next = Math.max(0, Math.min(count - 1, next));
52
+ }
53
+ // Focus the element after state update
54
+ requestAnimationFrame(() => {
55
+ itemRefs.current[next]?.focus();
56
+ });
57
+ return next;
58
+ });
59
+ },
60
+ [count, loop],
61
+ );
62
+
63
+ const getItemProps = useCallback(
64
+ (index: number) => ({
65
+ tabIndex: index === focusedIndex ? 0 : -1,
66
+ 'data-focused': index === focusedIndex,
67
+ onFocus: () => setFocusedIndex(index),
68
+ onKeyDown: (e: KeyboardEvent) => {
69
+ const isHorizontal = orientation === 'horizontal' || orientation === 'both';
70
+ const isVertical = orientation === 'vertical' || orientation === 'both';
71
+
72
+ if (isHorizontal && e.key === 'ArrowRight') { e.preventDefault(); moveFocus(1); }
73
+ if (isHorizontal && e.key === 'ArrowLeft') { e.preventDefault(); moveFocus(-1); }
74
+ if (isVertical && e.key === 'ArrowDown') { e.preventDefault(); moveFocus(1); }
75
+ if (isVertical && e.key === 'ArrowUp') { e.preventDefault(); moveFocus(-1); }
76
+ if (e.key === 'Home') { e.preventDefault(); setFocusedIndex(0); requestAnimationFrame(() => itemRefs.current[0]?.focus()); }
77
+ if (e.key === 'End') { e.preventDefault(); setFocusedIndex(count - 1); requestAnimationFrame(() => itemRefs.current[count - 1]?.focus()); }
78
+ },
79
+ }),
80
+ [focusedIndex, moveFocus, orientation, count],
81
+ );
82
+
83
+ return { focusedIndex, setFocusedIndex, getItemProps };
84
+ }
@@ -0,0 +1,46 @@
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
+
3
+ /**
4
+ * Returns a throttled version of the value — updates at most once per interval.
5
+ */
6
+ export function useThrottle<T>(value: T, interval: number): T {
7
+ const [throttledValue, setThrottledValue] = useState(value);
8
+ const lastUpdated = useRef(Date.now());
9
+
10
+ useEffect(() => {
11
+ const now = Date.now();
12
+ const remaining = interval - (now - lastUpdated.current);
13
+ if (remaining <= 0) {
14
+ lastUpdated.current = now;
15
+ setThrottledValue(value);
16
+ } else {
17
+ const timer = setTimeout(() => {
18
+ lastUpdated.current = Date.now();
19
+ setThrottledValue(value);
20
+ }, remaining);
21
+ return () => clearTimeout(timer);
22
+ }
23
+ }, [value, interval]);
24
+
25
+ return throttledValue;
26
+ }
27
+
28
+ /**
29
+ * Returns a throttled callback — executes at most once per interval.
30
+ */
31
+ export function useThrottledCallback<T extends (...args: any[]) => any>(
32
+ fn: T,
33
+ interval: number,
34
+ ): (...args: Parameters<T>) => void {
35
+ const fnRef = useRef(fn);
36
+ fnRef.current = fn;
37
+ const lastCalledRef = useRef(0);
38
+
39
+ return useCallback((...args: Parameters<T>) => {
40
+ const now = Date.now();
41
+ if (now - lastCalledRef.current >= interval) {
42
+ lastCalledRef.current = now;
43
+ fnRef.current(...args);
44
+ }
45
+ }, [interval]);
46
+ }
@@ -28,7 +28,7 @@ export function I18nProvider({
28
28
 
29
29
  return (
30
30
  <I18nContext.Provider value={value}>
31
- <div dir={value.direction} style={{ direction: value.direction }}>
31
+ <div dir={value.direction} style={{ direction: value.direction, display: 'contents' }}>
32
32
  {children}
33
33
  </div>
34
34
  </I18nContext.Provider>