@shohojdhara/atomix 0.5.1 → 0.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.
Files changed (145) hide show
  1. package/atomix.config.ts +45 -33
  2. package/build-tools/webpack-loader.js +5 -4
  3. package/dist/atomix.css +138 -17
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +1 -1
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/webpack-loader.js +5 -4
  8. package/dist/charts.d.ts +23 -23
  9. package/dist/charts.js +40 -37
  10. package/dist/charts.js.map +1 -1
  11. package/dist/config.d.ts +699 -0
  12. package/dist/config.js +17 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/core.d.ts +2 -2
  15. package/dist/core.js +111 -50
  16. package/dist/core.js.map +1 -1
  17. package/dist/forms.d.ts +3 -6
  18. package/dist/forms.js +2 -2
  19. package/dist/forms.js.map +1 -1
  20. package/dist/heavy.d.ts +1 -1
  21. package/dist/heavy.js +173 -111
  22. package/dist/heavy.js.map +1 -1
  23. package/dist/index.d.ts +1881 -790
  24. package/dist/index.esm.js +2713 -816
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +2693 -780
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.min.js +1 -1
  29. package/dist/index.min.js.map +1 -1
  30. package/dist/layout.js +59 -60
  31. package/dist/layout.js.map +1 -1
  32. package/dist/theme.d.ts +1390 -276
  33. package/dist/theme.js +2133 -625
  34. package/dist/theme.js.map +1 -1
  35. package/package.json +14 -9
  36. package/scripts/atomix-cli.js +15 -1
  37. package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
  38. package/scripts/cli/__tests__/detector.test.js +50 -0
  39. package/scripts/cli/__tests__/template-engine.test.js +23 -0
  40. package/scripts/cli/__tests__/test-setup.js +3 -0
  41. package/scripts/cli/commands/doctor.js +15 -3
  42. package/scripts/cli/commands/generate.js +113 -51
  43. package/scripts/cli/internal/ai-engine.js +30 -10
  44. package/scripts/cli/internal/complexity-utils.js +60 -0
  45. package/scripts/cli/internal/component-validator.js +49 -16
  46. package/scripts/cli/internal/config-loader.js +30 -20
  47. package/scripts/cli/internal/generator.js +89 -36
  48. package/scripts/cli/internal/hook-generator.js +5 -2
  49. package/scripts/cli/internal/itcss-generator.js +16 -12
  50. package/scripts/cli/templates/next-templates.js +81 -30
  51. package/scripts/cli/templates/storybook-templates.js +12 -2
  52. package/scripts/cli/utils/detector.js +45 -7
  53. package/scripts/cli/utils/diagnostics.js +78 -0
  54. package/scripts/cli/utils/telemetry.js +13 -0
  55. package/src/components/Accordion/Accordion.stories.tsx +4 -0
  56. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
  57. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +219 -0
  58. package/src/components/AtomixGlass/glass-utils.ts +1 -1
  59. package/src/components/Button/Button.tsx +114 -57
  60. package/src/components/Callout/Callout.tsx +4 -4
  61. package/src/components/Chart/ChartRenderer.tsx +1 -1
  62. package/src/components/Chart/DonutChart.tsx +11 -8
  63. package/src/components/EdgePanel/EdgePanel.tsx +119 -115
  64. package/src/components/Form/Select.tsx +4 -4
  65. package/src/components/List/List.tsx +4 -4
  66. package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
  67. package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
  68. package/src/components/ProductReview/ProductReview.tsx +4 -2
  69. package/src/components/Rating/Rating.tsx +4 -2
  70. package/src/components/SectionIntro/SectionIntro.tsx +4 -2
  71. package/src/components/Steps/Steps.tsx +1 -1
  72. package/src/components/Tabs/Tabs.tsx +5 -5
  73. package/src/components/Testimonial/Testimonial.tsx +4 -2
  74. package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
  75. package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
  76. package/src/layouts/CssGrid/CssGrid.tsx +215 -0
  77. package/src/layouts/CssGrid/index.ts +8 -0
  78. package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
  79. package/src/layouts/CssGrid/scripts/index.js +43 -0
  80. package/src/layouts/Grid/scripts/Container.js +139 -0
  81. package/src/layouts/Grid/scripts/Grid.js +184 -0
  82. package/src/layouts/Grid/scripts/GridCol.js +273 -0
  83. package/src/layouts/Grid/scripts/Row.js +154 -0
  84. package/src/layouts/Grid/scripts/index.js +48 -0
  85. package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
  86. package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
  87. package/src/lib/composables/useAccordion.ts +5 -5
  88. package/src/lib/composables/useAtomixGlass.ts +3 -3
  89. package/src/lib/composables/useBarChart.ts +2 -2
  90. package/src/lib/composables/useChart.ts +3 -2
  91. package/src/lib/composables/useChartToolbar.ts +48 -66
  92. package/src/lib/composables/useDataTable.ts +1 -1
  93. package/src/lib/composables/useDatePicker.ts +2 -2
  94. package/src/lib/composables/useEdgePanel.ts +45 -54
  95. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
  96. package/src/lib/composables/usePhotoViewer.ts +2 -3
  97. package/src/lib/composables/usePieChart.ts +1 -1
  98. package/src/lib/composables/usePopover.ts +151 -139
  99. package/src/lib/composables/useSideMenu.ts +28 -41
  100. package/src/lib/composables/useSlider.ts +2 -6
  101. package/src/lib/composables/useTooltip.ts +2 -2
  102. package/src/lib/config/index.ts +38 -323
  103. package/src/lib/config/loader.ts +419 -0
  104. package/src/lib/config/public-api.ts +43 -0
  105. package/src/lib/config/types.ts +389 -0
  106. package/src/lib/config/validator.ts +305 -0
  107. package/src/lib/theme/adapters/index.ts +1 -1
  108. package/src/lib/theme/adapters/themeAdapter.ts +358 -229
  109. package/src/lib/theme/components/ThemeToggle.tsx +276 -0
  110. package/src/lib/theme/config/configLoader.ts +351 -0
  111. package/src/lib/theme/config/loader.ts +221 -0
  112. package/src/lib/theme/core/createTheme.ts +126 -50
  113. package/src/lib/theme/core/createThemeObject.ts +7 -4
  114. package/src/lib/theme/devtools/Comparator.tsx +1 -1
  115. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  116. package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
  117. package/src/lib/theme/hooks/useThemeSwitcher.ts +164 -0
  118. package/src/lib/theme/index.ts +322 -38
  119. package/src/lib/theme/runtime/ThemeProvider.tsx +45 -11
  120. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +44 -393
  121. package/src/lib/theme/runtime/useTheme.ts +1 -0
  122. package/src/lib/theme/tokens/tokens.ts +101 -1
  123. package/src/lib/theme/types.ts +91 -0
  124. package/src/lib/theme/utils/performanceMonitor.ts +315 -0
  125. package/src/lib/theme/utils/responsive.ts +280 -0
  126. package/src/lib/theme/utils/themeUtils.ts +531 -117
  127. package/src/styles/01-settings/_index.scss +1 -0
  128. package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
  129. package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
  130. package/src/styles/02-tools/_tools.glass.scss +6 -0
  131. package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
  132. package/src/styles/06-components/_components.atomix-glass.scss +4 -4
  133. package/src/lib/composables/useBreadcrumb.ts +0 -81
  134. package/src/lib/composables/useChartInteractions.ts +0 -123
  135. package/src/lib/composables/useChartPerformance.ts +0 -347
  136. package/src/lib/composables/useDropdown.ts +0 -338
  137. package/src/lib/composables/useModal.ts +0 -110
  138. package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
  139. package/src/lib/utils/displacement-generator.ts +0 -92
  140. package/src/lib/utils/memoryMonitor.ts +0 -191
  141. package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
  142. package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
  143. package/src/styles/06-components/_components.testbutton.scss +0 -212
  144. package/src/styles/06-components/_components.testtypecheck.scss +0 -212
  145. package/src/styles/06-components/_components.typedbutton.scss +0 -212
