@shohojdhara/atomix 0.4.7 → 0.4.9
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 +58 -1
- package/dist/atomix.css +172 -157
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +4 -4
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.d.ts +33 -0
- package/dist/charts.js +1274 -164
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +33 -10
- package/dist/core.js +1099 -83
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +33 -0
- package/dist/forms.js +2106 -1050
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +42 -1
- package/dist/heavy.js +1663 -638
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +442 -270
- package/dist/index.esm.js +1947 -680
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1982 -712
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +6 -3
- package/scripts/atomix-cli.js +136 -1827
- package/scripts/cli/__tests__/basic.test.js +3 -2
- package/scripts/cli/__tests__/clean.test.js +278 -0
- package/scripts/cli/__tests__/component-validator.test.js +433 -0
- package/scripts/cli/__tests__/generator.test.js +613 -0
- package/scripts/cli/__tests__/glass-motion.test.js +256 -0
- package/scripts/cli/__tests__/integration.test.js +719 -108
- package/scripts/cli/__tests__/migrate.test.js +74 -0
- package/scripts/cli/__tests__/security.test.js +206 -0
- package/scripts/cli/__tests__/test-setup.js +3 -1
- package/scripts/cli/__tests__/theme-bridge.test.js +507 -0
- package/scripts/cli/__tests__/token-provider.test.js +361 -0
- package/scripts/cli/__tests__/utils.test.js +5 -5
- package/scripts/cli/commands/benchmark.js +105 -0
- package/scripts/cli/commands/build-theme.js +115 -0
- package/scripts/cli/commands/clean.js +109 -0
- package/scripts/cli/commands/doctor.js +88 -0
- package/scripts/cli/commands/generate.js +218 -0
- package/scripts/cli/commands/init.js +73 -0
- package/scripts/cli/commands/migrate.js +106 -0
- package/scripts/cli/commands/sync-tokens.js +206 -0
- package/scripts/cli/commands/theme-bridge.js +248 -0
- package/scripts/cli/commands/tokens.js +157 -0
- package/scripts/cli/commands/validate.js +194 -0
- package/scripts/cli/internal/ai-engine.js +156 -0
- package/scripts/cli/internal/compiler.js +114 -0
- package/scripts/cli/internal/component-validator.js +443 -0
- package/scripts/cli/internal/config-loader.js +162 -0
- package/scripts/cli/internal/filesystem.js +158 -0
- package/scripts/cli/internal/generator.js +430 -0
- package/scripts/cli/internal/glass-generator.js +398 -0
- package/scripts/cli/internal/hook-generator.js +369 -0
- package/scripts/cli/internal/hooks.js +61 -0
- package/scripts/cli/internal/itcss-generator.js +565 -0
- package/scripts/cli/internal/motion-generator.js +679 -0
- package/scripts/cli/internal/template-engine.js +301 -0
- package/scripts/cli/internal/theme-bridge.js +664 -0
- package/scripts/cli/internal/tokens/engine.js +122 -0
- package/scripts/cli/internal/tokens/provider.js +34 -0
- package/scripts/cli/internal/tokens/providers/figma.js +50 -0
- package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
- package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
- package/scripts/cli/internal/tokens/token-provider.js +443 -0
- package/scripts/cli/internal/tokens/token-validator.js +513 -0
- package/scripts/cli/internal/validator.js +276 -0
- package/scripts/cli/internal/wizard.js +115 -0
- package/scripts/cli/mappings.js +23 -0
- package/scripts/cli/migration-tools.js +164 -94
- package/scripts/cli/plugins/style-dictionary.js +46 -0
- package/scripts/cli/templates/README.md +525 -95
- package/scripts/cli/templates/common-templates.js +40 -14
- package/scripts/cli/templates/components/react-component.ts +282 -0
- package/scripts/cli/templates/config/project-config.ts +112 -0
- package/scripts/cli/templates/hooks/use-component.ts +477 -0
- package/scripts/cli/templates/index.js +19 -4
- package/scripts/cli/templates/index.ts +171 -0
- package/scripts/cli/templates/next-templates.js +72 -0
- package/scripts/cli/templates/react-templates.js +70 -126
- package/scripts/cli/templates/scss-templates.js +35 -35
- package/scripts/cli/templates/stories/storybook-story.ts +241 -0
- package/scripts/cli/templates/styles/scss-component.ts +255 -0
- package/scripts/cli/templates/tests/vitest-test.ts +229 -0
- package/scripts/cli/templates/token-templates.js +337 -1
- package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
- package/scripts/cli/templates/types/component-types.ts +145 -0
- package/scripts/cli/templates/utils/testing-utils.ts +144 -0
- package/scripts/cli/templates/vanilla-templates.js +39 -0
- package/scripts/cli/token-manager.js +8 -2
- package/scripts/cli/utils/cache-manager.js +240 -0
- package/scripts/cli/utils/detector.js +46 -0
- package/scripts/cli/utils/diagnostics.js +289 -0
- package/scripts/cli/utils/error.js +89 -0
- package/scripts/cli/utils/helpers.js +67 -0
- package/scripts/cli/utils/logger.js +75 -0
- package/scripts/cli/utils/security.js +302 -0
- package/scripts/cli/utils/telemetry.js +115 -0
- package/scripts/cli/utils/validation.js +37 -0
- package/scripts/cli/utils.js +28 -341
- package/src/components/Accordion/Accordion.stories.tsx +0 -18
- package/src/components/Accordion/Accordion.test.tsx +0 -17
- package/src/components/Accordion/Accordion.tsx +0 -4
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +37 -3
- package/src/components/AtomixGlass/AtomixGlass.tsx +143 -31
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +129 -31
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
- package/src/components/AtomixGlass/README.md +25 -10
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +216 -0
- package/src/components/AtomixGlass/animation-system.ts +578 -0
- package/src/components/AtomixGlass/shader-utils.ts +4 -1
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
- package/src/components/AtomixGlass/stories/Phase1-Animation.stories.tsx +653 -0
- package/src/components/AtomixGlass/stories/Phase1-Test.stories.tsx +95 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -51
- package/src/components/AtomixGlass/stories/shared-components.tsx +6 -0
- package/src/components/Avatar/Avatar.tsx +1 -1
- package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
- package/src/components/Button/Button.stories.tsx +10 -0
- package/src/components/Button/Button.test.tsx +16 -11
- package/src/components/Button/Button.tsx +4 -4
- package/src/components/Card/Card.tsx +1 -1
- package/src/components/Dropdown/Dropdown.tsx +12 -12
- package/src/components/Form/Select.tsx +62 -3
- package/src/components/Modal/Modal.tsx +14 -3
- package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
- package/src/components/Slider/Slider.stories.tsx +3 -3
- package/src/components/Slider/Slider.tsx +38 -0
- package/src/components/Steps/Steps.tsx +3 -3
- package/src/components/Tabs/Tabs.tsx +77 -8
- package/src/components/Testimonial/Testimonial.tsx +1 -1
- package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
- package/src/components/TypedButton/TypedButton.tsx +39 -0
- package/src/components/TypedButton/index.ts +2 -0
- package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
- package/src/lib/composables/index.ts +4 -7
- package/src/lib/composables/types.ts +45 -0
- package/src/lib/composables/useAccordion.ts +0 -7
- package/src/lib/composables/useAtomixGlass.ts +148 -6
- package/src/lib/composables/useAtomixGlassStyles.ts +9 -7
- package/src/lib/composables/useChartExport.ts +3 -13
- package/src/lib/composables/useDropdown.ts +66 -0
- package/src/lib/composables/useFocusTrap.ts +80 -0
- package/src/lib/composables/usePerformanceMonitor.ts +448 -0
- package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
- package/src/lib/composables/useResponsiveGlass.ts +441 -0
- package/src/lib/composables/useTooltip.ts +16 -0
- package/src/lib/composables/useTypedButton.ts +66 -0
- package/src/lib/config/index.ts +62 -5
- package/src/lib/constants/components.ts +62 -7
- package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
- package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
- package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
- package/src/lib/types/components.ts +37 -11
- package/src/lib/types/glass.ts +35 -0
- package/src/lib/types/index.ts +1 -0
- package/src/lib/utils/displacement-generator.ts +1 -1
- package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
- package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
- package/src/styles/06-components/_components.atomix-glass.scss +17 -21
- package/src/styles/06-components/_components.edge-panel.scss +1 -5
- package/src/styles/06-components/_components.modal.scss +1 -4
- package/src/styles/06-components/_components.navbar.scss +1 -1
- package/src/styles/06-components/_components.testbutton.scss +212 -0
- package/src/styles/06-components/_components.testtypecheck.scss +212 -0
- package/src/styles/06-components/_components.tooltip.scss +9 -5
- package/src/styles/06-components/_components.typedbutton.scss +212 -0
- package/src/styles/99-utilities/_index.scss +1 -0
- package/src/styles/99-utilities/_utilities.text.scss +1 -1
- package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
- package/scripts/cli/component-generator.js +0 -564
- package/scripts/cli/interactive-init.js +0 -357
- package/src/styles/06-components/old.chart.styles.scss +0 -2788
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestTypeCheck Hook Types
|
|
3
|
+
* Generated by Atomix CLI
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Component State Interface
|
|
8
|
+
*/
|
|
9
|
+
export interface ComponentState {
|
|
10
|
+
isActive: boolean;
|
|
11
|
+
isHovered: boolean;
|
|
12
|
+
isFocused: boolean;
|
|
13
|
+
isLoading: boolean;
|
|
14
|
+
variant: 'primary' | 'secondary' | 'outline' | string;
|
|
15
|
+
size: 'sm' | 'md' | 'lg' | string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Event Handler Types
|
|
20
|
+
*/
|
|
21
|
+
export type ClickHandler = (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
22
|
+
export type HoverHandler = (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
23
|
+
export type FocusHandler = (event: React.FocusEvent<HTMLDivElement>) => void;
|
|
24
|
+
export type BlurHandler = (event: React.FocusEvent<HTMLDivElement>) => void;
|
|
25
|
+
export type StateChangeHandler = (state: ComponentState) => void;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Variant Configuration
|
|
29
|
+
*/
|
|
30
|
+
export type VariantType = 'primary' | 'secondary' | 'outline' | 'ghost' | 'link';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Size Configuration
|
|
34
|
+
*/
|
|
35
|
+
export type SizeType = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Glass Effect Configuration
|
|
39
|
+
*/
|
|
40
|
+
export interface GlassConfig {
|
|
41
|
+
displacementScale?: number;
|
|
42
|
+
blurAmount?: number;
|
|
43
|
+
saturation?: number;
|
|
44
|
+
elasticity?: number;
|
|
45
|
+
}
|
|
@@ -23,6 +23,13 @@ import {
|
|
|
23
23
|
lerp,
|
|
24
24
|
} from '../../components/AtomixGlass/glass-utils';
|
|
25
25
|
import { updateAtomixGlassStyles } from './useAtomixGlassStyles';
|
|
26
|
+
// Phase 1: Time-Based Animation System
|
|
27
|
+
import {
|
|
28
|
+
createAnimationLoop,
|
|
29
|
+
createFBMEngine,
|
|
30
|
+
getFBMConfigForQuality,
|
|
31
|
+
liquidGlassWithTime,
|
|
32
|
+
} from '../../components/AtomixGlass/animation-system';
|
|
26
33
|
|
|
27
34
|
const { CONSTANTS } = ATOMIX_GLASS;
|
|
28
35
|
|
|
@@ -150,6 +157,14 @@ interface UseAtomixGlassOptions extends Omit<AtomixGlassProps, 'children'> {
|
|
|
150
157
|
contentRef: React.RefObject<HTMLDivElement>;
|
|
151
158
|
wrapperRef?: React.RefObject<HTMLDivElement>;
|
|
152
159
|
children?: React.ReactNode;
|
|
160
|
+
isFixedOrSticky?: boolean;
|
|
161
|
+
// Phase 1: Time-Based Animation System
|
|
162
|
+
withLiquidBlur?: boolean;
|
|
163
|
+
animationQuality?: 'low' | 'medium' | 'high';
|
|
164
|
+
timeSpeed?: number;
|
|
165
|
+
noiseAmplitude?: number;
|
|
166
|
+
noiseFrequency?: number;
|
|
167
|
+
displacementStrength?: number;
|
|
153
168
|
}
|
|
154
169
|
|
|
155
170
|
interface UseAtomixGlassReturn {
|
|
@@ -181,6 +196,10 @@ interface UseAtomixGlassReturn {
|
|
|
181
196
|
// Transform calculations
|
|
182
197
|
transformStyle: string;
|
|
183
198
|
|
|
199
|
+
// Phase 1: Animation System - Shader time control
|
|
200
|
+
getShaderTime: () => number;
|
|
201
|
+
applyTimeBasedDistortion: (uv: { x: number; y: number }) => { x: number; y: number };
|
|
202
|
+
|
|
184
203
|
// Event handlers
|
|
185
204
|
handleMouseEnter: () => void;
|
|
186
205
|
handleMouseLeave: () => void;
|
|
@@ -211,12 +230,20 @@ export function useAtomixGlass({
|
|
|
211
230
|
onClick,
|
|
212
231
|
debugBorderRadius = false,
|
|
213
232
|
debugOverLight = false,
|
|
214
|
-
debugPerformance = false,
|
|
215
233
|
children,
|
|
216
234
|
blurAmount,
|
|
217
235
|
saturation,
|
|
218
236
|
padding,
|
|
219
237
|
withLiquidBlur,
|
|
238
|
+
isFixedOrSticky = false,
|
|
239
|
+
// Phase 1: Animation System Props
|
|
240
|
+
withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION,
|
|
241
|
+
animationSpeed = ATOMIX_GLASS.DEFAULTS.ANIMATION_SPEED,
|
|
242
|
+
withMultiLayerDistortion = ATOMIX_GLASS.DEFAULTS.WITH_MULTI_LAYER_DISTORTION,
|
|
243
|
+
distortionOctaves = ATOMIX_GLASS.DEFAULTS.DISTORTION_OCTAVES,
|
|
244
|
+
distortionLacunarity = ATOMIX_GLASS.DEFAULTS.DISTORTION_LACUNARITY,
|
|
245
|
+
distortionGain = ATOMIX_GLASS.DEFAULTS.DISTORTION_GAIN,
|
|
246
|
+
distortionQuality = ATOMIX_GLASS.DEFAULTS.DISTORTION_QUALITY,
|
|
220
247
|
}: UseAtomixGlassOptions): UseAtomixGlassReturn {
|
|
221
248
|
// State
|
|
222
249
|
const [isHovered, setIsHovered] = useState(false);
|
|
@@ -242,6 +269,115 @@ export function useAtomixGlass({
|
|
|
242
269
|
const [userPrefersHighContrast, setUserPrefersHighContrast] = useState(false);
|
|
243
270
|
const [detectedOverLight, setDetectedOverLight] = useState(false);
|
|
244
271
|
|
|
272
|
+
// ============================================================================
|
|
273
|
+
// Phase 1: Time-Based Animation System (Feature 1.1)
|
|
274
|
+
// ============================================================================
|
|
275
|
+
|
|
276
|
+
// Animation state refs
|
|
277
|
+
const animationFrameIdRef = useRef<number | null>(null);
|
|
278
|
+
const animationStartTimeRef = useRef<number>(0);
|
|
279
|
+
const elapsedTimeRef = useRef<number>(0);
|
|
280
|
+
const shaderTimeRef = useRef<number>(0);
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Get FBM configuration based on quality preset or custom values
|
|
284
|
+
*/
|
|
285
|
+
const fbmConfig = useMemo(() => {
|
|
286
|
+
// If quality preset is provided, use it as base
|
|
287
|
+
const preset = getFBMConfigForQuality(distortionQuality);
|
|
288
|
+
|
|
289
|
+
// Override with custom values if provided
|
|
290
|
+
return {
|
|
291
|
+
octaves: distortionOctaves ?? preset.octaves,
|
|
292
|
+
lacunarity: distortionLacunarity ?? preset.lacunarity,
|
|
293
|
+
gain: distortionGain ?? preset.gain,
|
|
294
|
+
};
|
|
295
|
+
}, [distortionQuality, distortionOctaves, distortionLacunarity, distortionGain]);
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Create FBM engine for multi-layer distortion
|
|
299
|
+
*/
|
|
300
|
+
const fbmEngine = useMemo(() => {
|
|
301
|
+
if (!withMultiLayerDistortion) return null;
|
|
302
|
+
return createFBMEngine(fbmConfig);
|
|
303
|
+
}, [withMultiLayerDistortion, fbmConfig]);
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Determine effective animation settings
|
|
307
|
+
*/
|
|
308
|
+
const effectiveReducedMotion = useMemo(
|
|
309
|
+
() => reducedMotion || userPrefersReducedMotion,
|
|
310
|
+
[reducedMotion, userPrefersReducedMotion]
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const effectiveWithTimeAnimation = useMemo(() => {
|
|
314
|
+
return withTimeAnimation && !effectiveReducedMotion;
|
|
315
|
+
}, [withTimeAnimation, effectiveReducedMotion]);
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Animation loop for time-based effects
|
|
319
|
+
*/
|
|
320
|
+
useEffect(() => {
|
|
321
|
+
if (!effectiveWithTimeAnimation || typeof window === 'undefined') {
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
let lastFrameTime = performance.now();
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Animation frame handler
|
|
329
|
+
*/
|
|
330
|
+
const animate = (currentTime: number) => {
|
|
331
|
+
// Calculate delta time
|
|
332
|
+
const deltaTime = currentTime - lastFrameTime;
|
|
333
|
+
lastFrameTime = currentTime;
|
|
334
|
+
|
|
335
|
+
// Apply animation speed multiplier
|
|
336
|
+
const scaledDelta = deltaTime * animationSpeed;
|
|
337
|
+
elapsedTimeRef.current += scaledDelta;
|
|
338
|
+
shaderTimeRef.current = elapsedTimeRef.current;
|
|
339
|
+
|
|
340
|
+
// Continue animation loop
|
|
341
|
+
animationFrameIdRef.current = requestAnimationFrame(animate);
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// Start animation
|
|
345
|
+
animationStartTimeRef.current = performance.now();
|
|
346
|
+
animationFrameIdRef.current = requestAnimationFrame(animate);
|
|
347
|
+
|
|
348
|
+
// Cleanup
|
|
349
|
+
return () => {
|
|
350
|
+
if (animationFrameIdRef.current !== null) {
|
|
351
|
+
cancelAnimationFrame(animationFrameIdRef.current);
|
|
352
|
+
animationFrameIdRef.current = null;
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}, [effectiveWithTimeAnimation, animationSpeed]);
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Get current shader time for animations
|
|
359
|
+
*/
|
|
360
|
+
const getShaderTime = useCallback(() => {
|
|
361
|
+
return shaderTimeRef.current;
|
|
362
|
+
}, []);
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Apply time-based distortion to UV coordinates
|
|
366
|
+
*/
|
|
367
|
+
const applyTimeBasedDistortion = useCallback(
|
|
368
|
+
(uv: { x: number; y: number }): { x: number; y: number } => {
|
|
369
|
+
if (!effectiveWithTimeAnimation || !fbmEngine) {
|
|
370
|
+
return uv;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const time = shaderTimeRef.current;
|
|
374
|
+
|
|
375
|
+
// Apply liquid glass distortion with time
|
|
376
|
+
return liquidGlassWithTime(uv, time, fbmConfig);
|
|
377
|
+
},
|
|
378
|
+
[effectiveWithTimeAnimation, fbmEngine, fbmConfig]
|
|
379
|
+
);
|
|
380
|
+
|
|
245
381
|
// Memoized derived values
|
|
246
382
|
const effectiveBorderRadius = useMemo(() => {
|
|
247
383
|
if (borderRadius !== undefined) {
|
|
@@ -258,10 +394,6 @@ export function useAtomixGlass({
|
|
|
258
394
|
cachedRectRef
|
|
259
395
|
});
|
|
260
396
|
|
|
261
|
-
const effectiveReducedMotion = useMemo(
|
|
262
|
-
() => reducedMotion || userPrefersReducedMotion,
|
|
263
|
-
[reducedMotion, userPrefersReducedMotion]
|
|
264
|
-
);
|
|
265
397
|
|
|
266
398
|
const effectiveHighContrast = useMemo(
|
|
267
399
|
() => highContrast || userPrefersHighContrast,
|
|
@@ -700,6 +832,12 @@ export function useAtomixGlass({
|
|
|
700
832
|
const tick = () => {
|
|
701
833
|
if (!lerpActiveRef.current) return;
|
|
702
834
|
|
|
835
|
+
// Add ref validity check to prevent memory leaks
|
|
836
|
+
if (!glassRef.current || !wrapperRef?.current) {
|
|
837
|
+
lerpActiveRef.current = false;
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
|
|
703
841
|
const cur = internalMouseOffsetRef.current;
|
|
704
842
|
const tgt = targetMouseOffsetRef.current;
|
|
705
843
|
|
|
@@ -725,7 +863,7 @@ export function useAtomixGlass({
|
|
|
725
863
|
|
|
726
864
|
// Imperative style update with the smoothed values
|
|
727
865
|
updateAtomixGlassStyles(
|
|
728
|
-
wrapperRef
|
|
866
|
+
wrapperRef.current,
|
|
729
867
|
glassRef.current,
|
|
730
868
|
{
|
|
731
869
|
mouseOffset: internalMouseOffsetRef.current,
|
|
@@ -745,6 +883,7 @@ export function useAtomixGlass({
|
|
|
745
883
|
blurAmount,
|
|
746
884
|
saturation,
|
|
747
885
|
padding,
|
|
886
|
+
isFixedOrSticky,
|
|
748
887
|
}
|
|
749
888
|
);
|
|
750
889
|
|
|
@@ -768,6 +907,7 @@ export function useAtomixGlass({
|
|
|
768
907
|
blurAmount,
|
|
769
908
|
saturation,
|
|
770
909
|
padding,
|
|
910
|
+
isFixedOrSticky,
|
|
771
911
|
]);
|
|
772
912
|
|
|
773
913
|
const stopLerpLoop = useCallback(() => {
|
|
@@ -913,6 +1053,8 @@ export function useAtomixGlass({
|
|
|
913
1053
|
mouseOffset, // This is now static (refs or props) unless prop changes
|
|
914
1054
|
overLightConfig,
|
|
915
1055
|
transformStyle,
|
|
1056
|
+
getShaderTime,
|
|
1057
|
+
applyTimeBasedDistortion,
|
|
916
1058
|
handleMouseEnter,
|
|
917
1059
|
handleMouseLeave,
|
|
918
1060
|
handleMouseDown,
|
|
@@ -34,6 +34,7 @@ export const updateAtomixGlassStyles = (
|
|
|
34
34
|
blurAmount?: number;
|
|
35
35
|
saturation?: number;
|
|
36
36
|
padding?: string;
|
|
37
|
+
isFixedOrSticky?: boolean;
|
|
37
38
|
}
|
|
38
39
|
) => {
|
|
39
40
|
if (!wrapperElement && !containerElement) return;
|
|
@@ -56,6 +57,7 @@ export const updateAtomixGlassStyles = (
|
|
|
56
57
|
blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
|
|
57
58
|
saturation = ATOMIX_GLASS.DEFAULTS.SATURATION,
|
|
58
59
|
padding = ATOMIX_GLASS.DEFAULTS.PADDING,
|
|
60
|
+
isFixedOrSticky = false,
|
|
59
61
|
} = params;
|
|
60
62
|
|
|
61
63
|
// Calculate mouse influence
|
|
@@ -225,10 +227,10 @@ export const updateAtomixGlassStyles = (
|
|
|
225
227
|
const my = mouseOffset.y;
|
|
226
228
|
|
|
227
229
|
// Constants for blur calculation
|
|
228
|
-
const EDGE_BLUR_MULTIPLIER =
|
|
229
|
-
const CENTER_BLUR_MULTIPLIER =
|
|
230
|
-
const FLOW_BLUR_MULTIPLIER =
|
|
231
|
-
const MOUSE_INFLUENCE_BLUR_FACTOR = 0.
|
|
230
|
+
const EDGE_BLUR_MULTIPLIER = 0.5;
|
|
231
|
+
const CENTER_BLUR_MULTIPLIER = 0.2;
|
|
232
|
+
const FLOW_BLUR_MULTIPLIER = 0.3;
|
|
233
|
+
const MOUSE_INFLUENCE_BLUR_FACTOR = 0.4;
|
|
232
234
|
const EDGE_INTENSITY_MOUSE_FACTOR = 0.15;
|
|
233
235
|
const CENTER_INTENSITY_MOUSE_FACTOR = 0.1;
|
|
234
236
|
const MAX_BLUR_RELATIVE = 2;
|
|
@@ -296,8 +298,8 @@ export const updateAtomixGlassStyles = (
|
|
|
296
298
|
// Container variables
|
|
297
299
|
const style = containerElement.style;
|
|
298
300
|
|
|
299
|
-
style.setProperty('--atomix-glass-container-width', `${glassSize.width}`);
|
|
300
|
-
style.setProperty('--atomix-glass-container-height', `${glassSize.height}`);
|
|
301
|
+
style.setProperty('--atomix-glass-container-width', !isFixedOrSticky ? '100%' : `${glassSize.width}`);
|
|
302
|
+
style.setProperty('--atomix-glass-container-height', !isFixedOrSticky ? '100%' : `${glassSize.height}`);
|
|
301
303
|
style.setProperty('--atomix-glass-container-padding', padding);
|
|
302
304
|
style.setProperty('--atomix-glass-container-radius', `${effectiveBorderRadius}px`);
|
|
303
305
|
|
|
@@ -320,7 +322,7 @@ export const updateAtomixGlassStyles = (
|
|
|
320
322
|
: 'none');
|
|
321
323
|
|
|
322
324
|
style.setProperty('--atomix-glass-container-text-shadow', isOverLight
|
|
323
|
-
? '0px 2px
|
|
325
|
+
? '0px 1px 2px rgba(255, 255, 255, 0.15)'
|
|
324
326
|
: '0px 2px 12px rgba(0, 0, 0, 0.4)');
|
|
325
327
|
|
|
326
328
|
style.setProperty('--atomix-glass-container-box-shadow', isOverLight
|
|
@@ -139,20 +139,10 @@ export function useChartExport() {
|
|
|
139
139
|
|
|
140
140
|
// Export as PDF
|
|
141
141
|
const exportAsPDF = useCallback(
|
|
142
|
-
async (
|
|
143
|
-
|
|
144
|
-
// For now, we'll convert to canvas and then to PDF
|
|
145
|
-
const canvas = await svgToCanvas(svgElement, options);
|
|
146
|
-
|
|
147
|
-
// This would require jsPDF library
|
|
148
|
-
// const pdf = new jsPDF();
|
|
149
|
-
// const imgData = canvas.toDataURL('image/png');
|
|
150
|
-
// pdf.addImage(imgData, 'PNG', 0, 0);
|
|
151
|
-
// pdf.save(options.filename || 'chart.pdf');
|
|
152
|
-
|
|
153
|
-
console.warn('PDF export requires jsPDF library to be installed');
|
|
142
|
+
async (_svgElement: SVGSVGElement, _options: ExportOptions): Promise<void> => {
|
|
143
|
+
console.warn('PDF export requires a PDF library to be installed');
|
|
154
144
|
},
|
|
155
|
-
[
|
|
145
|
+
[]
|
|
156
146
|
);
|
|
157
147
|
|
|
158
148
|
// Export data as CSV
|
|
@@ -107,6 +107,72 @@ export const useDropdown = ({
|
|
|
107
107
|
};
|
|
108
108
|
}, [isOpen, closeOnEscape, setIsOpen]);
|
|
109
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
|
+
|
|
110
176
|
// Helper function to get the flipped placement if needed
|
|
111
177
|
const getFlippedPlacement = useCallback(
|
|
112
178
|
(
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useEffect, useRef, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook to trap focus within an element
|
|
5
|
+
*/
|
|
6
|
+
export function useFocusTrap(isOpen: boolean) {
|
|
7
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
8
|
+
const previouslyFocusedElement = useRef<HTMLElement | null>(null);
|
|
9
|
+
|
|
10
|
+
const getFocusableElements = useCallback(() => {
|
|
11
|
+
if (!containerRef.current) return [];
|
|
12
|
+
|
|
13
|
+
return Array.from(
|
|
14
|
+
containerRef.current.querySelectorAll<HTMLElement>(
|
|
15
|
+
'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
16
|
+
)
|
|
17
|
+
).filter((el) => {
|
|
18
|
+
// Check if visible
|
|
19
|
+
const style = window.getComputedStyle(el);
|
|
20
|
+
return style.display !== 'none' && style.visibility !== 'hidden';
|
|
21
|
+
});
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
const handleKeyDown = useCallback(
|
|
25
|
+
(event: KeyboardEvent) => {
|
|
26
|
+
if (event.key !== 'Tab') return;
|
|
27
|
+
|
|
28
|
+
const focusableElements = getFocusableElements();
|
|
29
|
+
if (focusableElements.length === 0) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const firstElement = focusableElements[0] as HTMLElement;
|
|
34
|
+
const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;
|
|
35
|
+
|
|
36
|
+
if (event.shiftKey) {
|
|
37
|
+
// Shift + Tab
|
|
38
|
+
if (document.activeElement === firstElement) {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
lastElement.focus();
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
// Tab
|
|
44
|
+
if (document.activeElement === lastElement) {
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
firstElement.focus();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
[getFocusableElements]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (isOpen) {
|
|
55
|
+
previouslyFocusedElement.current = document.activeElement as HTMLElement;
|
|
56
|
+
|
|
57
|
+
const focusableElements = getFocusableElements();
|
|
58
|
+
if (focusableElements.length > 0 && focusableElements[0]) {
|
|
59
|
+
// Delay focus slightly to ensure element is rendered
|
|
60
|
+
const firstEl = focusableElements[0] as HTMLElement;
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
firstEl.focus();
|
|
63
|
+
}, 0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
67
|
+
} else {
|
|
68
|
+
if (previouslyFocusedElement.current) {
|
|
69
|
+
previouslyFocusedElement.current.focus();
|
|
70
|
+
}
|
|
71
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return () => {
|
|
75
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
76
|
+
};
|
|
77
|
+
}, [isOpen, getFocusableElements, handleKeyDown]);
|
|
78
|
+
|
|
79
|
+
return containerRef;
|
|
80
|
+
}
|