@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
|
@@ -3,6 +3,11 @@ import type { AtomixGlassProps } from '../../lib/types/components';
|
|
|
3
3
|
import { ATOMIX_GLASS } from '../../lib/constants/components';
|
|
4
4
|
import { AtomixGlassContainer } from './AtomixGlassContainer';
|
|
5
5
|
import { useAtomixGlass } from '../../lib/composables/useAtomixGlass';
|
|
6
|
+
// Phase 3: Optimization & Adaptation
|
|
7
|
+
import { useResponsiveGlass } from '../../lib/composables/useResponsiveGlass';
|
|
8
|
+
import { usePerformanceMonitor } from '../../lib/composables/usePerformanceMonitor';
|
|
9
|
+
import { PerformanceDashboard } from './PerformanceDashboard';
|
|
10
|
+
import { getDevicePreset, MOBILE_OPTIMIZED_BREAKPOINTS } from '../../lib/composables/useResponsiveGlass.presets';
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* AtomixGlass - A high-performance glass morphism component with liquid distortion effects
|
|
@@ -18,6 +23,8 @@ import { useAtomixGlass } from '../../lib/composables/useAtomixGlass';
|
|
|
18
23
|
* - Focus ring support for keyboard navigation
|
|
19
24
|
* - Responsive breakpoints for mobile optimization
|
|
20
25
|
* - Enhanced ARIA attributes for screen readers
|
|
26
|
+
* - Time-based animation system with FBM distortion
|
|
27
|
+
* - Device preset optimization for performance/quality balance
|
|
21
28
|
*
|
|
22
29
|
* Design System Compliance:
|
|
23
30
|
* - Uses design tokens for opacity, spacing, and colors
|
|
@@ -74,6 +81,12 @@ import { useAtomixGlass } from '../../lib/composables/useAtomixGlass';
|
|
|
74
81
|
* <AtomixGlass overLight="auto" debugOverLight={true}>
|
|
75
82
|
* <div>Content with debug logging enabled</div>
|
|
76
83
|
* </AtomixGlass>
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Performance-optimized for mobile devices
|
|
87
|
+
* <AtomixGlass devicePreset="performance" disableResponsiveBreakpoints={false}>
|
|
88
|
+
* <div>Mobile-optimized glass effect</div>
|
|
89
|
+
* </AtomixGlass>
|
|
77
90
|
*/
|
|
78
91
|
export function AtomixGlass({
|
|
79
92
|
children,
|
|
@@ -104,15 +117,34 @@ export function AtomixGlass({
|
|
|
104
117
|
withBorder = true,
|
|
105
118
|
withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS,
|
|
106
119
|
debugPerformance = false,
|
|
107
|
-
debugBorderRadius = false,
|
|
108
120
|
debugOverLight = false,
|
|
109
121
|
height,
|
|
110
122
|
width,
|
|
123
|
+
withTimeAnimation = false,
|
|
124
|
+
animationSpeed = 1.0,
|
|
125
|
+
withMultiLayerDistortion = false,
|
|
126
|
+
distortionOctaves = 3,
|
|
127
|
+
distortionLacunarity = 2.0,
|
|
128
|
+
distortionGain = 0.5,
|
|
129
|
+
distortionQuality = 'medium',
|
|
130
|
+
devicePreset = 'balanced',
|
|
131
|
+
disableResponsiveBreakpoints = false,
|
|
111
132
|
...rest
|
|
112
133
|
}: AtomixGlassProps) {
|
|
113
134
|
const glassRef = useRef<HTMLDivElement>(null);
|
|
114
135
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
115
136
|
|
|
137
|
+
// ── Layout hoisting ──────────────────────────────────────────────────
|
|
138
|
+
// When position is fixed/sticky the layout props must live on the ROOT
|
|
139
|
+
// `.c-atomix-glass` element so that every decorative layer (borders,
|
|
140
|
+
// backgrounds, hover effects) stays in the same stacking context.
|
|
141
|
+
|
|
142
|
+
// Extract zIndex from style so it becomes the base for ALL internal
|
|
143
|
+
// layers via --atomix-glass-base-z-index. It must NOT be applied as a
|
|
144
|
+
// real z-index on the root element — that would break the glass effect.
|
|
145
|
+
const { zIndex: customZIndex, ...restStyle } = style;
|
|
146
|
+
const isFixedOrSticky = restStyle.position === 'fixed' || restStyle.position === 'sticky';
|
|
147
|
+
|
|
116
148
|
// Use composable hook for all state and logic
|
|
117
149
|
const {
|
|
118
150
|
isHovered,
|
|
@@ -126,6 +158,8 @@ export function AtomixGlass({
|
|
|
126
158
|
globalMousePosition,
|
|
127
159
|
mouseOffset,
|
|
128
160
|
transformStyle,
|
|
161
|
+
getShaderTime,
|
|
162
|
+
applyTimeBasedDistortion,
|
|
129
163
|
handleMouseEnter,
|
|
130
164
|
handleMouseLeave,
|
|
131
165
|
handleMouseDown,
|
|
@@ -144,7 +178,6 @@ export function AtomixGlass({
|
|
|
144
178
|
withoutEffects,
|
|
145
179
|
elasticity,
|
|
146
180
|
onClick,
|
|
147
|
-
debugBorderRadius,
|
|
148
181
|
debugOverLight,
|
|
149
182
|
debugPerformance,
|
|
150
183
|
children,
|
|
@@ -153,22 +186,72 @@ export function AtomixGlass({
|
|
|
153
186
|
withLiquidBlur,
|
|
154
187
|
padding,
|
|
155
188
|
style,
|
|
189
|
+
isFixedOrSticky,
|
|
190
|
+
// Phase 1: Animation System props
|
|
191
|
+
withTimeAnimation,
|
|
192
|
+
animationSpeed,
|
|
193
|
+
withMultiLayerDistortion,
|
|
194
|
+
distortionOctaves,
|
|
195
|
+
distortionLacunarity,
|
|
196
|
+
distortionGain,
|
|
197
|
+
distortionQuality,
|
|
156
198
|
});
|
|
157
199
|
|
|
158
|
-
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// Phase 3: Optimization & Adaptation Systems
|
|
202
|
+
// ============================================================================
|
|
159
203
|
|
|
160
|
-
|
|
204
|
+
// Get device preset parameters - memoized to prevent recalculation
|
|
205
|
+
const devicePresetParams = useMemo(() => {
|
|
206
|
+
return getDevicePreset(devicePreset);
|
|
207
|
+
}, [devicePreset]); // Re-calculate only when devicePreset changes
|
|
161
208
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
209
|
+
// Responsive breakpoint system - automatically adjusts parameters based on viewport
|
|
210
|
+
const {
|
|
211
|
+
responsiveParams,
|
|
212
|
+
currentBreakpoint,
|
|
213
|
+
performanceTier,
|
|
214
|
+
isActive: isResponsiveActive
|
|
215
|
+
} = useResponsiveGlass({
|
|
216
|
+
baseParams: {
|
|
217
|
+
...devicePresetParams,
|
|
218
|
+
distortionOctaves: Math.round((displacementScale || ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE) / 25),
|
|
219
|
+
displacementScale: displacementScale || ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE,
|
|
220
|
+
blurAmount: blurAmount || ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
|
|
221
|
+
saturation: saturation || ATOMIX_GLASS.DEFAULTS.SATURATION,
|
|
222
|
+
aberrationIntensity: aberrationIntensity || ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY,
|
|
223
|
+
animationSpeed: 1.0,
|
|
224
|
+
chromaticIntensity: aberrationIntensity || ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY,
|
|
225
|
+
},
|
|
226
|
+
breakpoints: MOBILE_OPTIMIZED_BREAKPOINTS,
|
|
227
|
+
enabled: !disableResponsiveBreakpoints && typeof window !== 'undefined', // Enable unless disabled
|
|
228
|
+
debug: false
|
|
229
|
+
});
|
|
166
230
|
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
231
|
+
// Performance monitoring - tracks FPS, frame time, memory usage
|
|
232
|
+
const {
|
|
233
|
+
metrics: performanceMetrics,
|
|
234
|
+
recommendedQuality,
|
|
235
|
+
isUnderperforming,
|
|
236
|
+
setQualityLevel,
|
|
237
|
+
toggleMonitoring
|
|
238
|
+
} = usePerformanceMonitor({
|
|
239
|
+
enabled: false, // We'll toggle manually based on prop
|
|
240
|
+
debug: false,
|
|
241
|
+
showOverlay: false
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Auto-start performance monitoring if enabled (only in development)
|
|
245
|
+
React.useEffect(() => {
|
|
246
|
+
if (process.env.NODE_ENV === 'development' && (window as any)?.enablePerformanceMonitoring) {
|
|
247
|
+
toggleMonitoring();
|
|
248
|
+
}
|
|
249
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
250
|
+
}, []); // Only run once on mount
|
|
251
|
+
|
|
252
|
+
const isOverLight = useMemo(() => overLightConfig.isOverLight, [overLightConfig.isOverLight]);
|
|
253
|
+
|
|
254
|
+
const shouldRenderOverLightLayers = withOverLightLayers && isOverLight;
|
|
172
255
|
|
|
173
256
|
const rootLayoutStyle = useMemo<React.CSSProperties>(() => {
|
|
174
257
|
if (!isFixedOrSticky) return {};
|
|
@@ -223,21 +306,26 @@ export function AtomixGlass({
|
|
|
223
306
|
);
|
|
224
307
|
|
|
225
308
|
const adjustedSize = useMemo(() => {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
) => {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
return typeof explicitSize === 'number' ? `${explicitSize}px` : explicitSize;
|
|
309
|
+
// Keep a reference to positionStyles to avoid unused-variable lint,
|
|
310
|
+
// but sizing is driven by explicit width/height or measured size.
|
|
311
|
+
const _position = positionStyles.position;
|
|
312
|
+
|
|
313
|
+
const resolveLength = (value: string | number | undefined, measured: number): string => {
|
|
314
|
+
if (value !== undefined) {
|
|
315
|
+
return typeof value === 'number' ? `${value}px` : value;
|
|
234
316
|
}
|
|
235
|
-
|
|
317
|
+
if (measured > 0) {
|
|
318
|
+
return `${measured}px`;
|
|
319
|
+
}
|
|
320
|
+
return '100%';
|
|
236
321
|
};
|
|
237
322
|
|
|
323
|
+
const effectiveWidth = width ?? restStyle.width;
|
|
324
|
+
const effectiveHeight = height ?? restStyle.height;
|
|
325
|
+
|
|
238
326
|
return {
|
|
239
|
-
width:
|
|
240
|
-
height:
|
|
327
|
+
width: resolveLength(effectiveWidth, glassSize.width),
|
|
328
|
+
height: resolveLength(effectiveHeight, glassSize.height),
|
|
241
329
|
};
|
|
242
330
|
}, [
|
|
243
331
|
width,
|
|
@@ -338,9 +426,10 @@ export function AtomixGlass({
|
|
|
338
426
|
...(customZIndex !== undefined && { '--atomix-glass-base-z-index': customZIndex }),
|
|
339
427
|
'--atomix-glass-radius': `${effectiveBorderRadius}px`,
|
|
340
428
|
'--atomix-glass-transform': transformStyle || 'none',
|
|
341
|
-
|
|
342
|
-
'--atomix-glass-
|
|
343
|
-
'--atomix-glass-
|
|
429
|
+
// Internal decorative layers are positioned relative to the root;
|
|
430
|
+
'--atomix-glass-position': rootLayoutStyle.position,
|
|
431
|
+
'--atomix-glass-top': `${isFixedOrSticky ? rootLayoutStyle.top : 0}px`,
|
|
432
|
+
'--atomix-glass-left': `${isFixedOrSticky ? rootLayoutStyle.left : 0}px`,
|
|
344
433
|
'--atomix-glass-width': adjustedSize.width,
|
|
345
434
|
'--atomix-glass-height': adjustedSize.height,
|
|
346
435
|
'--atomix-glass-border-width': 'var(--atomix-spacing-0-5, 0.09375rem)',
|
|
@@ -376,11 +465,12 @@ export function AtomixGlass({
|
|
|
376
465
|
opacityValues,
|
|
377
466
|
effectiveBorderRadius,
|
|
378
467
|
transformStyle,
|
|
379
|
-
positionStyles,
|
|
380
468
|
adjustedSize,
|
|
381
469
|
isOverLight,
|
|
382
470
|
overLightConfig.borderOpacity,
|
|
383
471
|
customZIndex,
|
|
472
|
+
rootLayoutStyle,
|
|
473
|
+
isFixedOrSticky,
|
|
384
474
|
]);
|
|
385
475
|
|
|
386
476
|
// Helper function to render background layers
|
|
@@ -417,7 +507,12 @@ export function AtomixGlass({
|
|
|
417
507
|
ref={glassRef}
|
|
418
508
|
contentRef={contentRef}
|
|
419
509
|
className={className}
|
|
420
|
-
style={
|
|
510
|
+
style={{
|
|
511
|
+
...restStyle,
|
|
512
|
+
...(!isFixedOrSticky && {
|
|
513
|
+
position: 'relative',
|
|
514
|
+
}),
|
|
515
|
+
}}
|
|
421
516
|
borderRadius={effectiveBorderRadius}
|
|
422
517
|
displacementScale={
|
|
423
518
|
effectiveWithoutEffects
|
|
@@ -462,11 +557,19 @@ export function AtomixGlass({
|
|
|
462
557
|
}}
|
|
463
558
|
onClick={onClick}
|
|
464
559
|
mode={mode}
|
|
465
|
-
transform={baseStyle.transform}
|
|
466
560
|
effectiveWithoutEffects={effectiveWithoutEffects}
|
|
467
561
|
effectiveReducedMotion={effectiveReducedMotion}
|
|
468
562
|
shaderVariant={shaderVariant}
|
|
469
563
|
withLiquidBlur={withLiquidBlur}
|
|
564
|
+
// Phase 1: Animation System props
|
|
565
|
+
shaderTime={getShaderTime()}
|
|
566
|
+
withTimeAnimation={withTimeAnimation}
|
|
567
|
+
animationSpeed={animationSpeed}
|
|
568
|
+
withMultiLayerDistortion={withMultiLayerDistortion}
|
|
569
|
+
distortionOctaves={distortionOctaves}
|
|
570
|
+
distortionLacunarity={distortionLacunarity}
|
|
571
|
+
distortionGain={distortionGain}
|
|
572
|
+
distortionQuality={distortionQuality}
|
|
470
573
|
>
|
|
471
574
|
{children}
|
|
472
575
|
</AtomixGlassContainer>
|
|
@@ -480,7 +583,7 @@ export function AtomixGlass({
|
|
|
480
583
|
)}
|
|
481
584
|
|
|
482
585
|
{/* Background layers for over-light mode */}
|
|
483
|
-
{/* Static styles (pointer-events
|
|
586
|
+
{/* Static styles (pointer-events) are in SCSS; will-change is managed via .u-glass-clean-root utility for backdrop-filter stability */}
|
|
484
587
|
{renderBackgroundLayer('dark')}
|
|
485
588
|
{renderBackgroundLayer('black')}
|
|
486
589
|
{shouldRenderOverLightLayers && (
|
|
@@ -500,6 +603,15 @@ export function AtomixGlass({
|
|
|
500
603
|
<span className={ATOMIX_GLASS.BORDER_2_CLASS} />
|
|
501
604
|
</>
|
|
502
605
|
)}
|
|
606
|
+
|
|
607
|
+
{/* Phase 3: Performance Monitoring Dashboard - Only in development with debugPerformance enabled */}
|
|
608
|
+
{debugPerformance && performanceMetrics && (
|
|
609
|
+
<PerformanceDashboard
|
|
610
|
+
metrics={performanceMetrics}
|
|
611
|
+
isVisible={true}
|
|
612
|
+
onClose={() => {}} // No-op, dashboard always visible when debugPerformance is true
|
|
613
|
+
/>
|
|
614
|
+
)}
|
|
503
615
|
</div>
|
|
504
616
|
);
|
|
505
617
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { forwardRef, useRef, useState, useEffect, useMemo } from 'react';
|
|
2
2
|
import type { CSSProperties } from 'react';
|
|
3
|
-
import type { DisplacementMode, MousePosition, GlassSize } from '../../lib/types/components';
|
|
3
|
+
import type { DisplacementMode, MousePosition, GlassSize, AtomixGlassProps } from '../../lib/types/components';
|
|
4
4
|
import type { FragmentShaderType } from './shader-utils';
|
|
5
5
|
import { GlassFilter } from './GlassFilter';
|
|
6
6
|
import { calculateMouseInfluence, clampBlur, validateGlassSize } from './glass-utils';
|
|
@@ -59,7 +59,17 @@ const setCachedShader = (key: string, url: string): void => {
|
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
interface AtomixGlassContainerProps
|
|
62
|
+
interface AtomixGlassContainerProps
|
|
63
|
+
extends Pick<
|
|
64
|
+
AtomixGlassProps,
|
|
65
|
+
| 'withTimeAnimation'
|
|
66
|
+
| 'animationSpeed'
|
|
67
|
+
| 'withMultiLayerDistortion'
|
|
68
|
+
| 'distortionOctaves'
|
|
69
|
+
| 'distortionLacunarity'
|
|
70
|
+
| 'distortionGain'
|
|
71
|
+
| 'distortionQuality'
|
|
72
|
+
> {
|
|
63
73
|
className?: string;
|
|
64
74
|
style?: React.CSSProperties;
|
|
65
75
|
displacementScale?: number;
|
|
@@ -92,6 +102,9 @@ interface AtomixGlassContainerProps {
|
|
|
92
102
|
shaderVariant?: FragmentShaderType;
|
|
93
103
|
withLiquidBlur?: boolean;
|
|
94
104
|
|
|
105
|
+
// Phase 1: Animation System props
|
|
106
|
+
shaderTime?: number;
|
|
107
|
+
|
|
95
108
|
contentRef?: React.RefObject<HTMLDivElement>;
|
|
96
109
|
children?: React.ReactNode;
|
|
97
110
|
}
|
|
@@ -111,12 +124,10 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
111
124
|
saturation = 180,
|
|
112
125
|
aberrationIntensity = 2,
|
|
113
126
|
mouseOffset = { x: 0, y: 0 },
|
|
114
|
-
globalMousePosition = { x: 0, y: 0 },
|
|
115
127
|
onMouseEnter,
|
|
116
128
|
onMouseLeave,
|
|
117
129
|
onMouseDown,
|
|
118
130
|
onMouseUp,
|
|
119
|
-
isHovered = false,
|
|
120
131
|
isActive = false,
|
|
121
132
|
overLight = false,
|
|
122
133
|
overLightConfig = {},
|
|
@@ -130,6 +141,16 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
130
141
|
shaderVariant = 'liquidGlass',
|
|
131
142
|
withLiquidBlur = false,
|
|
132
143
|
|
|
144
|
+
// Phase 1: Animation System props
|
|
145
|
+
shaderTime,
|
|
146
|
+
withTimeAnimation = false,
|
|
147
|
+
animationSpeed = 1.0,
|
|
148
|
+
withMultiLayerDistortion = false,
|
|
149
|
+
distortionOctaves = 3,
|
|
150
|
+
distortionLacunarity = 2.0,
|
|
151
|
+
distortionGain = 0.5,
|
|
152
|
+
distortionQuality = 'medium',
|
|
153
|
+
|
|
133
154
|
contentRef,
|
|
134
155
|
},
|
|
135
156
|
ref
|
|
@@ -149,11 +170,15 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
149
170
|
|
|
150
171
|
// Use shared module-level cache (no per-instance cache needed)
|
|
151
172
|
const shaderDebounceTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
173
|
+
const shaderUpdateTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
174
|
+
|
|
175
|
+
// Phase 1: Animation frame ref for continuous shader updates
|
|
176
|
+
const animationFrameRef = useRef<number | null>(null);
|
|
152
177
|
|
|
153
178
|
// Lazy load shader utilities only when shader mode is needed
|
|
154
179
|
useEffect(() => {
|
|
155
180
|
if (mode === 'shader') {
|
|
156
|
-
// Dynamic import shader utilities
|
|
181
|
+
// Dynamic import shader utilities with animation support
|
|
157
182
|
import('./shader-utils')
|
|
158
183
|
.then(shaderUtils => {
|
|
159
184
|
shaderUtilsRef.current = {
|
|
@@ -176,12 +201,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
176
201
|
// Generate shader map with debouncing and caching
|
|
177
202
|
useEffect(() => {
|
|
178
203
|
// Enhanced validation for shader mode
|
|
179
|
-
if (
|
|
180
|
-
mode === 'shader' &&
|
|
181
|
-
glassSize &&
|
|
182
|
-
validateGlassSize(glassSize) &&
|
|
183
|
-
shaderUtilsRef.current
|
|
184
|
-
) {
|
|
204
|
+
if (mode === 'shader' && glassSize && validateGlassSize(glassSize)) {
|
|
185
205
|
// Create cache key from size and variant
|
|
186
206
|
const cacheKey = `${glassSize.width}x${glassSize.height}-${shaderVariant}`;
|
|
187
207
|
|
|
@@ -215,10 +235,11 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
215
235
|
fragment: selectedShader,
|
|
216
236
|
});
|
|
217
237
|
|
|
218
|
-
|
|
219
|
-
setTimeout(() => {
|
|
238
|
+
shaderUpdateTimeoutRef.current = setTimeout(() => {
|
|
220
239
|
const url = shaderGeneratorRef.current?.updateShader() || '';
|
|
221
|
-
|
|
240
|
+
if (url) {
|
|
241
|
+
setCachedShader(cacheKey, url);
|
|
242
|
+
}
|
|
222
243
|
setShaderMapUrl(url);
|
|
223
244
|
}, 100);
|
|
224
245
|
} catch (error) {
|
|
@@ -240,6 +261,10 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
240
261
|
clearTimeout(shaderDebounceTimeoutRef.current);
|
|
241
262
|
shaderDebounceTimeoutRef.current = null;
|
|
242
263
|
}
|
|
264
|
+
if (shaderUpdateTimeoutRef.current) {
|
|
265
|
+
clearTimeout(shaderUpdateTimeoutRef.current);
|
|
266
|
+
shaderUpdateTimeoutRef.current = null;
|
|
267
|
+
}
|
|
243
268
|
try {
|
|
244
269
|
shaderGeneratorRef.current?.destroy();
|
|
245
270
|
} catch (error) {
|
|
@@ -250,6 +275,92 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
250
275
|
};
|
|
251
276
|
}, [mode, glassSize, shaderVariant]);
|
|
252
277
|
|
|
278
|
+
// Phase 1: Time-Based Animation Loop - Continuous shader regeneration
|
|
279
|
+
useEffect(() => {
|
|
280
|
+
// Only run animations in shader mode with time animation enabled
|
|
281
|
+
if (
|
|
282
|
+
mode !== 'shader' ||
|
|
283
|
+
!withTimeAnimation ||
|
|
284
|
+
effectiveReducedMotion ||
|
|
285
|
+
effectiveWithoutEffects
|
|
286
|
+
) {
|
|
287
|
+
// Cancel any existing animation frame
|
|
288
|
+
if (animationFrameRef.current !== null) {
|
|
289
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
290
|
+
animationFrameRef.current = null;
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const baseFps =
|
|
296
|
+
distortionQuality === 'ultra'
|
|
297
|
+
? 60
|
|
298
|
+
: distortionQuality === 'high'
|
|
299
|
+
? 30
|
|
300
|
+
: distortionQuality === 'medium'
|
|
301
|
+
? 24
|
|
302
|
+
: 20;
|
|
303
|
+
const effectiveSpeed = Math.max(0.5, Math.min(2, animationSpeed || 1));
|
|
304
|
+
const complexity =
|
|
305
|
+
withMultiLayerDistortion
|
|
306
|
+
? Math.max(
|
|
307
|
+
1,
|
|
308
|
+
(distortionOctaves || 3) / 3 +
|
|
309
|
+
Math.max(0, (distortionLacunarity || 2) - 2) * 0.25 +
|
|
310
|
+
Math.max(0, (distortionGain || 0.5) - 0.5)
|
|
311
|
+
)
|
|
312
|
+
: 1;
|
|
313
|
+
const targetFps = Math.max(12, Math.min(60, Math.round((baseFps * effectiveSpeed) / complexity)));
|
|
314
|
+
const frameInterval = 1000 / targetFps;
|
|
315
|
+
let lastUpdate = 0;
|
|
316
|
+
let isCancelled = false;
|
|
317
|
+
|
|
318
|
+
const animate = (currentTime: number) => {
|
|
319
|
+
if (isCancelled) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (currentTime - lastUpdate >= frameInterval && shaderGeneratorRef.current) {
|
|
324
|
+
lastUpdate = currentTime;
|
|
325
|
+
try {
|
|
326
|
+
const animatedShaderUrl = shaderGeneratorRef.current.updateShader();
|
|
327
|
+
if (animatedShaderUrl) {
|
|
328
|
+
setShaderMapUrl(animatedShaderUrl);
|
|
329
|
+
}
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.warn('AtomixGlassContainer: Error in animation loop', error);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// Start animation loop
|
|
339
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
340
|
+
|
|
341
|
+
// Cleanup animation on unmount or dependency change
|
|
342
|
+
return () => {
|
|
343
|
+
isCancelled = true;
|
|
344
|
+
if (animationFrameRef.current !== null) {
|
|
345
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
346
|
+
animationFrameRef.current = null;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}, [
|
|
350
|
+
mode,
|
|
351
|
+
withTimeAnimation,
|
|
352
|
+
animationSpeed,
|
|
353
|
+
displacementScale,
|
|
354
|
+
withMultiLayerDistortion,
|
|
355
|
+
distortionOctaves,
|
|
356
|
+
distortionLacunarity,
|
|
357
|
+
distortionGain,
|
|
358
|
+
distortionQuality,
|
|
359
|
+
effectiveReducedMotion,
|
|
360
|
+
effectiveWithoutEffects,
|
|
361
|
+
glassSize,
|
|
362
|
+
]);
|
|
363
|
+
|
|
253
364
|
// Removed forced reflow to avoid layout thrash and potential feedback sizing loops
|
|
254
365
|
|
|
255
366
|
const [rectCache, setRectCache] = useState<DOMRect | null>(null);
|
|
@@ -401,6 +512,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
401
512
|
effectiveReducedMotion,
|
|
402
513
|
effectiveWithoutEffects,
|
|
403
514
|
withLiquidBlur,
|
|
515
|
+
overLightConfig,
|
|
404
516
|
]);
|
|
405
517
|
|
|
406
518
|
const containerVars = useMemo(() => {
|
|
@@ -415,9 +527,6 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
415
527
|
? mouseOffset.y
|
|
416
528
|
: 0;
|
|
417
529
|
return {
|
|
418
|
-
'--atomix-glass-container-width': `${glassSize?.width}`,
|
|
419
|
-
'--atomix-glass-container-height': `${glassSize?.height}`,
|
|
420
|
-
'--atomix-glass-container-padding': padding || '0 0',
|
|
421
530
|
'--atomix-glass-container-radius': `${typeof borderRadius === 'number' && !isNaN(borderRadius) ? borderRadius : 0}px`,
|
|
422
531
|
'--atomix-glass-container-backdrop': backdropStyle?.backdropFilter || 'none',
|
|
423
532
|
'--atomix-glass-container-shadow': overLight
|
|
@@ -434,7 +543,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
434
543
|
? `linear-gradient(${180 + mx * 0.5}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)`
|
|
435
544
|
: 'none',
|
|
436
545
|
'--atomix-glass-container-text-shadow': overLight
|
|
437
|
-
? '0px 2px
|
|
546
|
+
? '0px 1px 2px rgba(255, 255, 255, 0.15)'
|
|
438
547
|
: '0px 2px 12px rgba(0, 0, 0, 0.4)',
|
|
439
548
|
'--atomix-glass-container-box-shadow': overLight
|
|
440
549
|
? '0px 16px 70px rgba(0, 0, 0, 0.75)'
|
|
@@ -453,24 +562,14 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
453
562
|
} as React.CSSProperties;
|
|
454
563
|
}
|
|
455
564
|
}, [
|
|
456
|
-
|
|
457
|
-
padding,
|
|
458
|
-
borderRadius,
|
|
565
|
+
borderRadius,
|
|
459
566
|
backdropStyle,
|
|
460
567
|
mouseOffset,
|
|
461
568
|
overLight,
|
|
462
569
|
effectiveWithoutEffects,
|
|
570
|
+
overLightConfig,
|
|
463
571
|
]);
|
|
464
572
|
|
|
465
|
-
// Helper to force no transition/animation overrides with !important
|
|
466
|
-
const setForceNoTransition = (el: HTMLElement | null) => {
|
|
467
|
-
if (el) {
|
|
468
|
-
el.style.setProperty('transition-duration', '0s', 'important');
|
|
469
|
-
el.style.setProperty('animation-duration', '0s', 'important');
|
|
470
|
-
el.style.setProperty('transition-delay', '0s', 'important');
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
|
|
474
573
|
return (
|
|
475
574
|
<div
|
|
476
575
|
ref={el => {
|
|
@@ -513,7 +612,6 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
513
612
|
/>
|
|
514
613
|
{/* Enhanced Apple Liquid Glass Inner Shadow Layer */}
|
|
515
614
|
<div
|
|
516
|
-
ref={setForceNoTransition}
|
|
517
615
|
className={ATOMIX_GLASS.FILTER_OVERLAY_CLASS}
|
|
518
616
|
style={{
|
|
519
617
|
filter: `url(#${filterId})`,
|