@oalacea/chaosui 0.1.0 → 0.4.0
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/bin/cli.js +75 -13
- package/components/backgrounds/glow-orbs/index.tsx +1 -1
- package/components/buttons/chaos-button/chaos-button.module.css +3 -2
- package/components/buttons/glitch-button/glitch-button.module.css +7 -7
- package/components/chaos-vars.css +27 -0
- package/components/cyber/cyber-avatar/css/cyber-avatar.module.css +60 -0
- package/components/cyber/cyber-avatar/css/index.tsx +28 -0
- package/components/cyber/cyber-avatar/tailwind/index.tsx +46 -0
- package/components/cyber/cyber-input/css/cyber-input.module.css +87 -0
- package/components/cyber/cyber-input/css/index.tsx +49 -0
- package/components/cyber/cyber-input/tailwind/index.tsx +55 -0
- package/components/cyber/cyber-loader/css/cyber-loader.module.css +102 -0
- package/components/cyber/cyber-loader/css/index.tsx +58 -0
- package/components/cyber/cyber-loader/tailwind/index.tsx +63 -0
- package/components/cyber/cyber-modal/css/cyber-modal.module.css +124 -0
- package/components/cyber/cyber-modal/css/index.tsx +75 -0
- package/components/cyber/cyber-modal/tailwind/index.tsx +87 -0
- package/components/cyber/cyber-slider/css/cyber-slider.module.css +61 -0
- package/components/cyber/cyber-slider/css/index.tsx +41 -0
- package/components/cyber/cyber-slider/tailwind/index.tsx +51 -0
- package/components/cyber/cyber-tooltip/css/cyber-tooltip.module.css +67 -0
- package/components/cyber/cyber-tooltip/css/index.tsx +36 -0
- package/components/cyber/cyber-tooltip/tailwind/index.tsx +48 -0
- package/components/effects/glitch-image/css/glitch-image.module.css +64 -0
- package/components/effects/glitch-image/css/index.tsx +25 -0
- package/components/effects/glitch-image/tailwind/index.tsx +49 -0
- package/components/effects/glowing-border/css/glowing-border.module.css +73 -0
- package/components/effects/glowing-border/css/index.tsx +45 -0
- package/components/effects/glowing-border/tailwind/index.tsx +40 -0
- package/components/effects/screen-distortion/screen-distortion.module.css +2 -2
- package/components/effects/warning-tape/index.tsx +4 -4
- package/components/effects/warning-tape/warning-tape.module.css +2 -0
- package/components/layout/data-grid/css/data-grid.module.css +52 -0
- package/components/layout/data-grid/css/index.tsx +76 -0
- package/components/layout/data-grid/tailwind/index.tsx +74 -0
- package/components/layout/hologram-card/css/hologram-card.module.css +102 -0
- package/components/layout/hologram-card/css/index.tsx +46 -0
- package/components/layout/hologram-card/tailwind/index.tsx +61 -0
- package/components/navigation/hexagon-menu/css/hexagon-menu.module.css +55 -0
- package/components/navigation/hexagon-menu/css/index.tsx +35 -0
- package/components/navigation/hexagon-menu/tailwind/index.tsx +53 -0
- package/components/neon/neon-alert/css/index.tsx +53 -0
- package/components/neon/neon-alert/css/neon-alert.module.css +60 -0
- package/components/neon/neon-alert/tailwind/index.tsx +59 -0
- package/components/neon/neon-badge/css/index.tsx +49 -0
- package/components/neon/neon-badge/css/neon-badge.module.css +53 -0
- package/components/neon/neon-badge/tailwind/index.tsx +50 -0
- package/components/neon/neon-button/css/index.tsx +54 -0
- package/components/neon/neon-button/css/neon-button.module.css +114 -0
- package/components/neon/neon-button/tailwind/index.tsx +51 -0
- package/components/neon/neon-divider/css/index.tsx +26 -0
- package/components/neon/neon-divider/css/neon-divider.module.css +43 -0
- package/components/neon/neon-divider/tailwind/index.tsx +36 -0
- package/components/neon/neon-progress/css/index.tsx +65 -0
- package/components/neon/neon-progress/css/neon-progress.module.css +88 -0
- package/components/neon/neon-progress/tailwind/index.tsx +46 -0
- package/components/neon/neon-tabs/css/index.tsx +41 -0
- package/components/neon/neon-tabs/css/neon-tabs.module.css +45 -0
- package/components/neon/neon-tabs/tailwind/index.tsx +53 -0
- package/components/neon/neon-toggle/css/index.tsx +58 -0
- package/components/neon/neon-toggle/css/neon-toggle.module.css +79 -0
- package/components/neon/neon-toggle/tailwind/index.tsx +57 -0
- package/components/text/glitch-text/glitch-text.module.css +2 -2
- package/package.json +1 -1
- package/components/glow-orbs/glow-orbs.module.css +0 -31
- package/components/glow-orbs/index.tsx +0 -87
- package/components/light-beams/index.tsx +0 -80
- package/components/light-beams/light-beams.module.css +0 -27
- package/components/noise-canvas/index.tsx +0 -113
- package/components/noise-canvas/noise-canvas.module.css +0 -8
- package/components/package.json +0 -13
- package/components/particle-field/index.tsx +0 -81
- package/components/particle-field/particle-field.module.css +0 -31
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { forwardRef, ButtonHTMLAttributes } from 'react';
|
|
4
|
+
import styles from './neon-toggle.module.css';
|
|
5
|
+
|
|
6
|
+
export interface NeonToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
|
|
7
|
+
/** Current checked state */
|
|
8
|
+
checked: boolean;
|
|
9
|
+
/** Change callback */
|
|
10
|
+
onChange: (checked: boolean) => void;
|
|
11
|
+
/** Neon color variant */
|
|
12
|
+
variant?: 'cyan' | 'pink' | 'green' | 'purple';
|
|
13
|
+
/** Toggle size */
|
|
14
|
+
size?: 'sm' | 'md' | 'lg';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const NeonToggle = forwardRef<HTMLButtonElement, NeonToggleProps>(
|
|
18
|
+
(
|
|
19
|
+
{
|
|
20
|
+
checked,
|
|
21
|
+
onChange,
|
|
22
|
+
variant = 'cyan',
|
|
23
|
+
size = 'md',
|
|
24
|
+
disabled = false,
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
},
|
|
28
|
+
ref
|
|
29
|
+
) => {
|
|
30
|
+
const classes = [
|
|
31
|
+
styles.toggle,
|
|
32
|
+
styles[variant],
|
|
33
|
+
styles[size],
|
|
34
|
+
checked && styles.checked,
|
|
35
|
+
className,
|
|
36
|
+
].filter(Boolean).join(' ');
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<button
|
|
40
|
+
ref={ref}
|
|
41
|
+
type="button"
|
|
42
|
+
role="switch"
|
|
43
|
+
aria-checked={checked}
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
className={classes}
|
|
46
|
+
onClick={() => !disabled && onChange(!checked)}
|
|
47
|
+
{...props}
|
|
48
|
+
>
|
|
49
|
+
<span className={styles.track} />
|
|
50
|
+
<span className={styles.thumb} />
|
|
51
|
+
</button>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
NeonToggle.displayName = 'NeonToggle';
|
|
57
|
+
|
|
58
|
+
export default NeonToggle;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
.toggle {
|
|
2
|
+
--toggle-color: #00f0ff;
|
|
3
|
+
--toggle-width: 3.25rem;
|
|
4
|
+
--toggle-height: 1.75rem;
|
|
5
|
+
--thumb-size: 1.25rem;
|
|
6
|
+
|
|
7
|
+
position: relative;
|
|
8
|
+
width: var(--toggle-width);
|
|
9
|
+
height: var(--toggle-height);
|
|
10
|
+
padding: 0;
|
|
11
|
+
background: transparent;
|
|
12
|
+
border: none;
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Sizes */
|
|
17
|
+
.sm {
|
|
18
|
+
--toggle-width: 2.5rem;
|
|
19
|
+
--toggle-height: 1.375rem;
|
|
20
|
+
--thumb-size: 1rem;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.lg {
|
|
24
|
+
--toggle-width: 4rem;
|
|
25
|
+
--toggle-height: 2.125rem;
|
|
26
|
+
--thumb-size: 1.625rem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Variants */
|
|
30
|
+
.cyan { --toggle-color: #00f0ff; }
|
|
31
|
+
.pink { --toggle-color: #ff00ff; }
|
|
32
|
+
.green { --toggle-color: #00ff88; }
|
|
33
|
+
.purple { --toggle-color: #a855f7; }
|
|
34
|
+
|
|
35
|
+
.track {
|
|
36
|
+
position: absolute;
|
|
37
|
+
top: 0;
|
|
38
|
+
left: 0;
|
|
39
|
+
width: 100%;
|
|
40
|
+
height: 100%;
|
|
41
|
+
background: rgba(255, 255, 255, 0.1);
|
|
42
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
43
|
+
border-radius: var(--toggle-height);
|
|
44
|
+
transition: all 0.3s ease;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.checked .track {
|
|
48
|
+
background: rgba(0, 240, 255, 0.2);
|
|
49
|
+
border-color: var(--toggle-color);
|
|
50
|
+
box-shadow: 0 0 10px var(--toggle-color), inset 0 0 10px rgba(0, 240, 255, 0.1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.thumb {
|
|
54
|
+
position: absolute;
|
|
55
|
+
top: 50%;
|
|
56
|
+
left: 0.25rem;
|
|
57
|
+
width: var(--thumb-size);
|
|
58
|
+
height: var(--thumb-size);
|
|
59
|
+
background: #666;
|
|
60
|
+
border-radius: 50%;
|
|
61
|
+
transform: translateY(-50%);
|
|
62
|
+
transition: all 0.3s ease;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.checked .thumb {
|
|
66
|
+
left: calc(100% - var(--thumb-size) - 0.25rem);
|
|
67
|
+
background: var(--toggle-color);
|
|
68
|
+
box-shadow: 0 0 10px var(--toggle-color), 0 0 20px var(--toggle-color);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.toggle:disabled {
|
|
72
|
+
opacity: 0.5;
|
|
73
|
+
cursor: not-allowed;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.toggle:focus-visible .track {
|
|
77
|
+
outline: 2px solid var(--toggle-color);
|
|
78
|
+
outline-offset: 2px;
|
|
79
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { forwardRef, ButtonHTMLAttributes } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface NeonToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
|
|
6
|
+
checked: boolean;
|
|
7
|
+
onChange: (checked: boolean) => void;
|
|
8
|
+
variant?: 'cyan' | 'pink' | 'green' | 'purple';
|
|
9
|
+
size?: 'sm' | 'md' | 'lg';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const sizeStyles = {
|
|
13
|
+
sm: { track: 'w-10 h-5', thumb: 'w-4 h-4', translate: 'translate-x-5' },
|
|
14
|
+
md: { track: 'w-[52px] h-7', thumb: 'w-5 h-5', translate: 'translate-x-6' },
|
|
15
|
+
lg: { track: 'w-16 h-8', thumb: 'w-6 h-6', translate: 'translate-x-8' },
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const variantStyles = {
|
|
19
|
+
cyan: { active: 'bg-cyan-400/20 border-cyan-400 shadow-[0_0_10px_#00f0ff]', thumb: 'bg-cyan-400 shadow-[0_0_10px_#00f0ff]' },
|
|
20
|
+
pink: { active: 'bg-fuchsia-500/20 border-fuchsia-500 shadow-[0_0_10px_#ff00ff]', thumb: 'bg-fuchsia-500 shadow-[0_0_10px_#ff00ff]' },
|
|
21
|
+
green: { active: 'bg-emerald-400/20 border-emerald-400 shadow-[0_0_10px_#00ff88]', thumb: 'bg-emerald-400 shadow-[0_0_10px_#00ff88]' },
|
|
22
|
+
purple: { active: 'bg-purple-500/20 border-purple-500 shadow-[0_0_10px_#a855f7]', thumb: 'bg-purple-500 shadow-[0_0_10px_#a855f7]' },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const NeonToggle = forwardRef<HTMLButtonElement, NeonToggleProps>(
|
|
26
|
+
({ checked, onChange, variant = 'cyan', size = 'md', disabled = false, className = '', ...props }, ref) => {
|
|
27
|
+
const sizes = sizeStyles[size];
|
|
28
|
+
const colors = variantStyles[variant];
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<button
|
|
32
|
+
ref={ref}
|
|
33
|
+
type="button"
|
|
34
|
+
role="switch"
|
|
35
|
+
aria-checked={checked}
|
|
36
|
+
disabled={disabled}
|
|
37
|
+
className={`
|
|
38
|
+
relative ${sizes.track} rounded-full border transition-all duration-300
|
|
39
|
+
${checked ? colors.active : 'bg-white/10 border-white/20'}
|
|
40
|
+
${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}
|
|
41
|
+
${className}
|
|
42
|
+
`}
|
|
43
|
+
onClick={() => !disabled && onChange(!checked)}
|
|
44
|
+
{...props}
|
|
45
|
+
>
|
|
46
|
+
<span className={`
|
|
47
|
+
absolute top-1/2 left-1 -translate-y-1/2
|
|
48
|
+
${sizes.thumb} rounded-full transition-all duration-300
|
|
49
|
+
${checked ? `${sizes.translate} ${colors.thumb}` : 'bg-gray-500'}
|
|
50
|
+
`} />
|
|
51
|
+
</button>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
NeonToggle.displayName = 'NeonToggle';
|
|
57
|
+
export default NeonToggle;
|
package/package.json
CHANGED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
.container {
|
|
2
|
-
inset: 0;
|
|
3
|
-
width: 100%;
|
|
4
|
-
height: 100%;
|
|
5
|
-
overflow: hidden;
|
|
6
|
-
pointer-events: none;
|
|
7
|
-
z-index: 1;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.orb {
|
|
11
|
-
position: absolute;
|
|
12
|
-
border-radius: 50%;
|
|
13
|
-
opacity: 0.3;
|
|
14
|
-
transform: translate(-50%, -50%);
|
|
15
|
-
animation: float 20s ease-in-out infinite;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
@keyframes float {
|
|
19
|
-
0%, 100% {
|
|
20
|
-
transform: translate(-50%, -50%) translate(0, 0);
|
|
21
|
-
}
|
|
22
|
-
25% {
|
|
23
|
-
transform: translate(-50%, -50%) translate(30px, -40px);
|
|
24
|
-
}
|
|
25
|
-
50% {
|
|
26
|
-
transform: translate(-50%, -50%) translate(-20px, 20px);
|
|
27
|
-
}
|
|
28
|
-
75% {
|
|
29
|
-
transform: translate(-50%, -50%) translate(40px, 30px);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { forwardRef, HTMLAttributes } from 'react';
|
|
4
|
-
import styles from './glow-orbs.module.css';
|
|
5
|
-
|
|
6
|
-
export interface GlowOrbsProps extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
-
/** Array of orb colors (will cycle through) */
|
|
8
|
-
colors?: string[];
|
|
9
|
-
/** Number of orbs */
|
|
10
|
-
count?: number;
|
|
11
|
-
/** Orb size range [min, max] in pixels */
|
|
12
|
-
sizeRange?: [number, number];
|
|
13
|
-
/** Blur amount in pixels */
|
|
14
|
-
blur?: number;
|
|
15
|
-
/** Animation duration range [min, max] in seconds */
|
|
16
|
-
durationRange?: [number, number];
|
|
17
|
-
/** Fixed or absolute positioning */
|
|
18
|
-
position?: 'fixed' | 'absolute';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const GlowOrbs = forwardRef<HTMLDivElement, GlowOrbsProps>(
|
|
22
|
-
(
|
|
23
|
-
{
|
|
24
|
-
colors = ['#7c3aed', '#06b6d4', '#ec4899', '#10b981'],
|
|
25
|
-
count = 5,
|
|
26
|
-
sizeRange = [100, 300],
|
|
27
|
-
blur = 80,
|
|
28
|
-
durationRange = [15, 25],
|
|
29
|
-
position = 'fixed',
|
|
30
|
-
className,
|
|
31
|
-
style,
|
|
32
|
-
...props
|
|
33
|
-
},
|
|
34
|
-
ref
|
|
35
|
-
) => {
|
|
36
|
-
// Generate random orbs
|
|
37
|
-
const orbs = Array.from({ length: count }, (_, i) => {
|
|
38
|
-
const size = sizeRange[0] + Math.random() * (sizeRange[1] - sizeRange[0]);
|
|
39
|
-
const duration = durationRange[0] + Math.random() * (durationRange[1] - durationRange[0]);
|
|
40
|
-
const delay = Math.random() * -duration;
|
|
41
|
-
const color = colors[i % colors.length];
|
|
42
|
-
const startX = Math.random() * 100;
|
|
43
|
-
const startY = Math.random() * 100;
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
id: i,
|
|
47
|
-
size,
|
|
48
|
-
duration,
|
|
49
|
-
delay,
|
|
50
|
-
color,
|
|
51
|
-
startX,
|
|
52
|
-
startY,
|
|
53
|
-
};
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<div
|
|
58
|
-
ref={ref}
|
|
59
|
-
className={`${styles.container} ${className || ''}`}
|
|
60
|
-
style={{ position, ...style }}
|
|
61
|
-
aria-hidden="true"
|
|
62
|
-
{...props}
|
|
63
|
-
>
|
|
64
|
-
{orbs.map((orb) => (
|
|
65
|
-
<div
|
|
66
|
-
key={orb.id}
|
|
67
|
-
className={styles.orb}
|
|
68
|
-
style={{
|
|
69
|
-
width: orb.size,
|
|
70
|
-
height: orb.size,
|
|
71
|
-
background: orb.color,
|
|
72
|
-
filter: `blur(${blur}px)`,
|
|
73
|
-
left: `${orb.startX}%`,
|
|
74
|
-
top: `${orb.startY}%`,
|
|
75
|
-
animationDuration: `${orb.duration}s`,
|
|
76
|
-
animationDelay: `${orb.delay}s`,
|
|
77
|
-
}}
|
|
78
|
-
/>
|
|
79
|
-
))}
|
|
80
|
-
</div>
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
GlowOrbs.displayName = 'GlowOrbs';
|
|
86
|
-
|
|
87
|
-
export default GlowOrbs;
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { forwardRef, HTMLAttributes } from 'react';
|
|
4
|
-
import styles from './light-beams.module.css';
|
|
5
|
-
|
|
6
|
-
export interface LightBeamsProps extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
-
/** Array of beam colors */
|
|
8
|
-
colors?: string[];
|
|
9
|
-
/** Number of beams */
|
|
10
|
-
count?: number;
|
|
11
|
-
/** Beam width in pixels */
|
|
12
|
-
beamWidth?: number;
|
|
13
|
-
/** Beam opacity (0-1) */
|
|
14
|
-
opacity?: number;
|
|
15
|
-
/** Animation duration range [min, max] in seconds */
|
|
16
|
-
durationRange?: [number, number];
|
|
17
|
-
/** Fixed or absolute positioning */
|
|
18
|
-
position?: 'fixed' | 'absolute';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const LightBeams = forwardRef<HTMLDivElement, LightBeamsProps>(
|
|
22
|
-
(
|
|
23
|
-
{
|
|
24
|
-
colors = ['#7c3aed', '#06b6d4', '#ec4899', '#10b981', '#f59e0b'],
|
|
25
|
-
count = 5,
|
|
26
|
-
beamWidth = 2,
|
|
27
|
-
opacity = 0.15,
|
|
28
|
-
durationRange = [15, 25],
|
|
29
|
-
position = 'fixed',
|
|
30
|
-
className,
|
|
31
|
-
style,
|
|
32
|
-
...props
|
|
33
|
-
},
|
|
34
|
-
ref
|
|
35
|
-
) => {
|
|
36
|
-
const beams = Array.from({ length: count }, (_, i) => {
|
|
37
|
-
const duration = durationRange[0] + Math.random() * (durationRange[1] - durationRange[0]);
|
|
38
|
-
const delay = Math.random() * -duration;
|
|
39
|
-
const color = colors[i % colors.length];
|
|
40
|
-
const positionX = ((i + 1) / (count + 1)) * 100;
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
id: i,
|
|
44
|
-
duration,
|
|
45
|
-
delay,
|
|
46
|
-
color,
|
|
47
|
-
positionX,
|
|
48
|
-
};
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<div
|
|
53
|
-
ref={ref}
|
|
54
|
-
className={`${styles.container} ${className || ''}`}
|
|
55
|
-
style={{ position, ...style }}
|
|
56
|
-
aria-hidden="true"
|
|
57
|
-
{...props}
|
|
58
|
-
>
|
|
59
|
-
{beams.map((beam) => (
|
|
60
|
-
<div
|
|
61
|
-
key={beam.id}
|
|
62
|
-
className={styles.beam}
|
|
63
|
-
style={{
|
|
64
|
-
left: `${beam.positionX}%`,
|
|
65
|
-
width: beamWidth,
|
|
66
|
-
background: `linear-gradient(180deg, transparent, ${beam.color}, transparent)`,
|
|
67
|
-
opacity,
|
|
68
|
-
animationDuration: `${beam.duration}s`,
|
|
69
|
-
animationDelay: `${beam.delay}s`,
|
|
70
|
-
}}
|
|
71
|
-
/>
|
|
72
|
-
))}
|
|
73
|
-
</div>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
LightBeams.displayName = 'LightBeams';
|
|
79
|
-
|
|
80
|
-
export default LightBeams;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
.container {
|
|
2
|
-
inset: 0;
|
|
3
|
-
width: 100%;
|
|
4
|
-
height: 100%;
|
|
5
|
-
overflow: hidden;
|
|
6
|
-
pointer-events: none;
|
|
7
|
-
z-index: 1;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.beam {
|
|
11
|
-
position: absolute;
|
|
12
|
-
top: 0;
|
|
13
|
-
height: 100%;
|
|
14
|
-
filter: blur(1px);
|
|
15
|
-
animation: beam-move 20s ease-in-out infinite;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
@keyframes beam-move {
|
|
19
|
-
0%, 100% {
|
|
20
|
-
transform: translateX(-50px) skewX(-5deg);
|
|
21
|
-
opacity: 0.1;
|
|
22
|
-
}
|
|
23
|
-
50% {
|
|
24
|
-
transform: translateX(50px) skewX(5deg);
|
|
25
|
-
opacity: 0.2;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useRef, forwardRef, HTMLAttributes } from 'react';
|
|
4
|
-
import styles from './noise-canvas.module.css';
|
|
5
|
-
|
|
6
|
-
export interface NoiseCanvasProps extends HTMLAttributes<HTMLCanvasElement> {
|
|
7
|
-
/** Noise opacity (0-1) */
|
|
8
|
-
opacity?: number;
|
|
9
|
-
/** Animation speed (fps) */
|
|
10
|
-
fps?: number;
|
|
11
|
-
/** Noise intensity (0-255) */
|
|
12
|
-
intensity?: number;
|
|
13
|
-
/** Monochrome or colored noise */
|
|
14
|
-
monochrome?: boolean;
|
|
15
|
-
/** Fixed or absolute positioning */
|
|
16
|
-
position?: 'fixed' | 'absolute';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const NoiseCanvas = forwardRef<HTMLCanvasElement, NoiseCanvasProps>(
|
|
20
|
-
(
|
|
21
|
-
{
|
|
22
|
-
opacity = 0.05,
|
|
23
|
-
fps = 24,
|
|
24
|
-
intensity = 50,
|
|
25
|
-
monochrome = true,
|
|
26
|
-
position = 'fixed',
|
|
27
|
-
className,
|
|
28
|
-
style,
|
|
29
|
-
...props
|
|
30
|
-
},
|
|
31
|
-
ref
|
|
32
|
-
) => {
|
|
33
|
-
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
34
|
-
const animationRef = useRef<number>();
|
|
35
|
-
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
const canvas = canvasRef.current;
|
|
38
|
-
if (!canvas) return;
|
|
39
|
-
|
|
40
|
-
const ctx = canvas.getContext('2d');
|
|
41
|
-
if (!ctx) return;
|
|
42
|
-
|
|
43
|
-
const resize = () => {
|
|
44
|
-
canvas.width = window.innerWidth;
|
|
45
|
-
canvas.height = window.innerHeight;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
resize();
|
|
49
|
-
window.addEventListener('resize', resize);
|
|
50
|
-
|
|
51
|
-
let lastFrame = 0;
|
|
52
|
-
const frameInterval = 1000 / fps;
|
|
53
|
-
|
|
54
|
-
const render = (timestamp: number) => {
|
|
55
|
-
if (timestamp - lastFrame >= frameInterval) {
|
|
56
|
-
const imageData = ctx.createImageData(canvas.width, canvas.height);
|
|
57
|
-
const data = imageData.data;
|
|
58
|
-
|
|
59
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
60
|
-
const value = Math.random() * intensity;
|
|
61
|
-
|
|
62
|
-
if (monochrome) {
|
|
63
|
-
data[i] = value;
|
|
64
|
-
data[i + 1] = value;
|
|
65
|
-
data[i + 2] = value;
|
|
66
|
-
} else {
|
|
67
|
-
data[i] = Math.random() * intensity;
|
|
68
|
-
data[i + 1] = Math.random() * intensity;
|
|
69
|
-
data[i + 2] = Math.random() * intensity;
|
|
70
|
-
}
|
|
71
|
-
data[i + 3] = 255;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
ctx.putImageData(imageData, 0, 0);
|
|
75
|
-
lastFrame = timestamp;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
animationRef.current = requestAnimationFrame(render);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
animationRef.current = requestAnimationFrame(render);
|
|
82
|
-
|
|
83
|
-
return () => {
|
|
84
|
-
window.removeEventListener('resize', resize);
|
|
85
|
-
if (animationRef.current) {
|
|
86
|
-
cancelAnimationFrame(animationRef.current);
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
}, [fps, intensity, monochrome]);
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<canvas
|
|
93
|
-
ref={(node) => {
|
|
94
|
-
(canvasRef as React.MutableRefObject<HTMLCanvasElement | null>).current = node;
|
|
95
|
-
if (typeof ref === 'function') ref(node);
|
|
96
|
-
else if (ref) ref.current = node;
|
|
97
|
-
}}
|
|
98
|
-
className={`${styles.canvas} ${className || ''}`}
|
|
99
|
-
style={{
|
|
100
|
-
position,
|
|
101
|
-
opacity,
|
|
102
|
-
...style,
|
|
103
|
-
}}
|
|
104
|
-
aria-hidden="true"
|
|
105
|
-
{...props}
|
|
106
|
-
/>
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
NoiseCanvas.displayName = 'NoiseCanvas';
|
|
112
|
-
|
|
113
|
-
export default NoiseCanvas;
|
package/components/package.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@chaos/components",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"description": "Glitch, noise, and distortion UI components",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"exports": {
|
|
7
|
-
"./text/*": "./text/*/index.tsx",
|
|
8
|
-
"./overlays/*": "./overlays/*/index.tsx",
|
|
9
|
-
"./buttons/*": "./buttons/*/index.tsx",
|
|
10
|
-
"./effects/*": "./effects/*/index.tsx",
|
|
11
|
-
"./backgrounds/*": "./backgrounds/*/index.tsx"
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { forwardRef, HTMLAttributes, useMemo } from 'react';
|
|
4
|
-
import styles from './particle-field.module.css';
|
|
5
|
-
|
|
6
|
-
export interface ParticleFieldProps extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
-
/** Number of particles */
|
|
8
|
-
count?: number;
|
|
9
|
-
/** Particle color */
|
|
10
|
-
color?: string;
|
|
11
|
-
/** Particle size range [min, max] in pixels */
|
|
12
|
-
sizeRange?: [number, number];
|
|
13
|
-
/** Animation duration range [min, max] in seconds */
|
|
14
|
-
durationRange?: [number, number];
|
|
15
|
-
/** Particle opacity */
|
|
16
|
-
opacity?: number;
|
|
17
|
-
/** Fixed or absolute positioning */
|
|
18
|
-
position?: 'fixed' | 'absolute';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const ParticleField = forwardRef<HTMLDivElement, ParticleFieldProps>(
|
|
22
|
-
(
|
|
23
|
-
{
|
|
24
|
-
count = 50,
|
|
25
|
-
color = '#ffffff',
|
|
26
|
-
sizeRange = [1, 3],
|
|
27
|
-
durationRange = [10, 20],
|
|
28
|
-
opacity = 0.5,
|
|
29
|
-
position = 'fixed',
|
|
30
|
-
className,
|
|
31
|
-
style,
|
|
32
|
-
...props
|
|
33
|
-
},
|
|
34
|
-
ref
|
|
35
|
-
) => {
|
|
36
|
-
const particles = useMemo(() => {
|
|
37
|
-
return Array.from({ length: count }, (_, i) => {
|
|
38
|
-
const size = sizeRange[0] + Math.random() * (sizeRange[1] - sizeRange[0]);
|
|
39
|
-
const duration = durationRange[0] + Math.random() * (durationRange[1] - durationRange[0]);
|
|
40
|
-
const delay = Math.random() * -duration;
|
|
41
|
-
const startX = Math.random() * 100;
|
|
42
|
-
const startY = Math.random() * 100;
|
|
43
|
-
const drift = (Math.random() - 0.5) * 100;
|
|
44
|
-
|
|
45
|
-
return { id: i, size, duration, delay, startX, startY, drift };
|
|
46
|
-
});
|
|
47
|
-
}, [count, sizeRange, durationRange]);
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<div
|
|
51
|
-
ref={ref}
|
|
52
|
-
className={`${styles.container} ${className || ''}`}
|
|
53
|
-
style={{ position, ...style }}
|
|
54
|
-
aria-hidden="true"
|
|
55
|
-
{...props}
|
|
56
|
-
>
|
|
57
|
-
{particles.map((p) => (
|
|
58
|
-
<div
|
|
59
|
-
key={p.id}
|
|
60
|
-
className={styles.particle}
|
|
61
|
-
style={{
|
|
62
|
-
width: p.size,
|
|
63
|
-
height: p.size,
|
|
64
|
-
backgroundColor: color,
|
|
65
|
-
opacity,
|
|
66
|
-
left: `${p.startX}%`,
|
|
67
|
-
top: `${p.startY}%`,
|
|
68
|
-
animationDuration: `${p.duration}s`,
|
|
69
|
-
animationDelay: `${p.delay}s`,
|
|
70
|
-
'--drift': `${p.drift}px`,
|
|
71
|
-
} as React.CSSProperties}
|
|
72
|
-
/>
|
|
73
|
-
))}
|
|
74
|
-
</div>
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
ParticleField.displayName = 'ParticleField';
|
|
80
|
-
|
|
81
|
-
export default ParticleField;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
.container {
|
|
2
|
-
inset: 0;
|
|
3
|
-
width: 100%;
|
|
4
|
-
height: 100%;
|
|
5
|
-
overflow: hidden;
|
|
6
|
-
pointer-events: none;
|
|
7
|
-
z-index: 1;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.particle {
|
|
11
|
-
position: absolute;
|
|
12
|
-
border-radius: 50%;
|
|
13
|
-
animation: drift linear infinite;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
@keyframes drift {
|
|
17
|
-
0% {
|
|
18
|
-
transform: translate(0, 0);
|
|
19
|
-
opacity: 0;
|
|
20
|
-
}
|
|
21
|
-
10% {
|
|
22
|
-
opacity: var(--opacity, 0.5);
|
|
23
|
-
}
|
|
24
|
-
90% {
|
|
25
|
-
opacity: var(--opacity, 0.5);
|
|
26
|
-
}
|
|
27
|
-
100% {
|
|
28
|
-
transform: translate(var(--drift, 50px), -100vh);
|
|
29
|
-
opacity: 0;
|
|
30
|
-
}
|
|
31
|
-
}
|