@shohojdhara/atomix 0.5.0 → 0.5.2
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/atomix.config.ts +12 -0
- package/build-tools/webpack-loader.js +5 -4
- package/dist/atomix.css +230 -83
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/webpack-loader.js +5 -4
- package/dist/charts.d.ts +24 -23
- package/dist/charts.js +271 -369
- package/dist/charts.js.map +1 -1
- package/dist/config.d.ts +624 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +3 -2
- package/dist/core.js +342 -382
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +4 -6
- package/dist/forms.js +233 -334
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +11 -2
- package/dist/heavy.js +406 -445
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +109 -65
- package/dist/index.esm.js +654 -748
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +621 -717
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.js +59 -60
- package/dist/layout.js.map +1 -1
- package/dist/theme.js +4 -4
- package/dist/theme.js.map +1 -1
- package/package.json +24 -9
- package/scripts/atomix-cli.js +15 -1
- package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
- package/scripts/cli/__tests__/detector.test.js +50 -0
- package/scripts/cli/__tests__/template-engine.test.js +23 -0
- package/scripts/cli/__tests__/test-setup.js +1 -133
- package/scripts/cli/commands/doctor.js +15 -3
- package/scripts/cli/commands/generate.js +113 -51
- package/scripts/cli/internal/ai-engine.js +30 -10
- package/scripts/cli/internal/complexity-utils.js +60 -0
- package/scripts/cli/internal/component-validator.js +49 -16
- package/scripts/cli/internal/generator.js +89 -36
- package/scripts/cli/internal/hook-generator.js +5 -2
- package/scripts/cli/internal/itcss-generator.js +16 -12
- package/scripts/cli/templates/next-templates.js +81 -30
- package/scripts/cli/templates/storybook-templates.js +12 -2
- package/scripts/cli/utils/detector.js +45 -7
- package/scripts/cli/utils/diagnostics.js +78 -0
- package/scripts/cli/utils/telemetry.js +13 -0
- package/src/components/Accordion/Accordion.stories.tsx +4 -0
- package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
- package/src/components/AtomixGlass/glass-utils.ts +51 -1
- package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
- package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
- package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
- package/src/components/AtomixGlass/stories/types.ts +3 -3
- package/src/components/Button/Button.tsx +114 -57
- package/src/components/Callout/Callout.tsx +4 -4
- package/src/components/Chart/ChartRenderer.tsx +1 -1
- package/src/components/Chart/DonutChart.tsx +11 -8
- package/src/components/EdgePanel/EdgePanel.tsx +119 -115
- package/src/components/Form/Select.tsx +4 -4
- package/src/components/List/List.tsx +4 -4
- package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
- package/src/components/ProductReview/ProductReview.tsx +4 -2
- package/src/components/Rating/Rating.tsx +4 -2
- package/src/components/SectionIntro/SectionIntro.tsx +4 -2
- package/src/components/Steps/Steps.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +5 -5
- package/src/components/Testimonial/Testimonial.tsx +4 -2
- package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
- package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
- package/src/layouts/CssGrid/CssGrid.tsx +215 -0
- package/src/layouts/CssGrid/index.ts +8 -0
- package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
- package/src/layouts/CssGrid/scripts/index.js +43 -0
- package/src/layouts/Grid/scripts/Container.js +139 -0
- package/src/layouts/Grid/scripts/Grid.js +184 -0
- package/src/layouts/Grid/scripts/GridCol.js +273 -0
- package/src/layouts/Grid/scripts/Row.js +154 -0
- package/src/layouts/Grid/scripts/index.js +48 -0
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
- package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
- package/src/lib/composables/useAccordion.ts +5 -5
- package/src/lib/composables/useAtomixGlass.ts +111 -74
- package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
- package/src/lib/composables/useBarChart.ts +2 -2
- package/src/lib/composables/useChart.ts +3 -2
- package/src/lib/composables/useChartToolbar.ts +48 -66
- package/src/lib/composables/useDataTable.ts +1 -1
- package/src/lib/composables/useDatePicker.ts +2 -2
- package/src/lib/composables/useEdgePanel.ts +45 -54
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
- package/src/lib/composables/usePhotoViewer.ts +2 -3
- package/src/lib/composables/usePieChart.ts +1 -1
- package/src/lib/composables/usePopover.ts +151 -139
- package/src/lib/composables/useSideMenu.ts +28 -41
- package/src/lib/composables/useSlider.ts +2 -6
- package/src/lib/composables/useTooltip.ts +2 -2
- package/src/lib/config/index.ts +39 -0
- package/src/lib/constants/components.ts +1 -0
- package/src/lib/theme/devtools/Comparator.tsx +1 -1
- package/src/lib/theme/devtools/Inspector.tsx +1 -1
- package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
- package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
- package/src/lib/types/components.ts +1 -0
- package/src/styles/01-settings/_index.scss +1 -0
- package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
- package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
- package/src/styles/02-tools/_tools.glass.scss +6 -0
- package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
- package/src/styles/06-components/_components.atomix-glass.scss +160 -99
- package/scripts/cli/__tests__/README.md +0 -81
- package/scripts/cli/__tests__/basic.test.js +0 -116
- package/scripts/cli/__tests__/clean.test.js +0 -278
- package/scripts/cli/__tests__/component-generator.test.js +0 -332
- package/scripts/cli/__tests__/component-validator.test.js +0 -433
- package/scripts/cli/__tests__/generator.test.js +0 -613
- package/scripts/cli/__tests__/glass-motion.test.js +0 -256
- package/scripts/cli/__tests__/integration.test.js +0 -938
- package/scripts/cli/__tests__/migrate.test.js +0 -74
- package/scripts/cli/__tests__/security.test.js +0 -206
- package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
- package/scripts/cli/__tests__/token-manager.test.js +0 -251
- package/scripts/cli/__tests__/token-provider.test.js +0 -361
- package/scripts/cli/__tests__/utils.test.js +0 -165
- package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
- package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
- package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
- package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
- package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
- package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
- package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
- package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
- package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
- package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
- package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
- package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
- package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
- package/src/components/TypedButton/TypedButton.tsx +0 -39
- package/src/components/TypedButton/index.ts +0 -2
- package/src/lib/composables/useBreadcrumb.ts +0 -81
- package/src/lib/composables/useChartInteractions.ts +0 -123
- package/src/lib/composables/useChartPerformance.ts +0 -347
- package/src/lib/composables/useDropdown.ts +0 -338
- package/src/lib/composables/useModal.ts +0 -110
- package/src/lib/composables/useTypedButton.ts +0 -66
- package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
- package/src/lib/utils/displacement-generator.ts +0 -92
- package/src/lib/utils/memoryMonitor.ts +0 -191
- package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
- package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
- package/src/styles/06-components/_components.testbutton.scss +0 -212
- package/src/styles/06-components/_components.testtypecheck.scss +0 -212
- package/src/styles/06-components/_components.typedbutton.scss +0 -212
|
@@ -7,9 +7,7 @@ import React, {
|
|
|
7
7
|
useRef,
|
|
8
8
|
useState,
|
|
9
9
|
useCallback,
|
|
10
|
-
useMemo,
|
|
11
10
|
Children,
|
|
12
|
-
cloneElement,
|
|
13
11
|
isValidElement,
|
|
14
12
|
} from 'react';
|
|
15
13
|
// Import styles for scoped CSS modules
|
|
@@ -122,7 +120,7 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
|
|
|
122
120
|
// === REFS & STATE ===
|
|
123
121
|
const [columns, setColumns] = useState(xs);
|
|
124
122
|
const [positions, setPositions] = useState<ItemPosition[]>([]);
|
|
125
|
-
const [
|
|
123
|
+
const [, setLayoutComplete] = useState(false);
|
|
126
124
|
const [loadingImages, setLoadingImages] = useState(false);
|
|
127
125
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
128
126
|
const columnHeights = useRef<number[]>([]);
|
|
@@ -178,6 +176,32 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
|
|
|
178
176
|
setItems(newItems);
|
|
179
177
|
}, [children]);
|
|
180
178
|
|
|
179
|
+
// === MANAGE ITEM LAYOUT ===
|
|
180
|
+
const calculateLayout = useCallback(() => {
|
|
181
|
+
if (!containerRef.current || items.length === 0) return;
|
|
182
|
+
const containerWidth = containerRef.current.offsetWidth;
|
|
183
|
+
const colWidth = (containerWidth - gap * (columns - 1)) / columns;
|
|
184
|
+
columnHeights.current = Array(columns).fill(0);
|
|
185
|
+
const newPositions: ItemPosition[] = [];
|
|
186
|
+
items.forEach((item, index) => {
|
|
187
|
+
if (item.ref.current) {
|
|
188
|
+
// Find the shortest column
|
|
189
|
+
const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current));
|
|
190
|
+
const left = shortestCol * (colWidth + gap);
|
|
191
|
+
const top = columnHeights.current[shortestCol] ?? 0;
|
|
192
|
+
const height = item.ref.current.offsetHeight;
|
|
193
|
+
columnHeights.current[shortestCol] = top + height + gap;
|
|
194
|
+
newPositions[index] = {
|
|
195
|
+
left,
|
|
196
|
+
top,
|
|
197
|
+
width: colWidth,
|
|
198
|
+
height,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
setPositions(newPositions);
|
|
203
|
+
}, [items, columns, gap]);
|
|
204
|
+
|
|
181
205
|
// === TRACK & MANAGE IMAGES ===
|
|
182
206
|
|
|
183
207
|
const handleImageLoad = useCallback(
|
|
@@ -195,30 +219,28 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
|
|
|
195
219
|
itemElement.classList.remove('o-masonry-grid__item-loading');
|
|
196
220
|
}
|
|
197
221
|
}
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
requestAnimationFrame(() => {
|
|
222
|
+
// Schedule layout recalculation after next paint to prevent overlap
|
|
223
|
+
const scheduleLayoutUpdate = () => {
|
|
224
|
+
const frameId = requestAnimationFrame(() => {
|
|
225
|
+
onImageLoad?.(imagesLoadedCount.current, totalImagesCount.current);
|
|
201
226
|
calculateLayout();
|
|
202
227
|
});
|
|
203
|
-
|
|
204
|
-
|
|
228
|
+
return () => cancelAnimationFrame(frameId);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// Clean up previous scheduled updates
|
|
232
|
+
const cleanup = scheduleLayoutUpdate();
|
|
205
233
|
|
|
206
234
|
// If all images have loaded, update loading state and complete layout
|
|
207
235
|
if (imagesLoadedCount.current >= totalImagesCount.current && totalImagesCount.current > 0) {
|
|
208
236
|
setLayoutComplete(true);
|
|
209
|
-
setLoadingImages(false);
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
requestAnimationFrame(() => {
|
|
213
|
-
calculateLayout();
|
|
214
|
-
// As a failsafe, if still present for some render lag, force another setLoadingImages(false)
|
|
215
|
-
setLoadingImages(false);
|
|
216
|
-
});
|
|
217
|
-
});
|
|
237
|
+
setLoadingImages(false);
|
|
238
|
+
setTimeout(() => cleanup(), 0); // Clean up after current execution
|
|
239
|
+
scheduleLayoutUpdate();
|
|
218
240
|
onLayoutComplete?.();
|
|
219
241
|
}
|
|
220
242
|
},
|
|
221
|
-
[onImageLoad, onLayoutComplete, imagesLoaded]
|
|
243
|
+
[onImageLoad, onLayoutComplete, imagesLoaded, calculateLayout]
|
|
222
244
|
);
|
|
223
245
|
|
|
224
246
|
const trackImages = useCallback(() => {
|
|
@@ -262,40 +284,28 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
|
|
|
262
284
|
};
|
|
263
285
|
}, [imagesLoaded, handleImageLoad, onLayoutComplete]);
|
|
264
286
|
|
|
265
|
-
// === MANAGE ITEM LAYOUT ===
|
|
266
|
-
const calculateLayout = useCallback(() => {
|
|
267
|
-
if (!containerRef.current || items.length === 0) return;
|
|
268
|
-
const containerWidth = containerRef.current.offsetWidth;
|
|
269
|
-
const colWidth = (containerWidth - gap * (columns - 1)) / columns;
|
|
270
|
-
columnHeights.current = Array(columns).fill(0);
|
|
271
|
-
const newPositions: ItemPosition[] = [];
|
|
272
|
-
items.forEach((item, index) => {
|
|
273
|
-
if (item.ref.current) {
|
|
274
|
-
// Find the shortest column
|
|
275
|
-
const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current));
|
|
276
|
-
const left = shortestCol * (colWidth + gap);
|
|
277
|
-
const top = columnHeights.current[shortestCol] ?? 0;
|
|
278
|
-
const height = item.ref.current.offsetHeight;
|
|
279
|
-
columnHeights.current[shortestCol] = top + height + gap;
|
|
280
|
-
newPositions[index] = {
|
|
281
|
-
left,
|
|
282
|
-
top,
|
|
283
|
-
width: colWidth,
|
|
284
|
-
height,
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
setPositions(newPositions);
|
|
289
|
-
}, [items, columns, gap]);
|
|
290
|
-
|
|
291
287
|
// === OBSERVE CONTAINER RESIZE ===
|
|
292
288
|
useEffect(() => {
|
|
293
289
|
if (!containerRef.current) return undefined;
|
|
290
|
+
|
|
294
291
|
let animationFrame: ReturnType<typeof requestAnimationFrame> | null = null;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
292
|
+
let lastWidth = 0;
|
|
293
|
+
|
|
294
|
+
const observer = new ResizeObserver((entries) => {
|
|
295
|
+
const entry = entries[0];
|
|
296
|
+
if (!entry) return;
|
|
297
|
+
const currentWidth = entry.contentRect.width;
|
|
298
|
+
|
|
299
|
+
// Only recalculate if width actually changed (prevents excessive calculations)
|
|
300
|
+
if (Math.abs(currentWidth - lastWidth) > 1) {
|
|
301
|
+
if (animationFrame) cancelAnimationFrame(animationFrame);
|
|
302
|
+
animationFrame = requestAnimationFrame(() => {
|
|
303
|
+
calculateLayout();
|
|
304
|
+
lastWidth = currentWidth;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
298
307
|
});
|
|
308
|
+
|
|
299
309
|
observer.observe(containerRef.current);
|
|
300
310
|
return () => {
|
|
301
311
|
observer.disconnect();
|
|
@@ -317,26 +327,28 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
|
|
|
317
327
|
// Only reset layoutComplete when items or columns change
|
|
318
328
|
}, [items, columns, calculateLayout, imagesLoaded, trackImages]);
|
|
319
329
|
|
|
320
|
-
// ===
|
|
330
|
+
// === ADD RESIZEOBSERVERS TO GRID ITEMS FOR DYNAMIC CONTENT MEASUREMENT ===
|
|
321
331
|
React.useEffect(() => {
|
|
322
|
-
// Clean up old observers if items ever change
|
|
323
332
|
const observers: ResizeObserver[] = [];
|
|
333
|
+
let animationFrame: ReturnType<typeof requestAnimationFrame> | null = null;
|
|
334
|
+
|
|
335
|
+
// Debounced layout calculation for item resize events
|
|
336
|
+
const debouncedCalculateLayout = () => {
|
|
337
|
+
if (animationFrame) cancelAnimationFrame(animationFrame);
|
|
338
|
+
animationFrame = requestAnimationFrame(calculateLayout);
|
|
339
|
+
};
|
|
340
|
+
|
|
324
341
|
items.forEach(item => {
|
|
325
342
|
if (item.ref.current) {
|
|
326
|
-
const obs = new ResizeObserver(
|
|
327
|
-
// Double rAF: ensures layout only runs after DOM/paint/async renders
|
|
328
|
-
requestAnimationFrame(() => {
|
|
329
|
-
requestAnimationFrame(() => {
|
|
330
|
-
calculateLayout();
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
});
|
|
343
|
+
const obs = new ResizeObserver(debouncedCalculateLayout);
|
|
334
344
|
obs.observe(item.ref.current);
|
|
335
345
|
observers.push(obs);
|
|
336
346
|
}
|
|
337
347
|
});
|
|
348
|
+
|
|
338
349
|
return () => {
|
|
339
350
|
observers.forEach(obs => obs.disconnect());
|
|
351
|
+
if (animationFrame) cancelAnimationFrame(animationFrame);
|
|
340
352
|
};
|
|
341
353
|
}, [items, calculateLayout]);
|
|
342
354
|
|
|
@@ -376,7 +388,7 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
|
|
|
376
388
|
return (
|
|
377
389
|
<div
|
|
378
390
|
key={item.id}
|
|
379
|
-
ref={item.ref as React.
|
|
391
|
+
ref={item.ref as React.RefObject<HTMLDivElement>}
|
|
380
392
|
style={{ opacity: 0, position: 'absolute' }}
|
|
381
393
|
>
|
|
382
394
|
{item.element}
|
|
@@ -386,7 +398,7 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
|
|
|
386
398
|
return (
|
|
387
399
|
<div
|
|
388
400
|
key={item.id}
|
|
389
|
-
ref={item.ref as React.
|
|
401
|
+
ref={item.ref as React.RefObject<HTMLDivElement>}
|
|
390
402
|
className="o-masonry-grid__item"
|
|
391
403
|
style={{
|
|
392
404
|
position: 'absolute',
|
|
@@ -6,7 +6,7 @@ import type { GlassSize } from '../../types/components';
|
|
|
6
6
|
const { CONSTANTS } = ATOMIX_GLASS;
|
|
7
7
|
|
|
8
8
|
interface UseGlassSizeProps {
|
|
9
|
-
glassRef: React.RefObject<HTMLDivElement>;
|
|
9
|
+
glassRef: React.RefObject<HTMLDivElement | null>;
|
|
10
10
|
effectiveBorderRadius: number;
|
|
11
11
|
cachedRectRef?: React.MutableRefObject<DOMRect | null>;
|
|
12
12
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AccordionProps, AccordionState, IconPosition, ElementRefs } from '../types/components';
|
|
2
|
-
import { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
3
3
|
import { ACCORDION } from '../constants/components';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -72,20 +72,20 @@ export function useAccordion(
|
|
|
72
72
|
/**
|
|
73
73
|
* Update panel height based on content
|
|
74
74
|
*/
|
|
75
|
-
const updatePanelHeight = (): void => {
|
|
75
|
+
const updatePanelHeight = useCallback((): void => {
|
|
76
76
|
if (contentRef.current && panelRef.current) {
|
|
77
77
|
const height = isOpen ? `${contentRef.current.clientHeight}px` : '0px';
|
|
78
78
|
panelRef.current.style.setProperty(ACCORDION.CSS_VARS.PANEL_HEIGHT, height);
|
|
79
79
|
setPanelHeight(height);
|
|
80
80
|
}
|
|
81
|
-
};
|
|
81
|
+
}, [isOpen]);
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* Effect to update panel height when open state changes
|
|
85
85
|
*/
|
|
86
86
|
useEffect(() => {
|
|
87
87
|
updatePanelHeight();
|
|
88
|
-
}, [isOpen]);
|
|
88
|
+
}, [isOpen, updatePanelHeight]);
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
91
|
* Effect to handle window resize and update panel height
|
|
@@ -99,7 +99,7 @@ export function useAccordion(
|
|
|
99
99
|
|
|
100
100
|
window.addEventListener('resize', handleResize);
|
|
101
101
|
return () => window.removeEventListener('resize', handleResize);
|
|
102
|
-
}, [isOpen]);
|
|
102
|
+
}, [isOpen, updatePanelHeight]);
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
105
|
* Generate accordion class names based on state
|
|
@@ -153,9 +153,9 @@ const setCachedBackgroundDetection = (
|
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
interface UseAtomixGlassOptions extends Omit<AtomixGlassProps, 'children'> {
|
|
156
|
-
glassRef: React.RefObject<HTMLDivElement>;
|
|
157
|
-
contentRef: React.RefObject<HTMLDivElement>;
|
|
158
|
-
wrapperRef?: React.RefObject<HTMLDivElement>;
|
|
156
|
+
glassRef: React.RefObject<HTMLDivElement | null>;
|
|
157
|
+
contentRef: React.RefObject<HTMLDivElement | null>;
|
|
158
|
+
wrapperRef?: React.RefObject<HTMLDivElement | null>;
|
|
159
159
|
children?: React.ReactNode;
|
|
160
160
|
isFixedOrSticky?: boolean;
|
|
161
161
|
// Phase 1: Time-Based Animation System
|
|
@@ -775,49 +775,14 @@ export function useAtomixGlass({
|
|
|
775
775
|
// ── Raw mouse handler — writes to TARGET refs only ──────────────────
|
|
776
776
|
// The lerp loop (below) reads the targets and incrementally
|
|
777
777
|
// moves the "current" refs toward them for liquid smoothing.
|
|
778
|
-
const handleGlobalMousePosition = useCallback(
|
|
779
|
-
(globalPos: MousePosition) => {
|
|
780
|
-
if (externalGlobalMousePosition && externalMouseOffset) {
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
if (effectiveWithoutEffects) {
|
|
785
|
-
return;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
const container = mouseContainer?.current || glassRef.current;
|
|
789
|
-
if (!container) {
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// Use cached rect if available, otherwise get new one
|
|
794
|
-
let rect = cachedRectRef.current;
|
|
795
|
-
if (!rect || rect.width === 0 || rect.height === 0) {
|
|
796
|
-
rect = container.getBoundingClientRect();
|
|
797
|
-
cachedRectRef.current = rect;
|
|
798
|
-
}
|
|
799
778
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
targetMouseOffsetRef.current = {
|
|
808
|
-
x: ((globalPos.x - center.x) / rect.width) * 100,
|
|
809
|
-
y: ((globalPos.y - center.y) / rect.height) * 100,
|
|
810
|
-
};
|
|
811
|
-
targetGlobalMousePositionRef.current = globalPos;
|
|
812
|
-
},
|
|
813
|
-
[
|
|
814
|
-
mouseContainer,
|
|
815
|
-
glassRef,
|
|
816
|
-
externalGlobalMousePosition,
|
|
817
|
-
externalMouseOffset,
|
|
818
|
-
effectiveWithoutEffects,
|
|
819
|
-
]
|
|
820
|
-
);
|
|
779
|
+
const stopLerpLoop = useCallback(() => {
|
|
780
|
+
lerpActiveRef.current = false;
|
|
781
|
+
if (lerpRafRef.current !== null) {
|
|
782
|
+
cancelAnimationFrame(lerpRafRef.current);
|
|
783
|
+
lerpRafRef.current = null;
|
|
784
|
+
}
|
|
785
|
+
}, []);
|
|
821
786
|
|
|
822
787
|
// ── Lerp animation loop ─────────────────────────────────────────────
|
|
823
788
|
// Continuously interpolates the current offset toward the target.
|
|
@@ -827,20 +792,18 @@ export function useAtomixGlass({
|
|
|
827
792
|
lerpActiveRef.current = true;
|
|
828
793
|
|
|
829
794
|
const LERP_T = CONSTANTS.LERP_FACTOR; // 0.08 – lower = more viscous
|
|
830
|
-
const EPSILON = 0.
|
|
795
|
+
const EPSILON = 0.01; // Snap when close enough
|
|
831
796
|
|
|
832
797
|
const tick = () => {
|
|
833
798
|
if (!lerpActiveRef.current) return;
|
|
834
799
|
|
|
835
|
-
|
|
836
|
-
if (!glassRef.current || !wrapperRef?.current) {
|
|
800
|
+
if (!glassRef.current) {
|
|
837
801
|
lerpActiveRef.current = false;
|
|
838
802
|
return;
|
|
839
803
|
}
|
|
840
804
|
|
|
841
805
|
const cur = internalMouseOffsetRef.current;
|
|
842
806
|
const tgt = targetMouseOffsetRef.current;
|
|
843
|
-
|
|
844
807
|
const dx = tgt.x - cur.x;
|
|
845
808
|
const dy = tgt.y - cur.y;
|
|
846
809
|
|
|
@@ -848,22 +811,53 @@ export function useAtomixGlass({
|
|
|
848
811
|
if (Math.abs(dx) < EPSILON && Math.abs(dy) < EPSILON) {
|
|
849
812
|
internalMouseOffsetRef.current = { ...tgt };
|
|
850
813
|
internalGlobalMousePositionRef.current = { ...targetGlobalMousePositionRef.current };
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
814
|
+
|
|
815
|
+
// Final update and stop
|
|
816
|
+
updateAtomixGlassStyles(
|
|
817
|
+
wrapperRef?.current || null,
|
|
818
|
+
glassRef.current,
|
|
819
|
+
{
|
|
820
|
+
mouseOffset: internalMouseOffsetRef.current,
|
|
821
|
+
globalMousePosition: internalGlobalMousePositionRef.current,
|
|
822
|
+
glassSize,
|
|
823
|
+
isHovered,
|
|
824
|
+
isActive,
|
|
825
|
+
isOverLight: overLightConfig.isOverLight,
|
|
826
|
+
baseOverLightConfig: overLightConfig,
|
|
827
|
+
effectiveBorderRadius,
|
|
828
|
+
effectiveWithoutEffects,
|
|
829
|
+
effectiveReducedMotion,
|
|
830
|
+
elasticity,
|
|
831
|
+
directionalScale: isActive && Boolean(onClick) ? 'scale(0.96)' : 'scale(1)',
|
|
832
|
+
onClick,
|
|
833
|
+
withLiquidBlur,
|
|
834
|
+
blurAmount,
|
|
835
|
+
saturation,
|
|
836
|
+
padding,
|
|
837
|
+
isFixedOrSticky,
|
|
838
|
+
}
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
stopLerpLoop();
|
|
842
|
+
return;
|
|
862
843
|
}
|
|
863
844
|
|
|
864
|
-
//
|
|
845
|
+
// Smooth step
|
|
846
|
+
internalMouseOffsetRef.current = {
|
|
847
|
+
x: lerp(cur.x, tgt.x, LERP_T),
|
|
848
|
+
y: lerp(cur.y, tgt.y, LERP_T),
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
const curG = internalGlobalMousePositionRef.current;
|
|
852
|
+
const tgtG = targetGlobalMousePositionRef.current;
|
|
853
|
+
internalGlobalMousePositionRef.current = {
|
|
854
|
+
x: lerp(curG.x, tgtG.x, LERP_T),
|
|
855
|
+
y: lerp(curG.y, tgtG.y, LERP_T),
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
// Imperative style update
|
|
865
859
|
updateAtomixGlassStyles(
|
|
866
|
-
wrapperRef
|
|
860
|
+
wrapperRef?.current || null,
|
|
867
861
|
glassRef.current,
|
|
868
862
|
{
|
|
869
863
|
mouseOffset: internalMouseOffsetRef.current,
|
|
@@ -908,15 +902,58 @@ export function useAtomixGlass({
|
|
|
908
902
|
saturation,
|
|
909
903
|
padding,
|
|
910
904
|
isFixedOrSticky,
|
|
905
|
+
stopLerpLoop,
|
|
911
906
|
]);
|
|
912
907
|
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
908
|
+
const handleGlobalMousePosition = useCallback(
|
|
909
|
+
(globalPos: MousePosition) => {
|
|
910
|
+
if (externalGlobalMousePosition && externalMouseOffset) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
if (effectiveWithoutEffects) {
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const container = mouseContainer?.current || glassRef.current;
|
|
919
|
+
if (!container) {
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// Use cached rect if available, otherwise get new one
|
|
924
|
+
let rect = cachedRectRef.current;
|
|
925
|
+
if (!rect || rect.width === 0 || rect.height === 0) {
|
|
926
|
+
rect = container.getBoundingClientRect();
|
|
927
|
+
cachedRectRef.current = rect;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
if (rect.width === 0 || rect.height === 0) {
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
const center = calculateElementCenter(rect);
|
|
935
|
+
|
|
936
|
+
// Write raw target — the lerp loop will smoothly pursue it
|
|
937
|
+
targetMouseOffsetRef.current = {
|
|
938
|
+
x: ((globalPos.x - center.x) / rect.width) * 100,
|
|
939
|
+
y: ((globalPos.y - center.y) / rect.height) * 100,
|
|
940
|
+
};
|
|
941
|
+
targetGlobalMousePositionRef.current = globalPos;
|
|
942
|
+
|
|
943
|
+
// Ensure the lerp loop is running to smoothly chase the new target
|
|
944
|
+
if (!lerpActiveRef.current) {
|
|
945
|
+
startLerpLoop();
|
|
946
|
+
}
|
|
947
|
+
},
|
|
948
|
+
[
|
|
949
|
+
mouseContainer,
|
|
950
|
+
glassRef,
|
|
951
|
+
externalGlobalMousePosition,
|
|
952
|
+
externalMouseOffset,
|
|
953
|
+
effectiveWithoutEffects,
|
|
954
|
+
startLerpLoop,
|
|
955
|
+
]
|
|
956
|
+
);
|
|
920
957
|
|
|
921
958
|
// Subscribe to shared mouse tracker
|
|
922
959
|
useEffect(() => {
|
|
@@ -929,8 +966,8 @@ export function useAtomixGlass({
|
|
|
929
966
|
}
|
|
930
967
|
|
|
931
968
|
const unsubscribe = globalMouseTracker.subscribe(handleGlobalMousePosition);
|
|
932
|
-
|
|
933
|
-
//
|
|
969
|
+
|
|
970
|
+
// Initial start
|
|
934
971
|
startLerpLoop();
|
|
935
972
|
|
|
936
973
|
const updateRect = () => {
|
|
@@ -966,14 +1003,14 @@ export function useAtomixGlass({
|
|
|
966
1003
|
}
|
|
967
1004
|
};
|
|
968
1005
|
}, [
|
|
1006
|
+
externalGlobalMousePosition,
|
|
1007
|
+
externalMouseOffset,
|
|
1008
|
+
effectiveWithoutEffects,
|
|
969
1009
|
handleGlobalMousePosition,
|
|
970
1010
|
startLerpLoop,
|
|
971
1011
|
stopLerpLoop,
|
|
972
1012
|
mouseContainer,
|
|
973
1013
|
glassRef,
|
|
974
|
-
externalGlobalMousePosition,
|
|
975
|
-
externalMouseOffset,
|
|
976
|
-
effectiveWithoutEffects,
|
|
977
1014
|
]);
|
|
978
1015
|
|
|
979
1016
|
// Also call updateStyles on other state changes (hover, active, etc)
|
|
@@ -298,8 +298,6 @@ export const updateAtomixGlassStyles = (
|
|
|
298
298
|
// Container variables
|
|
299
299
|
const style = containerElement.style;
|
|
300
300
|
|
|
301
|
-
style.setProperty('--atomix-glass-container-width', !isFixedOrSticky ? '100%' : `${glassSize.width}`);
|
|
302
|
-
style.setProperty('--atomix-glass-container-height', !isFixedOrSticky ? '100%' : `${glassSize.height}`);
|
|
303
301
|
style.setProperty('--atomix-glass-container-padding', padding);
|
|
304
302
|
style.setProperty('--atomix-glass-container-radius', `${effectiveBorderRadius}px`);
|
|
305
303
|
|
|
@@ -325,7 +325,7 @@ export function useBarChart(datasets: ChartDataset[], options: BarChartOptions =
|
|
|
325
325
|
}
|
|
326
326
|
return value.toString();
|
|
327
327
|
},
|
|
328
|
-
[options
|
|
328
|
+
[options]
|
|
329
329
|
);
|
|
330
330
|
|
|
331
331
|
// Calculate data label position
|
|
@@ -352,7 +352,7 @@ export function useBarChart(datasets: ChartDataset[], options: BarChartOptions =
|
|
|
352
352
|
};
|
|
353
353
|
}
|
|
354
354
|
},
|
|
355
|
-
[options
|
|
355
|
+
[options]
|
|
356
356
|
);
|
|
357
357
|
|
|
358
358
|
return {
|
|
@@ -104,9 +104,10 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
104
104
|
|
|
105
105
|
// Cleanup animation frame on unmount
|
|
106
106
|
useEffect(() => {
|
|
107
|
+
const currentRef = animationFrameRef.current;
|
|
107
108
|
return () => {
|
|
108
|
-
if (
|
|
109
|
-
cancelAnimationFrame(
|
|
109
|
+
if (currentRef) {
|
|
110
|
+
cancelAnimationFrame(currentRef);
|
|
110
111
|
}
|
|
111
112
|
};
|
|
112
113
|
}, []);
|