@shohojdhara/atomix 0.6.5 → 0.6.6
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/dist/atomix.css +2 -2
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/atomix.umd.js +1 -1
- package/dist/atomix.umd.js.map +1 -1
- package/dist/atomix.umd.min.js +1 -1
- package/dist/charts.js +65 -255
- package/dist/charts.js.map +1 -1
- package/dist/core.js +65 -255
- package/dist/core.js.map +1 -1
- package/dist/forms.js +65 -255
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +65 -255
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +6 -37
- package/dist/index.esm.js +66 -300
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +66 -300
- 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 +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +0 -9
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +0 -2
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +1 -1
- package/src/components/AtomixGlass/glass-utils.ts +82 -53
- package/src/components/AtomixGlass/shader-utils.ts +19 -77
- package/src/components/Form/Select.test.tsx +6 -6
- package/src/components/Form/Textarea.stories.tsx +5 -5
- package/src/lib/composables/useAtomixGlass.ts +2 -134
- package/src/lib/composables/useAtomixGlassStyles.ts +3 -3
- package/src/lib/composables/usePerformanceMonitor.ts +0 -66
- package/src/lib/constants/components.ts +6 -1
- package/src/styles/01-settings/_settings.atomix-glass.scss +2 -2
- package/src/styles/02-tools/_tools.button.scss +51 -42
- package/src/styles/06-components/_components.atomix-glass.scss +2 -2
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +0 -171
- package/src/components/AtomixGlass/animation-system.ts +0 -578
package/package.json
CHANGED
|
@@ -180,7 +180,6 @@ const AtomixGlassInner = forwardRef<HTMLDivElement, AtomixGlassProps>(function A
|
|
|
180
180
|
globalMousePosition,
|
|
181
181
|
mouseOffset,
|
|
182
182
|
transformStyle,
|
|
183
|
-
getShaderTime,
|
|
184
183
|
handleMouseEnter,
|
|
185
184
|
handleMouseLeave,
|
|
186
185
|
handleMouseDown,
|
|
@@ -213,13 +212,6 @@ const AtomixGlassInner = forwardRef<HTMLDivElement, AtomixGlassProps>(function A
|
|
|
213
212
|
|
|
214
213
|
style,
|
|
215
214
|
isFixedOrSticky,
|
|
216
|
-
withTimeAnimation,
|
|
217
|
-
animationSpeed,
|
|
218
|
-
withMultiLayerDistortion,
|
|
219
|
-
distortionOctaves,
|
|
220
|
-
distortionLacunarity,
|
|
221
|
-
distortionGain,
|
|
222
|
-
distortionQuality,
|
|
223
215
|
});
|
|
224
216
|
|
|
225
217
|
|
|
@@ -429,7 +421,6 @@ const AtomixGlassInner = forwardRef<HTMLDivElement, AtomixGlassProps>(function A
|
|
|
429
421
|
shaderVariant={shaderVariant}
|
|
430
422
|
withLiquidBlur={withLiquidBlur}
|
|
431
423
|
isFixedOrSticky={isFixedOrSticky}
|
|
432
|
-
shaderTime={getShaderTime()}
|
|
433
424
|
withTimeAnimation={withTimeAnimation}
|
|
434
425
|
animationSpeed={animationSpeed}
|
|
435
426
|
withMultiLayerDistortion={withMultiLayerDistortion}
|
|
@@ -77,7 +77,6 @@ interface AtomixGlassContainerProps
|
|
|
77
77
|
withLiquidBlur?: boolean;
|
|
78
78
|
isFixedOrSticky?: boolean;
|
|
79
79
|
elasticity?: number;
|
|
80
|
-
shaderTime?: number;
|
|
81
80
|
|
|
82
81
|
contentRef?: React.RefObject<HTMLDivElement | null>;
|
|
83
82
|
children?: React.ReactNode;
|
|
@@ -117,7 +116,6 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
117
116
|
shaderVariant = 'liquidGlass',
|
|
118
117
|
withLiquidBlur = false,
|
|
119
118
|
isFixedOrSticky = false,
|
|
120
|
-
shaderTime,
|
|
121
119
|
withTimeAnimation = false,
|
|
122
120
|
animationSpeed = 1.0,
|
|
123
121
|
withMultiLayerDistortion = false,
|
|
@@ -8,7 +8,7 @@ exports[`AtomixGlass Visual Regression > matches snapshot with default props 1`]
|
|
|
8
8
|
>
|
|
9
9
|
<div
|
|
10
10
|
class="c-atomix-glass__container"
|
|
11
|
-
style="--atomix-glass-container-radius: 16px; --atomix-glass-container-backdrop: blur(20.16px) saturate(250%) contrast(1.02) brightness(1.02); --atomix-glass-container-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.32), inset 0 1px 2px rgba(255, 255, 255, 0.06), 0
|
|
11
|
+
style="--atomix-glass-container-radius: 16px; --atomix-glass-container-backdrop: blur(20.16px) saturate(250%) contrast(1.02) brightness(1.02); --atomix-glass-container-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.32), inset 0 1px 2px rgba(255, 255, 255, 0.06), 0 4px 16px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.08); --atomix-glass-container-shadow-opacity: 1; --atomix-glass-container-bg: none; --atomix-glass-container-text-shadow: 0px 2px 12px rgba(0, 0, 0, 0.4); --atomix-glass-container-box-shadow: 0 4px 16px rgba(0, 0, 0, 0.14), 0 1px 4px rgba(0, 0, 0, 0.10);"
|
|
12
12
|
>
|
|
13
13
|
<div
|
|
14
14
|
class="c-atomix-glass__inner"
|
|
@@ -6,23 +6,15 @@ import { ATOMIX_GLASS } from '../../lib/constants/components';
|
|
|
6
6
|
const { CONSTANTS } = ATOMIX_GLASS;
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
typeof pos2.y !== 'number'
|
|
19
|
-
) {
|
|
20
|
-
return 0;
|
|
21
|
-
}
|
|
22
|
-
const deltaX = pos1.x - pos2.x;
|
|
23
|
-
const deltaY = pos1.y - pos2.y;
|
|
24
|
-
return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
25
|
-
};
|
|
9
|
+
* Canonical 2D vector type shared across the glass math/shader utilities.
|
|
10
|
+
*
|
|
11
|
+
* Structurally identical to {@link MousePosition}; re-exported by `shader-utils`
|
|
12
|
+
* as the public `Vec2`.
|
|
13
|
+
*/
|
|
14
|
+
export interface Vec2 {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
}
|
|
26
18
|
|
|
27
19
|
/**
|
|
28
20
|
* Calculate element center from bounding rect
|
|
@@ -251,6 +243,79 @@ export const softClamp = (value: number, max: number): number => {
|
|
|
251
243
|
return max * (1 - Math.exp(-value / max));
|
|
252
244
|
};
|
|
253
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Clamp a value to the `[min, max]` range. Returns `min` for non-finite input.
|
|
248
|
+
*/
|
|
249
|
+
export const clamp = (value: number, min: number, max: number): number => {
|
|
250
|
+
if (typeof value !== 'number' || isNaN(value)) {
|
|
251
|
+
return min;
|
|
252
|
+
}
|
|
253
|
+
return Math.max(min, Math.min(max, value));
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Two-edge smoothstep (GLSL semantics) — Hermite interpolation between `edge0`
|
|
258
|
+
* and `edge1`. Reuses {@link smoothstep} after normalizing `x` into `[0, 1]`.
|
|
259
|
+
*/
|
|
260
|
+
export const smoothstepEdge = (edge0: number, edge1: number, x: number): number => {
|
|
261
|
+
if (typeof edge0 !== 'number' || typeof edge1 !== 'number' || typeof x !== 'number') {
|
|
262
|
+
return 0;
|
|
263
|
+
}
|
|
264
|
+
return smoothstep((x - edge0) / (edge1 - edge0));
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Cubic ease-in-out curve. `t` is clamped to `[0, 1]`.
|
|
269
|
+
*/
|
|
270
|
+
export const easeInOutCubic = (t: number): number => {
|
|
271
|
+
if (typeof t !== 'number' || isNaN(t)) {
|
|
272
|
+
return 0;
|
|
273
|
+
}
|
|
274
|
+
const clamped = Math.max(0, Math.min(1, t));
|
|
275
|
+
return clamped < 0.5
|
|
276
|
+
? 4 * clamped * clamped * clamped
|
|
277
|
+
: 1 - Math.pow(-2 * clamped + 2, 3) / 2;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Quartic ease-out curve. `t` is clamped to `[0, 1]`.
|
|
282
|
+
*/
|
|
283
|
+
export const easeOutQuart = (t: number): number => {
|
|
284
|
+
if (typeof t !== 'number' || isNaN(t)) {
|
|
285
|
+
return 0;
|
|
286
|
+
}
|
|
287
|
+
const clamped = Math.max(0, Math.min(1, t));
|
|
288
|
+
return 1 - Math.pow(1 - clamped, 4);
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Overflow-safe Euclidean length of a 2D vector.
|
|
293
|
+
*/
|
|
294
|
+
export const vec2Length = (x: number, y: number): number => {
|
|
295
|
+
if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) {
|
|
296
|
+
return 0;
|
|
297
|
+
}
|
|
298
|
+
const maxComponent = Math.max(Math.abs(x), Math.abs(y));
|
|
299
|
+
if (maxComponent === 0) return 0;
|
|
300
|
+
const scaledX = x / maxComponent;
|
|
301
|
+
const scaledY = y / maxComponent;
|
|
302
|
+
return maxComponent * Math.sqrt(scaledX * scaledX + scaledY * scaledY);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Resolves the hover/active intensity multipliers from interaction state.
|
|
307
|
+
*
|
|
308
|
+
* Single source of truth for the `HOVER_INTENSITY` / `ACTIVE_INTENSITY`
|
|
309
|
+
* multipliers shared by the hook and the imperative style updater.
|
|
310
|
+
*/
|
|
311
|
+
export const getInteractionIntensity = (
|
|
312
|
+
isHovered: boolean,
|
|
313
|
+
isActive: boolean
|
|
314
|
+
): { hoverIntensity: number; activeIntensity: number } => ({
|
|
315
|
+
hoverIntensity: isHovered ? CONSTANTS.INTERACTION.HOVER_INTENSITY : 1,
|
|
316
|
+
activeIntensity: isActive ? CONSTANTS.INTERACTION.ACTIVE_INTENSITY : 1,
|
|
317
|
+
});
|
|
318
|
+
|
|
254
319
|
/**
|
|
255
320
|
* Spring-damper integration helper
|
|
256
321
|
* Calculates the next value based on velocity, stiffness, and damping.
|
|
@@ -349,42 +414,6 @@ export function pickGlassLayoutStyle(style: CSSProperties): GlassLayoutPosition
|
|
|
349
414
|
};
|
|
350
415
|
}
|
|
351
416
|
|
|
352
|
-
/**
|
|
353
|
-
* Builds inline layout styles for the root wrapper.
|
|
354
|
-
*
|
|
355
|
-
* Reserved for alternate layout strategies. The default implementation applies
|
|
356
|
-
* layout on `.c-atomix-glass__container` to preserve backdrop-filter behavior.
|
|
357
|
-
*/
|
|
358
|
-
export function pickGlassHoistedRootStyle(style: CSSProperties): CSSProperties {
|
|
359
|
-
const layout = pickGlassLayoutStyle(style);
|
|
360
|
-
const resolvedPosition = (layout.position ?? style.position ?? 'fixed') as CSSProperties['position'];
|
|
361
|
-
|
|
362
|
-
return {
|
|
363
|
-
position: resolvedPosition,
|
|
364
|
-
...(layout.top !== undefined && { top: layout.top }),
|
|
365
|
-
...(layout.left !== undefined && { left: layout.left }),
|
|
366
|
-
...(layout.right !== undefined && { right: layout.right }),
|
|
367
|
-
...(layout.bottom !== undefined && { bottom: layout.bottom }),
|
|
368
|
-
...(layout.inset !== undefined && { inset: layout.inset }),
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Returns `style` without layout properties, preserving visual declarations only.
|
|
374
|
-
*/
|
|
375
|
-
export function omitGlassLayoutStyle(style: CSSProperties): CSSProperties {
|
|
376
|
-
const {
|
|
377
|
-
position: _p,
|
|
378
|
-
top: _t,
|
|
379
|
-
left: _l,
|
|
380
|
-
right: _r,
|
|
381
|
-
bottom: _b,
|
|
382
|
-
inset: _inset,
|
|
383
|
-
...visualStyle
|
|
384
|
-
} = style;
|
|
385
|
-
return visualStyle;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
417
|
/**
|
|
389
418
|
* Resolves inset custom properties for decorative layers (hover, borders, backgrounds).
|
|
390
419
|
*
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
// Adapted from https://github.com/shuding/liquid-glass
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import {
|
|
4
|
+
clamp,
|
|
5
|
+
easeInOutCubic,
|
|
6
|
+
easeOutQuart,
|
|
7
|
+
smoothstepEdge as smoothStep,
|
|
8
|
+
vec2Length as calculateLength,
|
|
9
|
+
} from './glass-utils';
|
|
10
|
+
import type { Vec2 } from './glass-utils';
|
|
11
|
+
|
|
12
|
+
export type { Vec2 } from './glass-utils';
|
|
7
13
|
|
|
8
14
|
export interface ShaderOptions {
|
|
9
15
|
width: number;
|
|
@@ -34,31 +40,6 @@ const DEFAULT_CANVAS_WIDTH = 256;
|
|
|
34
40
|
const DEFAULT_CANVAS_HEIGHT = 256;
|
|
35
41
|
|
|
36
42
|
// Utility functions
|
|
37
|
-
const smoothStep = (a: number, b: number, t: number): number => {
|
|
38
|
-
// Add input validation
|
|
39
|
-
if (typeof a !== 'number' || typeof b !== 'number' || typeof t !== 'number') {
|
|
40
|
-
return 0;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const clamped = Math.max(0, Math.min(1, (t - a) / (b - a)));
|
|
44
|
-
return clamped * clamped * (3 - 2 * clamped);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const calculateLength = (x: number, y: number): number => {
|
|
48
|
-
// Add input validation and error handling
|
|
49
|
-
if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) {
|
|
50
|
-
return 0;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Prevent potential overflow
|
|
54
|
-
const maxX = Math.max(Math.abs(x), Math.abs(y));
|
|
55
|
-
if (maxX === 0) return 0;
|
|
56
|
-
|
|
57
|
-
const scaledX = x / maxX;
|
|
58
|
-
const scaledY = y / maxX;
|
|
59
|
-
return maxX * Math.sqrt(scaledX * scaledX + scaledY * scaledY);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
43
|
const roundedRectSDF = (
|
|
63
44
|
x: number,
|
|
64
45
|
y: number,
|
|
@@ -96,42 +77,6 @@ const validateVec2 = (vec: Vec2): boolean => {
|
|
|
96
77
|
);
|
|
97
78
|
};
|
|
98
79
|
|
|
99
|
-
const clampValue = (value: number, min: number, max: number): number => {
|
|
100
|
-
// Add input validation
|
|
101
|
-
if (typeof value !== 'number' || typeof min !== 'number' || typeof max !== 'number') {
|
|
102
|
-
return min;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (isNaN(value)) return min;
|
|
106
|
-
if (isNaN(min)) return 0;
|
|
107
|
-
if (isNaN(max)) return 1;
|
|
108
|
-
|
|
109
|
-
return Math.max(min, Math.min(max, value));
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// Advanced easing functions for Apple-style smooth animations
|
|
113
|
-
const easeInOutCubic = (t: number): number => {
|
|
114
|
-
// Add input validation
|
|
115
|
-
if (typeof t !== 'number' || isNaN(t)) {
|
|
116
|
-
return 0;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const clampedT = Math.max(0, Math.min(1, t));
|
|
120
|
-
return clampedT < 0.5
|
|
121
|
-
? 4 * clampedT * clampedT * clampedT
|
|
122
|
-
: 1 - Math.pow(-2 * clampedT + 2, 3) / 2;
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const easeOutQuart = (t: number): number => {
|
|
126
|
-
// Add input validation
|
|
127
|
-
if (typeof t !== 'number' || isNaN(t)) {
|
|
128
|
-
return 0;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const clampedT = Math.max(0, Math.min(1, t));
|
|
132
|
-
return 1 - Math.pow(1 - clampedT, 4);
|
|
133
|
-
};
|
|
134
|
-
|
|
135
80
|
// Perlin-like noise for organic distortion
|
|
136
81
|
const noise2D = (x: number, y: number): number => {
|
|
137
82
|
// Add input validation
|
|
@@ -503,8 +448,8 @@ export const fragmentShaders = {
|
|
|
503
448
|
const finalY = iy + totalDistortionY + chromaticOffset.y * 0.5;
|
|
504
449
|
|
|
505
450
|
return createTexture(
|
|
506
|
-
|
|
507
|
-
|
|
451
|
+
clamp(finalX * scaled + 0.5, 0, 1),
|
|
452
|
+
clamp(finalY * scaled + 0.5, 0, 1)
|
|
508
453
|
);
|
|
509
454
|
},
|
|
510
455
|
|
|
@@ -545,7 +490,7 @@ export const fragmentShaders = {
|
|
|
545
490
|
const totalX = ix + (organicX * 0.035 + fluidVelocityX + vortexX) * mask;
|
|
546
491
|
const totalY = iy + (organicY * 0.035 + fluidVelocityY + vortexY) * mask;
|
|
547
492
|
|
|
548
|
-
return createTexture(
|
|
493
|
+
return createTexture(clamp(totalX + 0.5, 0, 1), clamp(totalY + 0.5, 0, 1));
|
|
549
494
|
},
|
|
550
495
|
|
|
551
496
|
// High-end glass with advanced refraction and depth
|
|
@@ -593,7 +538,7 @@ export const fragmentShaders = {
|
|
|
593
538
|
const finalX = ix + (refractionX + depthX + organicNoise * 0.015) * edgeMask;
|
|
594
539
|
const finalY = iy + (refractionY + depthY + organicNoise * 0.015) * edgeMask;
|
|
595
540
|
|
|
596
|
-
return createTexture(
|
|
541
|
+
return createTexture(clamp(finalX + 0.5, 0, 1), clamp(finalY + 0.5, 0, 1));
|
|
597
542
|
},
|
|
598
543
|
|
|
599
544
|
// Metallic liquid effect with shimmer
|
|
@@ -630,7 +575,7 @@ export const fragmentShaders = {
|
|
|
630
575
|
const totalX = ix + (wave1 + shimmer + Math.cos(flowAngle) * flowEffect) * mask;
|
|
631
576
|
const totalY = iy + (wave2 + shimmer * 0.8 + Math.sin(flowAngle) * flowEffect) * mask;
|
|
632
577
|
|
|
633
|
-
return createTexture(
|
|
578
|
+
return createTexture(clamp(totalX + 0.5, 0, 1), clamp(totalY + 0.5, 0, 1));
|
|
634
579
|
},
|
|
635
580
|
|
|
636
581
|
// basiBasi - Expert Premium Glass Shader
|
|
@@ -784,7 +729,7 @@ export const fragmentShaders = {
|
|
|
784
729
|
const finalX = ix + totalDistortionX * 0.85; // Scale down for subtlety
|
|
785
730
|
const finalY = iy + totalDistortionY * 0.85;
|
|
786
731
|
|
|
787
|
-
return createTexture(
|
|
732
|
+
return createTexture(clamp(finalX + 0.5, 0, 1), clamp(finalY + 0.5, 0, 1));
|
|
788
733
|
},
|
|
789
734
|
|
|
790
735
|
// Aliases for compatibility
|
|
@@ -903,13 +848,13 @@ export class ShaderDisplacementGenerator {
|
|
|
903
848
|
const g = smoothedDy / maxScale + 0.5;
|
|
904
849
|
|
|
905
850
|
const pixelIndex = (y * w + x) * 4;
|
|
906
|
-
data[pixelIndex] =
|
|
907
|
-
data[pixelIndex + 1] =
|
|
851
|
+
data[pixelIndex] = clamp(r * 255, NORMALIZATION_CLAMP.min, NORMALIZATION_CLAMP.max); // Red channel (X displacement)
|
|
852
|
+
data[pixelIndex + 1] = clamp(
|
|
908
853
|
g * 255,
|
|
909
854
|
NORMALIZATION_CLAMP.min,
|
|
910
855
|
NORMALIZATION_CLAMP.max
|
|
911
856
|
); // Green channel (Y displacement)
|
|
912
|
-
data[pixelIndex + 2] =
|
|
857
|
+
data[pixelIndex + 2] = clamp(
|
|
913
858
|
g * 255,
|
|
914
859
|
NORMALIZATION_CLAMP.min,
|
|
915
860
|
NORMALIZATION_CLAMP.max
|
|
@@ -951,6 +896,3 @@ export class ShaderDisplacementGenerator {
|
|
|
951
896
|
return this.canvasDPI;
|
|
952
897
|
}
|
|
953
898
|
}
|
|
954
|
-
|
|
955
|
-
// Re-export animation system functions for convenience
|
|
956
|
-
export { createFBMEngine, liquidGlassWithTime } from './animation-system';
|
|
@@ -20,9 +20,9 @@ describe('Select Component', () => {
|
|
|
20
20
|
// Check native select options
|
|
21
21
|
const select = document.querySelector('select');
|
|
22
22
|
expect(select).not.toBeNull();
|
|
23
|
-
expect(select
|
|
24
|
-
expect(select
|
|
25
|
-
expect(select
|
|
23
|
+
expect(select!.options).toHaveLength(3); // Placeholder + 2
|
|
24
|
+
expect(select!.options[1]!.value).toBe('1');
|
|
25
|
+
expect(select!.options[2]!.value).toBe('2');
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
it('renders compound options correctly', async () => {
|
|
@@ -43,9 +43,9 @@ describe('Select Component', () => {
|
|
|
43
43
|
await waitFor(() => {
|
|
44
44
|
const select = document.querySelector('select');
|
|
45
45
|
expect(select).not.toBeNull();
|
|
46
|
-
expect(select
|
|
47
|
-
expect(select
|
|
48
|
-
expect(select
|
|
46
|
+
expect(select!.options).toHaveLength(3); // Placeholder + 2
|
|
47
|
+
expect(select!.options[1]!.value).toBe('1');
|
|
48
|
+
expect(select!.options[2]!.value).toBe('2');
|
|
49
49
|
});
|
|
50
50
|
});
|
|
51
51
|
|
|
@@ -104,7 +104,7 @@ Textarea component provides a multi-line text input field for longer content. It
|
|
|
104
104
|
description: 'Number of visible text lines',
|
|
105
105
|
table: {
|
|
106
106
|
type: { summary: 'number' },
|
|
107
|
-
defaultValue: { summary: 2 },
|
|
107
|
+
defaultValue: { summary: '2' },
|
|
108
108
|
},
|
|
109
109
|
},
|
|
110
110
|
cols: {
|
|
@@ -138,7 +138,7 @@ Textarea component provides a multi-line text input field for longer content. It
|
|
|
138
138
|
description: 'Whether the textarea is disabled',
|
|
139
139
|
table: {
|
|
140
140
|
type: { summary: 'boolean' },
|
|
141
|
-
defaultValue: { summary: false },
|
|
141
|
+
defaultValue: { summary: 'false' },
|
|
142
142
|
},
|
|
143
143
|
},
|
|
144
144
|
invalid: {
|
|
@@ -146,7 +146,7 @@ Textarea component provides a multi-line text input field for longer content. It
|
|
|
146
146
|
description: 'Whether the textarea is invalid',
|
|
147
147
|
table: {
|
|
148
148
|
type: { summary: 'boolean' },
|
|
149
|
-
defaultValue: { summary: false },
|
|
149
|
+
defaultValue: { summary: 'false' },
|
|
150
150
|
},
|
|
151
151
|
},
|
|
152
152
|
valid: {
|
|
@@ -154,7 +154,7 @@ Textarea component provides a multi-line text input field for longer content. It
|
|
|
154
154
|
description: 'Whether the textarea is valid',
|
|
155
155
|
table: {
|
|
156
156
|
type: { summary: 'boolean' },
|
|
157
|
-
defaultValue: { summary: false },
|
|
157
|
+
defaultValue: { summary: 'false' },
|
|
158
158
|
},
|
|
159
159
|
},
|
|
160
160
|
glass: {
|
|
@@ -162,7 +162,7 @@ Textarea component provides a multi-line text input field for longer content. It
|
|
|
162
162
|
description: 'Enable glass morphism effect',
|
|
163
163
|
table: {
|
|
164
164
|
type: { summary: 'boolean' },
|
|
165
|
-
defaultValue: { summary: false },
|
|
165
|
+
defaultValue: { summary: 'false' },
|
|
166
166
|
},
|
|
167
167
|
},
|
|
168
168
|
defaultValue: {
|
|
@@ -10,13 +10,11 @@ import { ATOMIX_GLASS } from '../constants/components';
|
|
|
10
10
|
import { globalMouseTracker } from './shared-mouse-tracker';
|
|
11
11
|
import {
|
|
12
12
|
calculateElementCenter,
|
|
13
|
-
calculateMouseInfluence,
|
|
14
13
|
extractBorderRadiusFromChildren,
|
|
15
14
|
extractBorderRadiusFromDOMElement,
|
|
16
|
-
|
|
15
|
+
getInteractionIntensity,
|
|
17
16
|
lerp,
|
|
18
17
|
calculateSpring,
|
|
19
|
-
calculateVelocity,
|
|
20
18
|
smoothstep,
|
|
21
19
|
} from '../../components/AtomixGlass/glass-utils';
|
|
22
20
|
import {
|
|
@@ -24,13 +22,6 @@ import {
|
|
|
24
22
|
type ResolvedGlassBorderConfig,
|
|
25
23
|
} from '../../components/AtomixGlass/glass-border-styles';
|
|
26
24
|
import { updateAtomixGlassStyles } from './useAtomixGlassStyles';
|
|
27
|
-
// Phase 1: Time-Based Animation System
|
|
28
|
-
import {
|
|
29
|
-
createAnimationLoop,
|
|
30
|
-
createFBMEngine,
|
|
31
|
-
getFBMConfigForQuality,
|
|
32
|
-
liquidGlassWithTime,
|
|
33
|
-
} from '../../components/AtomixGlass/animation-system';
|
|
34
25
|
|
|
35
26
|
const { CONSTANTS } = ATOMIX_GLASS;
|
|
36
27
|
|
|
@@ -157,13 +148,7 @@ interface UseAtomixGlassOptions extends Omit<AtomixGlassProps, 'children'> {
|
|
|
157
148
|
children?: React.ReactNode;
|
|
158
149
|
isFixedOrSticky?: boolean;
|
|
159
150
|
priority?: number; // Priority for z-index ordering
|
|
160
|
-
// Phase 1: Time-Based Animation System
|
|
161
151
|
withLiquidBlur?: boolean;
|
|
162
|
-
animationQuality?: 'low' | 'medium' | 'high';
|
|
163
|
-
timeSpeed?: number;
|
|
164
|
-
noiseAmplitude?: number;
|
|
165
|
-
noiseFrequency?: number;
|
|
166
|
-
displacementStrength?: number;
|
|
167
152
|
}
|
|
168
153
|
|
|
169
154
|
interface UseAtomixGlassReturn {
|
|
@@ -198,10 +183,6 @@ interface UseAtomixGlassReturn {
|
|
|
198
183
|
/** Resolved liquid glass rim configuration */
|
|
199
184
|
resolvedBorder: ResolvedGlassBorderConfig;
|
|
200
185
|
|
|
201
|
-
// Phase 1: Animation System - Shader time control
|
|
202
|
-
getShaderTime: () => number;
|
|
203
|
-
applyTimeBasedDistortion: (uv: { x: number; y: number }) => { x: number; y: number };
|
|
204
|
-
|
|
205
186
|
// Event handlers
|
|
206
187
|
handleMouseEnter: () => void;
|
|
207
188
|
handleMouseLeave: () => void;
|
|
@@ -241,14 +222,6 @@ export function useAtomixGlass({
|
|
|
241
222
|
withLiquidBlur,
|
|
242
223
|
isFixedOrSticky = false,
|
|
243
224
|
priority = 1, // Default priority
|
|
244
|
-
// Phase 1: Animation System Props
|
|
245
|
-
withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION,
|
|
246
|
-
animationSpeed = ATOMIX_GLASS.DEFAULTS.ANIMATION_SPEED,
|
|
247
|
-
withMultiLayerDistortion = ATOMIX_GLASS.DEFAULTS.WITH_MULTI_LAYER_DISTORTION,
|
|
248
|
-
distortionOctaves = ATOMIX_GLASS.DEFAULTS.DISTORTION_OCTAVES,
|
|
249
|
-
distortionLacunarity = ATOMIX_GLASS.DEFAULTS.DISTORTION_LACUNARITY,
|
|
250
|
-
distortionGain = ATOMIX_GLASS.DEFAULTS.DISTORTION_GAIN,
|
|
251
|
-
distortionQuality = ATOMIX_GLASS.DEFAULTS.DISTORTION_QUALITY,
|
|
252
225
|
}: UseAtomixGlassOptions): UseAtomixGlassReturn {
|
|
253
226
|
// State
|
|
254
227
|
const [isHovered, setIsHovered] = useState(false);
|
|
@@ -281,46 +254,12 @@ export function useAtomixGlass({
|
|
|
281
254
|
const elasticVelocityRef = useRef<MousePosition>({ x: 0, y: 0 });
|
|
282
255
|
const directionalScaleRef = useRef<{ x: number; y: number }>({ x: 1, y: 1 });
|
|
283
256
|
const scaleVelocityRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
|
284
|
-
const lastMouseTimeRef = useRef<number>(0);
|
|
285
257
|
const mouseVelocityRef = useRef<MousePosition>({ x: 0, y: 0 });
|
|
286
258
|
|
|
287
259
|
const [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(false);
|
|
288
260
|
const [userPrefersHighContrast, setUserPrefersHighContrast] = useState(false);
|
|
289
261
|
const [detectedOverLight, setDetectedOverLight] = useState(false);
|
|
290
262
|
|
|
291
|
-
// ============================================================================
|
|
292
|
-
// Phase 1: Time-Based Animation System (Feature 1.1)
|
|
293
|
-
// ============================================================================
|
|
294
|
-
|
|
295
|
-
// Animation state refs
|
|
296
|
-
const animationFrameIdRef = useRef<number | null>(null);
|
|
297
|
-
const animationStartTimeRef = useRef<number>(0);
|
|
298
|
-
const elapsedTimeRef = useRef<number>(0);
|
|
299
|
-
const shaderTimeRef = useRef<number>(0);
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Get FBM configuration based on quality preset or custom values
|
|
303
|
-
*/
|
|
304
|
-
const fbmConfig = useMemo(() => {
|
|
305
|
-
// If quality preset is provided, use it as base
|
|
306
|
-
const preset = getFBMConfigForQuality(distortionQuality);
|
|
307
|
-
|
|
308
|
-
// Override with custom values if provided
|
|
309
|
-
return {
|
|
310
|
-
octaves: distortionOctaves ?? preset.octaves,
|
|
311
|
-
lacunarity: distortionLacunarity ?? preset.lacunarity,
|
|
312
|
-
gain: distortionGain ?? preset.gain,
|
|
313
|
-
};
|
|
314
|
-
}, [distortionQuality, distortionOctaves, distortionLacunarity, distortionGain]);
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Create FBM engine for multi-layer distortion
|
|
318
|
-
*/
|
|
319
|
-
const fbmEngine = useMemo(() => {
|
|
320
|
-
if (!withMultiLayerDistortion) return null;
|
|
321
|
-
return createFBMEngine(fbmConfig);
|
|
322
|
-
}, [withMultiLayerDistortion, fbmConfig]);
|
|
323
|
-
|
|
324
263
|
/**
|
|
325
264
|
* Determine effective animation settings
|
|
326
265
|
*/
|
|
@@ -329,74 +268,6 @@ export function useAtomixGlass({
|
|
|
329
268
|
[reducedMotion, userPrefersReducedMotion]
|
|
330
269
|
);
|
|
331
270
|
|
|
332
|
-
const effectiveWithTimeAnimation = useMemo(() => {
|
|
333
|
-
return withTimeAnimation && !effectiveReducedMotion;
|
|
334
|
-
}, [withTimeAnimation, effectiveReducedMotion]);
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Animation loop for time-based effects
|
|
338
|
-
*/
|
|
339
|
-
useEffect(() => {
|
|
340
|
-
if (!effectiveWithTimeAnimation || typeof window === 'undefined') {
|
|
341
|
-
return undefined;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
let lastFrameTime = performance.now();
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Animation frame handler
|
|
348
|
-
*/
|
|
349
|
-
const animate = (currentTime: number) => {
|
|
350
|
-
// Calculate delta time
|
|
351
|
-
const deltaTime = currentTime - lastFrameTime;
|
|
352
|
-
lastFrameTime = currentTime;
|
|
353
|
-
|
|
354
|
-
// Apply animation speed multiplier
|
|
355
|
-
const scaledDelta = deltaTime * animationSpeed;
|
|
356
|
-
elapsedTimeRef.current += scaledDelta;
|
|
357
|
-
shaderTimeRef.current = elapsedTimeRef.current;
|
|
358
|
-
|
|
359
|
-
// Continue animation loop
|
|
360
|
-
animationFrameIdRef.current = requestAnimationFrame(animate);
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
// Start animation
|
|
364
|
-
animationStartTimeRef.current = performance.now();
|
|
365
|
-
animationFrameIdRef.current = requestAnimationFrame(animate);
|
|
366
|
-
|
|
367
|
-
// Cleanup
|
|
368
|
-
return () => {
|
|
369
|
-
if (animationFrameIdRef.current !== null) {
|
|
370
|
-
cancelAnimationFrame(animationFrameIdRef.current);
|
|
371
|
-
animationFrameIdRef.current = null;
|
|
372
|
-
}
|
|
373
|
-
};
|
|
374
|
-
}, [effectiveWithTimeAnimation, animationSpeed]);
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Get current shader time for animations
|
|
378
|
-
*/
|
|
379
|
-
const getShaderTime = useCallback(() => {
|
|
380
|
-
return shaderTimeRef.current;
|
|
381
|
-
}, []);
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Apply time-based distortion to UV coordinates
|
|
385
|
-
*/
|
|
386
|
-
const applyTimeBasedDistortion = useCallback(
|
|
387
|
-
(uv: { x: number; y: number }): { x: number; y: number } => {
|
|
388
|
-
if (!effectiveWithTimeAnimation || !fbmEngine) {
|
|
389
|
-
return uv;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const time = shaderTimeRef.current;
|
|
393
|
-
|
|
394
|
-
// Apply liquid glass distortion with time
|
|
395
|
-
return liquidGlassWithTime(uv, time, fbmConfig);
|
|
396
|
-
},
|
|
397
|
-
[effectiveWithTimeAnimation, fbmEngine, fbmConfig]
|
|
398
|
-
);
|
|
399
|
-
|
|
400
271
|
// Memoized derived values
|
|
401
272
|
const effectiveBorderRadius = useMemo(() => {
|
|
402
273
|
if (borderRadius !== undefined) {
|
|
@@ -680,8 +551,7 @@ export function useAtomixGlass({
|
|
|
680
551
|
|
|
681
552
|
const overLightConfig = useMemo(() => {
|
|
682
553
|
const isOverLight = getEffectiveOverLight();
|
|
683
|
-
const hoverIntensity = isHovered
|
|
684
|
-
const activeIntensity = isActive ? 1.6 : 1;
|
|
554
|
+
const { hoverIntensity, activeIntensity } = getInteractionIntensity(isHovered, isActive);
|
|
685
555
|
|
|
686
556
|
// More robust overlight configuration with better defaults and clamping
|
|
687
557
|
const baseOpacity = isOverLight
|
|
@@ -1223,8 +1093,6 @@ export function useAtomixGlass({
|
|
|
1223
1093
|
overLightConfig,
|
|
1224
1094
|
resolvedBorder,
|
|
1225
1095
|
transformStyle,
|
|
1226
|
-
getShaderTime,
|
|
1227
|
-
applyTimeBasedDistortion,
|
|
1228
1096
|
handleMouseEnter,
|
|
1229
1097
|
handleMouseLeave,
|
|
1230
1098
|
handleMouseDown,
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from '../../components/AtomixGlass/glass-border-styles';
|
|
6
6
|
import {
|
|
7
7
|
calculateMouseInfluence,
|
|
8
|
+
getInteractionIntensity,
|
|
8
9
|
validateGlassSize,
|
|
9
10
|
clampBlur,
|
|
10
11
|
smoothstep,
|
|
@@ -84,8 +85,7 @@ export const updateAtomixGlassStyles = (
|
|
|
84
85
|
|
|
85
86
|
// Calculate mouse influence
|
|
86
87
|
const mouseInfluence = calculateMouseInfluence(mouseOffset);
|
|
87
|
-
const hoverIntensity = isHovered
|
|
88
|
-
const activeIntensity = isActive ? 1.6 : 1;
|
|
88
|
+
const { hoverIntensity, activeIntensity } = getInteractionIntensity(isHovered, isActive);
|
|
89
89
|
|
|
90
90
|
// Calculate dynamic OverLight config
|
|
91
91
|
const overLightConfig = {
|
|
@@ -360,7 +360,7 @@ export const updateAtomixGlassStyles = (
|
|
|
360
360
|
'--atomix-glass-container-box-shadow',
|
|
361
361
|
isOverLight
|
|
362
362
|
? '0px 16px 70px rgba(0, 0, 0, 0.75)'
|
|
363
|
-
: '0
|
|
363
|
+
: '0 4px 16px rgba(0, 0, 0, 0.14), 0 1px 4px rgba(0, 0, 0, 0.10)'
|
|
364
364
|
);
|
|
365
365
|
}
|
|
366
366
|
};
|