@@ -1,338 +0,0 @@
1
- import { useState, useRef, useEffect, useCallback } from 'react';
2
- import { DROPDOWN } from '../constants/components';
3
- import type { DropdownPlacement, DropdownTrigger } from '../types/components';
4
-
5
- interface UseDropdownProps {
6
- placement?: DropdownPlacement;
7
- trigger?: DropdownTrigger;
8
- offset?: number;
9
- defaultOpen?: boolean;
10
- isOpen?: boolean;
11
- onOpenChange?: (isOpen: boolean) => void;
12
- closeOnClickOutside?: boolean;
13
- closeOnEscape?: boolean;
14
- id?: string;
15
- }
16
-
17
- interface UseDropdownReturn {
18
- isOpen: boolean;
19
- setIsOpen: (isOpen: boolean) => void;
20
- triggerRef: React.RefObject<HTMLElement | null>;
21
- menuRef: React.RefObject<HTMLElement | null>;
22
- dropdownId: string;
23
- currentPlacement: DropdownPlacement;
24
- updatePosition: () => void;
25
- }
26
-
27
- /**
28
- * Hook for managing dropdown state and position
29
- */
30
- export const useDropdown = ({
31
- placement = DROPDOWN.DEFAULTS.PLACEMENT as DropdownPlacement,
32
- trigger = DROPDOWN.DEFAULTS.TRIGGER as DropdownTrigger,
33
- offset = DROPDOWN.DEFAULTS.OFFSET,
34
- defaultOpen = false,
35
- isOpen: controlledIsOpen,
36
- onOpenChange,
37
- closeOnClickOutside = true,
38
- closeOnEscape = true,
39
- id,
40
- }: UseDropdownProps): UseDropdownReturn => {
41
- // Generate unique ID for the dropdown menu
42
- const uniqueId = useRef(`dropdown-${id || Math.random().toString(36).substring(2, 9)}`);
43
-
44
- // Setup controlled vs uncontrolled state
45
- const [uncontrolledIsOpen, setUncontrolledIsOpen] = useState(defaultOpen);
46
-
47
- // Use either controlled or uncontrolled state
48
- const isOpen = controlledIsOpen !== undefined ? controlledIsOpen : uncontrolledIsOpen;
49
-
50
- // Callback to update open state with notification to parent
51
- const setIsOpen = useCallback(
52
- (nextIsOpen: boolean) => {
53
- if (controlledIsOpen === undefined) {
54
- setUncontrolledIsOpen(nextIsOpen);
55
- }
56
-
57
- if (onOpenChange) {
58
- onOpenChange(nextIsOpen);
59
- }
60
- },
61
- [controlledIsOpen, onOpenChange]
62
- );
63
-
64
- // Refs for trigger and dropdown menu elements
65
- const triggerRef = useRef<HTMLElement>(null);
66
- const menuRef = useRef<HTMLElement>(null);
67
-
68
- // Current placement state
69
- const [currentPlacement, setCurrentPlacement] = useState(placement);
70
-
71
- // Handle click outside
72
- useEffect(() => {
73
- if (!isOpen || !closeOnClickOutside) return undefined;
74
-
75
- const handleClickOutside = (event: MouseEvent) => {
76
- if (
77
- menuRef.current &&
78
- triggerRef.current &&
79
- !menuRef.current.contains(event.target as Node) &&
80
- !triggerRef.current.contains(event.target as Node)
81
- ) {
82
- setIsOpen(false);
83
- }
84
- };
85
-
86
- document.addEventListener('mousedown', handleClickOutside);
87
-
88
- return () => {
89
- document.removeEventListener('mousedown', handleClickOutside);
90
- };
91
- }, [isOpen, closeOnClickOutside, setIsOpen]);
92
-
93
- // Handle escape key
94
- useEffect(() => {
95
- if (!isOpen || !closeOnEscape) return undefined;
96
-
97
- const handleEscapeKey = (event: KeyboardEvent) => {
98
- if (event.key === 'Escape') {
99
- setIsOpen(false);
100
- }
101
- };
102
-
103
- document.addEventListener('keydown', handleEscapeKey);
104
-
105
- return () => {
106
- document.removeEventListener('keydown', handleEscapeKey);
107
- };
108
- }, [isOpen, closeOnEscape, setIsOpen]);
109
-
110
- // Handle arrow key navigation
111
- useEffect(() => {
112
- if (!isOpen || !menuRef.current) return undefined;
113
-
114
- const handleKeyDown = (event: KeyboardEvent) => {
115
- const menu = menuRef.current;
116
- if (!menu) return;
117
-
118
- const items = Array.from(
119
- menu.querySelectorAll<HTMLElement>('[role="menuitem"]:not([disabled])')
120
- );
121
- if (items.length === 0) return;
122
-
123
- const currentIndex = items.indexOf(document.activeElement as HTMLElement);
124
-
125
- switch (event.key) {
126
- case 'ArrowDown':
127
- event.preventDefault();
128
- const nextIndex = (currentIndex + 1) % items.length;
129
- const nextItem = items[nextIndex];
130
- if (nextItem) nextItem.focus();
131
- break;
132
- case 'ArrowUp':
133
- event.preventDefault();
134
- const prevIndex = (currentIndex - 1 + items.length) % items.length;
135
- const prevItem = items[prevIndex];
136
- if (prevItem) prevItem.focus();
137
- break;
138
- case 'Home':
139
- event.preventDefault();
140
- const firstItem = items[0];
141
- if (firstItem) firstItem.focus();
142
- break;
143
- case 'End':
144
- event.preventDefault();
145
- const lastItem = items[items.length - 1];
146
- if (lastItem) lastItem.focus();
147
- break;
148
- case 'Tab':
149
- // Close dropdown on tab
150
- setIsOpen(false);
151
- break;
152
- default:
153
- break;
154
- }
155
- };
156
-
157
- document.addEventListener('keydown', handleKeyDown);
158
-
159
- return () => {
160
- document.removeEventListener('keydown', handleKeyDown);
161
- };
162
- }, [isOpen, setIsOpen]);
163
-
164
- // Focus management when dropdown opens
165
- useEffect(() => {
166
- if (isOpen && menuRef.current) {
167
- const firstItem = menuRef.current.querySelector<HTMLElement>(
168
- '[role="menuitem"]:not([disabled])'
169
- );
170
- if (firstItem) {
171
- setTimeout(() => firstItem.focus(), 0);
172
- }
173
- }
174
- }, [isOpen]);
175
-
176
- // Helper function to get the flipped placement if needed
177
- const getFlippedPlacement = useCallback(
178
- (
179
- placement: DropdownPlacement,
180
- triggerRect: DOMRect,
181
- menuRect: DOMRect,
182
- offset: number
183
- ): DropdownPlacement => {
184
- const viewportWidth = window.innerWidth;
185
- const viewportHeight = window.innerHeight;
186
-
187
- // Start with the requested placement
188
- let newPlacement = placement;
189
-
190
- // Flip vertical placement if needed
191
- if (
192
- placement.startsWith('bottom') &&
193
- triggerRect.bottom + menuRect.height + offset > viewportHeight
194
- ) {
195
- newPlacement = placement.replace('bottom', 'top') as DropdownPlacement;
196
- } else if (placement.startsWith('top') && triggerRect.top - menuRect.height - offset < 0) {
197
- newPlacement = placement.replace('top', 'bottom') as DropdownPlacement;
198
- }
199
-
200
- // Flip horizontal placement if needed
201
- if (placement.startsWith('left') && triggerRect.left - menuRect.width - offset < 0) {
202
- newPlacement = placement.replace('left', 'right') as DropdownPlacement;
203
- } else if (
204
- placement.startsWith('right') &&
205
- triggerRect.right + menuRect.width + offset > viewportWidth
206
- ) {
207
- newPlacement = placement.replace('right', 'left') as DropdownPlacement;
208
- }
209
-
210
- // Adjust alignment for top/bottom placements
211
- if (newPlacement.startsWith('top') || newPlacement.startsWith('bottom')) {
212
- if (newPlacement.endsWith('start') && triggerRect.left + menuRect.width > viewportWidth) {
213
- newPlacement = newPlacement.replace('start', 'end') as DropdownPlacement;
214
- } else if (newPlacement.endsWith('end') && triggerRect.right - menuRect.width < 0) {
215
- newPlacement = newPlacement.replace('end', 'start') as DropdownPlacement;
216
- }
217
- }
218
-
219
- return newPlacement;
220
- },
221
- []
222
- );
223
-
224
- // Helper function to calculate position based on placement
225
- const calculatePosition = useCallback(
226
- (
227
- placement: DropdownPlacement,
228
- triggerRect: DOMRect,
229
- menuRect: DOMRect,
230
- offset: number
231
- ): { top: number; left: number } => {
232
- let top = 0;
233
- let left = 0;
234
-
235
- // Vertical positioning
236
- if (placement.startsWith('bottom')) {
237
- top = triggerRect.height + offset;
238
- } else if (placement.startsWith('top')) {
239
- top = -menuRect.height - offset;
240
- } else if (placement.startsWith('left') || placement.startsWith('right')) {
241
- top = triggerRect.height / 2 - menuRect.height / 2;
242
- }
243
-
244
- // Horizontal positioning
245
- if (placement.startsWith('left')) {
246
- left = -menuRect.width - offset;
247
- } else if (placement.startsWith('right')) {
248
- left = triggerRect.width + offset;
249
- } else if (placement.endsWith('start')) {
250
- left = 0;
251
- } else if (placement.endsWith('end')) {
252
- left = triggerRect.width - menuRect.width;
253
- } else {
254
- left = triggerRect.width / 2 - menuRect.width / 2;
255
- }
256
-
257
- return { top, left };
258
- },
259
- []
260
- );
261
-
262
- // Calculate and update dropdown position
263
- const updatePosition = useCallback(() => {
264
- if (!isOpen || !triggerRef.current || !menuRef.current) return;
265
-
266
- const triggerRect = triggerRef.current.getBoundingClientRect();
267
- const menuRect = menuRef.current.getBoundingClientRect();
268
-
269
- // Get the optimal placement
270
- const newPlacement = getFlippedPlacement(placement, triggerRect, menuRect, offset);
271
-
272
- // Calculate position based on the new placement
273
- const { top, left } = calculatePosition(newPlacement, triggerRect, menuRect, offset);
274
-
275
- // Apply position
276
- menuRef.current.style.position = 'absolute';
277
- menuRef.current.style.top = `${top}px`;
278
- menuRef.current.style.left = `${left}px`;
279
-
280
- // Update placement state if it changed
281
- if (newPlacement !== currentPlacement) {
282
- setCurrentPlacement(newPlacement);
283
- }
284
- }, [isOpen, offset, placement, currentPlacement, getFlippedPlacement, calculatePosition]);
285
-
286
- // Update position when menu is opened
287
- useEffect(() => {
288
- if (!isOpen) return undefined;
289
-
290
- // Initial position update
291
- updatePosition();
292
-
293
- // Use ResizeObserver to detect size changes in the menu
294
- let resizeObserver: ResizeObserver | null = null;
295
- if (menuRef.current && typeof ResizeObserver !== 'undefined') {
296
- resizeObserver = new ResizeObserver(() => {
297
- requestAnimationFrame(updatePosition);
298
- });
299
- resizeObserver.observe(menuRef.current);
300
- }
301
-
302
- // Update position on resize/scroll
303
- const handleResize = () => {
304
- requestAnimationFrame(updatePosition);
305
- };
306
-
307
- const handleScroll = () => {
308
- requestAnimationFrame(updatePosition);
309
- };
310
-
311
- window.addEventListener('resize', handleResize);
312
- window.addEventListener('scroll', handleScroll, { passive: true });
313
-
314
- // Fallback for browsers without ResizeObserver or for dynamic content changes
315
- // Use a less frequent interval (500ms instead of 200ms)
316
- const intervalId = window.setInterval(updatePosition, 500);
317
-
318
- return () => {
319
- if (resizeObserver && menuRef.current) {
320
- resizeObserver.unobserve(menuRef.current);
321
- resizeObserver.disconnect();
322
- }
323
- window.removeEventListener('resize', handleResize);
324
- window.removeEventListener('scroll', handleScroll);
325
- window.clearInterval(intervalId);
326
- };
327
- }, [isOpen, updatePosition]);
328
-
329
- return {
330
- isOpen,
331
- setIsOpen,
332
- triggerRef,
333
- menuRef,
334
- dropdownId: uniqueId.current,
335
- currentPlacement,
336
- updatePosition,
337
- };
338
- };
@@ -1,110 +0,0 @@
1
- import { useState, useCallback, useEffect } from 'react';
2
-
3
- export interface UseModalProps {
4
- /**
5
- * Whether the modal is open
6
- */
7
- isOpen?: boolean;
8
-
9
- /**
10
- * Callback when modal state changes
11
- */
12
- onOpenChange?: (isOpen: boolean) => void;
13
-
14
- /**
15
- * Callback when modal opens
16
- */
17
- onOpen?: () => void;
18
-
19
- /**
20
- * Callback when modal closes
21
- */
22
- onClose?: () => void;
23
- }
24
-
25
- export interface UseModalReturn {
26
- /**
27
- * Current open state
28
- */
29
- isOpen: boolean;
30
-
31
- /**
32
- * Function to open the modal
33
- */
34
- open: () => void;
35
-
36
- /**
37
- * Function to close the modal
38
- */
39
- close: () => void;
40
-
41
- /**
42
- * Function to toggle the modal
43
- */
44
- toggle: () => void;
45
- }
46
-
47
- /**
48
- * Hook for managing modal state
49
- */
50
- export function useModal({
51
- isOpen: isOpenProp,
52
- onOpenChange,
53
- onOpen,
54
- onClose,
55
- }: UseModalProps = {}): UseModalReturn {
56
- // For uncontrolled usage
57
- const [isOpenState, setIsOpenState] = useState(false);
58
-
59
- // Determine if we're in controlled or uncontrolled mode
60
- const isControlled = isOpenProp !== undefined;
61
- const isOpen = isControlled ? !!isOpenProp : isOpenState;
62
-
63
- // Update internal state when prop changes (for controlled mode)
64
- useEffect(() => {
65
- if (isControlled) {
66
- setIsOpenState(!!isOpenProp);
67
- }
68
- }, [isOpenProp, isControlled]);
69
-
70
- const updateOpen = useCallback(
71
- (nextIsOpen: boolean) => {
72
- // For uncontrolled mode, update internal state
73
- if (!isControlled) {
74
- setIsOpenState(nextIsOpen);
75
- }
76
-
77
- // Call the change handler in either mode
78
- if (onOpenChange) {
79
- onOpenChange(nextIsOpen);
80
- }
81
-
82
- // Call the specific handler
83
- if (nextIsOpen && onOpen) {
84
- onOpen();
85
- } else if (!nextIsOpen && onClose) {
86
- onClose();
87
- }
88
- },
89
- [isControlled, onOpenChange, onOpen, onClose]
90
- );
91
-
92
- const open = useCallback(() => {
93
- updateOpen(true);
94
- }, [updateOpen]);
95
-
96
- const close = useCallback(() => {
97
- updateOpen(false);
98
- }, [updateOpen]);
99
-
100
- const toggle = useCallback(() => {
101
- updateOpen(!isOpen);
102
- }, [isOpen, updateOpen]);
103
-
104
- return {
105
- isOpen,
106
- open,
107
- close,
108
- toggle,
109
- };
110
- }
@@ -1,148 +0,0 @@
1
- /**
2
- * Performance Monitoring Hook
3
- *
4
- * Tracks component render times and re-render counts
5
- * for performance analysis and optimization
6
- */
7
-
8
- import { useEffect, useRef } from 'react';
9
-
10
- export interface PerformanceMetrics {
11
- /**
12
- * Component name
13
- */
14
- componentName: string;
15
- /**
16
- * Number of renders
17
- */
18
- renderCount: number;
19
- /**
20
- * Average render time in milliseconds
21
- */
22
- averageRenderTime: number;
23
- /**
24
- * Total render time in milliseconds
25
- */
26
- totalRenderTime: number;
27
- /**
28
- * Maximum render time in milliseconds
29
- */
30
- maxRenderTime: number;
31
- /**
32
- * Minimum render time in milliseconds
33
- */
34
- minRenderTime: number;
35
- }
36
-
37
- /**
38
- * Options for performance monitoring
39
- */
40
- export interface UsePerformanceMonitorOptions {
41
- /**
42
- * Component name to track
43
- */
44
- componentName: string;
45
- /**
46
- * Whether to log metrics to console (development only)
47
- */
48
- logToConsole?: boolean;
49
- /**
50
- * Threshold in milliseconds to warn about slow renders
51
- */
52
- warnThreshold?: number;
53
- /**
54
- * Callback to report metrics (e.g., to analytics)
55
- */
56
- onMetrics?: (metrics: PerformanceMetrics) => void;
57
- }
58
-
59
- /**
60
- * Hook to monitor component performance
61
- *
62
- * @param options - Performance monitoring options
63
- * @returns Performance metrics
64
- *
65
- * @example
66
- * ```tsx
67
- * function MyComponent() {
68
- * usePerformanceMonitor({
69
- * componentName: 'MyComponent',
70
- * warnThreshold: 16, // Warn if render takes > 16ms (1 frame)
71
- * });
72
- *
73
- * return <div>Content</div>;
74
- * }
75
- * ```
76
- */
77
- export function usePerformanceMonitor(options: UsePerformanceMonitorOptions) {
78
- const {
79
- componentName,
80
- logToConsole = typeof process === 'undefined' || process.env?.NODE_ENV === 'development',
81
- warnThreshold = 16,
82
- onMetrics,
83
- } = options;
84
-
85
- const metricsRef = useRef<PerformanceMetrics>({
86
- componentName,
87
- renderCount: 0,
88
- averageRenderTime: 0,
89
- totalRenderTime: 0,
90
- maxRenderTime: 0,
91
- minRenderTime: Infinity,
92
- });
93
-
94
- const renderStartRef = useRef<number>(0);
95
-
96
- useEffect(() => {
97
- // Start timing the render
98
- renderStartRef.current = performance.now();
99
- });
100
-
101
- useEffect(() => {
102
- // Calculate render time
103
- const renderTime = performance.now() - renderStartRef.current;
104
- const metrics = metricsRef.current;
105
-
106
- // Update metrics
107
- metrics.renderCount += 1;
108
- metrics.totalRenderTime += renderTime;
109
- metrics.averageRenderTime = metrics.totalRenderTime / metrics.renderCount;
110
- metrics.maxRenderTime = Math.max(metrics.maxRenderTime, renderTime);
111
- metrics.minRenderTime = Math.min(metrics.minRenderTime, renderTime);
112
-
113
- // Warn if render is slow
114
- if (renderTime > warnThreshold && logToConsole) {
115
- console.warn(
116
- `[Performance] ${componentName} render took ${renderTime.toFixed(2)}ms ` +
117
- `(threshold: ${warnThreshold}ms)`
118
- );
119
- }
120
-
121
- // Log metrics in development
122
- if (logToConsole && metrics.renderCount % 10 === 0) {
123
- console.log(`[Performance] ${componentName} metrics:`, {
124
- renderCount: metrics.renderCount,
125
- averageRenderTime: metrics.averageRenderTime.toFixed(2) + 'ms',
126
- maxRenderTime: metrics.maxRenderTime.toFixed(2) + 'ms',
127
- minRenderTime: metrics.minRenderTime.toFixed(2) + 'ms',
128
- });
129
- }
130
-
131
- // Report metrics via callback
132
- if (onMetrics) {
133
- onMetrics({ ...metrics });
134
- }
135
- });
136
-
137
- return metricsRef.current;
138
- }
139
-
140
- /**
141
- * Get all performance metrics for all monitored components
142
- * (useful for debugging and analytics)
143
- */
144
- export function getPerformanceMetrics(): PerformanceMetrics[] {
145
- // This would need to be implemented with a global store
146
- // For now, this is a placeholder
147
- return [];
148
- }
@@ -1,92 +0,0 @@
1
- export interface Vec2 {
2
- x: number;
3
- y: number;
4
- }
5
-
6
- export interface ShaderOptions {
7
- width: number;
8
- height: number;
9
- fragment: (uv: Vec2) => Vec2;
10
- }
11
-
12
- function smoothStep(a: number, b: number, t: number): number {
13
- t = Math.max(0, Math.min(1, (t - a) / (b - a)));
14
- return t * t * (3 - 2 * t);
15
- }
16
-
17
- function length(x: number, y: number): number {
18
- return Math.sqrt(x * x + y * y);
19
- }
20
-
21
- function roundedRectSDF(
22
- x: number,
23
- y: number,
24
- width: number,
25
- height: number,
26
- radius: number
27
- ): number {
28
- const qx = Math.abs(x) - width + radius;
29
- const qy = Math.abs(y) - height + radius;
30
- return Math.min(Math.max(qx, qy), 0) + length(Math.max(qx, 0), Math.max(qy, 0)) - radius;
31
- }
32
-
33
- export const fragmentShaders = {
34
- liquidGlass: (uv: Vec2): Vec2 => {
35
- const ix = uv.x - 0.5;
36
- const iy = uv.y - 0.5;
37
- const distanceToEdge = roundedRectSDF(ix, iy, 0.3, 0.2, 0.6);
38
- const displacement = smoothStep(0.8, 0, distanceToEdge - 0.15);
39
- const scaled = smoothStep(0, 1, displacement);
40
- return { x: ix * scaled + 0.5, y: iy * scaled + 0.5 };
41
- },
42
- };
43
-
44
- export class ShaderDisplacementGenerator {
45
- private canvas: HTMLCanvasElement;
46
- private context: CanvasRenderingContext2D;
47
-
48
- constructor(private options: ShaderOptions) {
49
- this.canvas = document.createElement('canvas');
50
- this.canvas.width = options.width;
51
- this.canvas.height = options.height;
52
- this.canvas.style.display = 'none';
53
-
54
- const context = this.canvas.getContext('2d');
55
- if (!context) {
56
- throw new Error('Could not get 2D context');
57
- }
58
- this.context = context;
59
- }
60
-
61
- updateShader(): string {
62
- const { width, height } = this.options;
63
- const imageData = this.context.createImageData(width, height);
64
- const data = imageData.data;
65
-
66
- for (let y = 0; y < height; y++) {
67
- for (let x = 0; x < width; x++) {
68
- const uv: Vec2 = { x: x / width, y: y / height };
69
- const pos = this.options.fragment(uv);
70
-
71
- const dx = pos.x * width - x;
72
- const dy = pos.y * height - y;
73
-
74
- const r = dx / 10 + 0.5;
75
- const g = dy / 10 + 0.5;
76
-
77
- const pixelIndex = (y * width + x) * 4;
78
- data[pixelIndex] = Math.max(0, Math.min(255, r * 255));
79
- data[pixelIndex + 1] = Math.max(0, Math.min(255, g * 255));
80
- data[pixelIndex + 2] = Math.max(0, Math.min(255, g * 255));
81
- data[pixelIndex + 3] = 255;
82
- }
83
- }
84
-
85
- this.context.putImageData(imageData, 0, 0);
86
- return this.canvas.toDataURL();
87
- }
88
-
89
- destroy(): void {
90
- this.canvas.remove();
91
- }
92
- }