@alexandretav/aleui 1.0.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +208 -105
- package/cli.js +142 -0
- package/package.json +10 -8
- package/src/components/accordion/accordion.tsx +103 -0
- package/src/components/accordion/index.ts +2 -0
- package/src/components/aero-bubble/aero-bubble.tsx +51 -0
- package/src/components/aero-bubble/index.ts +2 -0
- package/src/components/aero-button/aero-button.tsx +49 -0
- package/src/components/aero-button/index.ts +2 -0
- package/src/components/aero-button/shiny-button.tsx +83 -0
- package/src/components/aero-card/aero-card.tsx +43 -0
- package/src/components/aero-card/index.ts +2 -0
- package/src/components/aero-input/aero-input.tsx +41 -0
- package/src/components/aero-input/index.ts +2 -0
- package/src/components/button/button.tsx +50 -0
- package/src/components/button/index.ts +2 -0
- package/src/components/card/card.tsx +43 -0
- package/src/components/card/index.ts +2 -0
- package/src/components/index.ts +30 -0
- package/src/components/input/index.ts +2 -0
- package/src/components/input/input.tsx +49 -0
- package/src/components/modal/index.ts +2 -0
- package/src/components/modal/modal.tsx +82 -0
- package/src/theme/glass.ts +124 -0
- package/src/utils/index.ts +39 -0
- package/dist/index.d.mts +0 -137
- package/dist/index.d.ts +0 -137
- package/dist/index.js +0 -417
- package/dist/index.mjs +0 -371
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface AeroBubbleProps {
|
|
4
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
5
|
+
color?: 'cyan' | 'blue' | 'lime' | 'sky';
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const sizeClasses = {
|
|
10
|
+
sm: 'w-12 h-12',
|
|
11
|
+
md: 'w-20 h-20',
|
|
12
|
+
lg: 'w-32 h-32',
|
|
13
|
+
xl: 'w-48 h-48',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const colorClasses = {
|
|
17
|
+
cyan: 'bg-gradient-to-br from-cyan-300/40 to-cyan-500/20',
|
|
18
|
+
blue: 'bg-gradient-to-br from-blue-300/40 to-blue-500/20',
|
|
19
|
+
lime: 'bg-gradient-to-br from-lime-300/40 to-lime-500/20',
|
|
20
|
+
sky: 'bg-gradient-to-br from-sky-300/40 to-sky-500/20',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const AeroBubble: React.FC<AeroBubbleProps> = ({
|
|
24
|
+
size = 'md',
|
|
25
|
+
color = 'cyan',
|
|
26
|
+
className = '',
|
|
27
|
+
}) => {
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
className={`
|
|
31
|
+
${sizeClasses[size]}
|
|
32
|
+
${colorClasses[color]}
|
|
33
|
+
rounded-full
|
|
34
|
+
backdrop-blur-sm
|
|
35
|
+
border border-white/30
|
|
36
|
+
shadow-[0_8px_32px_0_rgba(31,38,135,0.37)]
|
|
37
|
+
relative
|
|
38
|
+
overflow-hidden
|
|
39
|
+
${className}
|
|
40
|
+
`}
|
|
41
|
+
>
|
|
42
|
+
{/* Shine effect */}
|
|
43
|
+
<div className="absolute inset-0 bg-gradient-to-br from-white/40 via-transparent to-transparent rounded-full"></div>
|
|
44
|
+
|
|
45
|
+
{/* Inner glow */}
|
|
46
|
+
<div className="absolute bottom-2 right-2 w-1/3 h-1/3 bg-white/30 rounded-full blur-md"></div>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default AeroBubble;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { getAeroClasses, AeroVariantType } from '@/theme/glass';
|
|
3
|
+
|
|
4
|
+
export interface AeroButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
5
|
+
variant?: AeroVariantType;
|
|
6
|
+
size?: 'sm' | 'md' | 'lg';
|
|
7
|
+
fullWidth?: boolean;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const sizeClasses = {
|
|
12
|
+
sm: 'px-4 py-2 text-sm',
|
|
13
|
+
md: 'px-6 py-3 text-base',
|
|
14
|
+
lg: 'px-8 py-4 text-lg',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const AeroButton: React.FC<AeroButtonProps> = ({
|
|
18
|
+
variant = 'light',
|
|
19
|
+
size = 'md',
|
|
20
|
+
fullWidth = false,
|
|
21
|
+
children,
|
|
22
|
+
className = '',
|
|
23
|
+
disabled = false,
|
|
24
|
+
...props
|
|
25
|
+
}) => {
|
|
26
|
+
const aeroClasses = getAeroClasses(variant, 'rounded-lg');
|
|
27
|
+
const sizeClass = sizeClasses[size];
|
|
28
|
+
const widthClass = fullWidth ? 'w-full' : '';
|
|
29
|
+
const disabledClass = disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer';
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<button
|
|
33
|
+
className={`
|
|
34
|
+
${aeroClasses}
|
|
35
|
+
${sizeClass}
|
|
36
|
+
${widthClass}
|
|
37
|
+
${disabledClass}
|
|
38
|
+
font-medium text-white
|
|
39
|
+
${className}
|
|
40
|
+
`}
|
|
41
|
+
disabled={disabled}
|
|
42
|
+
{...props}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
</button>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default AeroButton;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { motion } from 'framer-motion';
|
|
5
|
+
import { cn } from '@/utils';
|
|
6
|
+
|
|
7
|
+
const animation = {
|
|
8
|
+
initial: { "--x": "-100%", scale: 1 },
|
|
9
|
+
animate: { "--x": "-100%", scale: 1 },
|
|
10
|
+
whileHover: { scale: 1.05 },
|
|
11
|
+
whileTap: { scale: 0.95 },
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const sizeClasses = {
|
|
15
|
+
sm: 'px-4 py-2 text-sm',
|
|
16
|
+
md: 'px-6 py-3 text-base',
|
|
17
|
+
lg: 'px-8 py-4 text-lg',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
interface ShinyButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
21
|
+
size?: 'sm' | 'md' | 'lg';
|
|
22
|
+
fullWidth?: boolean;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
onClick?: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const ShinyButton: React.FC<ShinyButtonProps> = ({
|
|
28
|
+
onClick,
|
|
29
|
+
size = 'md',
|
|
30
|
+
fullWidth = false,
|
|
31
|
+
className = '',
|
|
32
|
+
children,
|
|
33
|
+
disabled = false,
|
|
34
|
+
}) => {
|
|
35
|
+
const sizeClass = sizeClasses[size];
|
|
36
|
+
const widthClass = fullWidth ? 'w-full' : '';
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<motion.button
|
|
40
|
+
{...animation}
|
|
41
|
+
onClick={onClick}
|
|
42
|
+
disabled={disabled}
|
|
43
|
+
className={cn(
|
|
44
|
+
"relative rounded-xl overflow-hidden font-medium",
|
|
45
|
+
sizeClass,
|
|
46
|
+
widthClass,
|
|
47
|
+
"bg-gradient-to-br from-cyan-400/30 via-blue-400/25 to-cyan-500/30",
|
|
48
|
+
"backdrop-blur-lg border-2 border-cyan-300/40",
|
|
49
|
+
"shadow-[0_0_30px_rgba(0,206,209,0.4),inset_0_0_20px_rgba(255,255,255,0.1)]",
|
|
50
|
+
"hover:shadow-[0_0_50px_rgba(0,206,209,0.6),inset_0_0_30px_rgba(255,255,255,0.2)]",
|
|
51
|
+
"hover:border-cyan-300/60",
|
|
52
|
+
"transition-all duration-300",
|
|
53
|
+
disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
|
|
54
|
+
className,
|
|
55
|
+
)}
|
|
56
|
+
>
|
|
57
|
+
{/* Shine effect */}
|
|
58
|
+
<motion.span
|
|
59
|
+
className="absolute inset-0 z-0"
|
|
60
|
+
animate={{
|
|
61
|
+
x: ["0%", "200%"],
|
|
62
|
+
}}
|
|
63
|
+
transition={{
|
|
64
|
+
duration: 3,
|
|
65
|
+
repeat: Infinity,
|
|
66
|
+
ease: "linear",
|
|
67
|
+
}}
|
|
68
|
+
style={{
|
|
69
|
+
background: "linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.4) 50%, transparent 100%)",
|
|
70
|
+
width: "50%",
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
{/* Inner glow */}
|
|
75
|
+
<span className="absolute inset-0 bg-gradient-to-t from-transparent via-white/5 to-white/10 z-0" />
|
|
76
|
+
|
|
77
|
+
{/* Text */}
|
|
78
|
+
<span className="relative z-10 text-white font-semibold drop-shadow-[0_2px_8px_rgba(0,0,0,0.5)]">
|
|
79
|
+
{children}
|
|
80
|
+
</span>
|
|
81
|
+
</motion.button>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { getAeroClasses, AeroVariantType } from '@/theme/glass';
|
|
3
|
+
|
|
4
|
+
export interface AeroCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
variant?: AeroVariantType;
|
|
6
|
+
padding?: 'none' | 'sm' | 'md' | 'lg';
|
|
7
|
+
enableInteractions?: boolean;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const paddingClasses = {
|
|
12
|
+
none: '',
|
|
13
|
+
sm: 'p-4',
|
|
14
|
+
md: 'p-6',
|
|
15
|
+
lg: 'p-8',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const AeroCard: React.FC<AeroCardProps> = ({
|
|
19
|
+
variant = 'light',
|
|
20
|
+
padding = 'md',
|
|
21
|
+
enableInteractions = false,
|
|
22
|
+
children,
|
|
23
|
+
className = '',
|
|
24
|
+
...props
|
|
25
|
+
}) => {
|
|
26
|
+
const aeroClasses = getAeroClasses(variant, 'rounded-xl', enableInteractions);
|
|
27
|
+
const paddingClass = paddingClasses[padding];
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
className={`
|
|
32
|
+
${aeroClasses}
|
|
33
|
+
${paddingClass}
|
|
34
|
+
${className}
|
|
35
|
+
`}
|
|
36
|
+
{...props}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default AeroCard;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { getAeroClasses, AeroVariantType } from '@/theme/glass';
|
|
3
|
+
|
|
4
|
+
export interface AeroInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
5
|
+
variant?: AeroVariantType;
|
|
6
|
+
label?: string;
|
|
7
|
+
fullWidth?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const AeroInput: React.FC<AeroInputProps> = ({
|
|
11
|
+
variant = 'light',
|
|
12
|
+
label,
|
|
13
|
+
fullWidth = false,
|
|
14
|
+
className = '',
|
|
15
|
+
...props
|
|
16
|
+
}) => {
|
|
17
|
+
const aeroClasses = getAeroClasses(variant, 'rounded-lg');
|
|
18
|
+
const widthClass = fullWidth ? 'w-full' : '';
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className={`flex flex-col gap-2 ${widthClass}`}>
|
|
22
|
+
{label && (
|
|
23
|
+
<label className="text-white/90 text-sm font-medium">
|
|
24
|
+
{label}
|
|
25
|
+
</label>
|
|
26
|
+
)}
|
|
27
|
+
<input
|
|
28
|
+
className={`
|
|
29
|
+
${aeroClasses}
|
|
30
|
+
px-4 py-3
|
|
31
|
+
${widthClass}
|
|
32
|
+
text-white placeholder-white/50
|
|
33
|
+
${className}
|
|
34
|
+
`}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default AeroInput;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { getGlassClasses, GlassVariantType } from '@/theme/glass';
|
|
3
|
+
|
|
4
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
5
|
+
variant?: GlassVariantType;
|
|
6
|
+
size?: 'sm' | 'md' | 'lg';
|
|
7
|
+
fullWidth?: boolean;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const sizeClasses = {
|
|
12
|
+
sm: 'px-4 py-2 text-sm',
|
|
13
|
+
md: 'px-6 py-3 text-base',
|
|
14
|
+
lg: 'px-8 py-4 text-lg',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
export const Button: React.FC<ButtonProps> = ({
|
|
19
|
+
variant = 'light',
|
|
20
|
+
size = 'md',
|
|
21
|
+
fullWidth = false,
|
|
22
|
+
children,
|
|
23
|
+
className = '',
|
|
24
|
+
disabled = false,
|
|
25
|
+
...props
|
|
26
|
+
}) => {
|
|
27
|
+
const glassClasses = getGlassClasses(variant);
|
|
28
|
+
const sizeClass = sizeClasses[size];
|
|
29
|
+
const widthClass = fullWidth ? 'w-full' : '';
|
|
30
|
+
const disabledClass = disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer';
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<button
|
|
34
|
+
className={`
|
|
35
|
+
${glassClasses}
|
|
36
|
+
${sizeClass}
|
|
37
|
+
${widthClass}
|
|
38
|
+
${disabledClass}
|
|
39
|
+
font-medium text-white
|
|
40
|
+
${className}
|
|
41
|
+
`}
|
|
42
|
+
disabled={disabled}
|
|
43
|
+
{...props}
|
|
44
|
+
>
|
|
45
|
+
{children}
|
|
46
|
+
</button>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default Button;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { getGlassClasses, GlassVariantType } from '@/theme/glass';
|
|
3
|
+
|
|
4
|
+
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
variant?: GlassVariantType;
|
|
6
|
+
padding?: 'none' | 'sm' | 'md' | 'lg';
|
|
7
|
+
enableInteractions?: boolean;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const paddingClasses = {
|
|
12
|
+
none: '',
|
|
13
|
+
sm: 'p-4',
|
|
14
|
+
md: 'p-6',
|
|
15
|
+
lg: 'p-8',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const Card: React.FC<CardProps> = ({
|
|
19
|
+
variant = 'light',
|
|
20
|
+
padding = 'md',
|
|
21
|
+
enableInteractions = false,
|
|
22
|
+
children,
|
|
23
|
+
className = '',
|
|
24
|
+
...props
|
|
25
|
+
}) => {
|
|
26
|
+
const glassClasses = getGlassClasses(variant, undefined, enableInteractions);
|
|
27
|
+
const paddingClass = paddingClasses[padding];
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
className={`
|
|
32
|
+
${glassClasses}
|
|
33
|
+
${paddingClass}
|
|
34
|
+
${className}
|
|
35
|
+
`}
|
|
36
|
+
{...props}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default Card;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glassmorphism UI Component Library
|
|
3
|
+
* Export all components from a single entry point
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { Button } from './button';
|
|
7
|
+
export type { ButtonProps } from './button';
|
|
8
|
+
|
|
9
|
+
export { Card } from './card';
|
|
10
|
+
export type { CardProps } from './card';
|
|
11
|
+
|
|
12
|
+
export { Input } from './input';
|
|
13
|
+
export type { InputProps } from './input';
|
|
14
|
+
|
|
15
|
+
export { Modal } from './modal';
|
|
16
|
+
export type { ModalProps } from './modal';
|
|
17
|
+
|
|
18
|
+
export { AeroBubble } from './aero-bubble';
|
|
19
|
+
export type { AeroBubbleProps } from './aero-bubble';
|
|
20
|
+
|
|
21
|
+
export { Accordion } from './accordion';
|
|
22
|
+
export type { AccordionProps, AccordionItemProps } from './accordion';
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
getGlassClasses,
|
|
26
|
+
getAeroClasses,
|
|
27
|
+
glassVariants,
|
|
28
|
+
aeroVariants,
|
|
29
|
+
} from '@/theme/glass';
|
|
30
|
+
export type { GlassVariantType, AeroVariantType } from '@/theme/glass';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { getGlassClasses, GlassVariantType } from '@/theme/glass';
|
|
3
|
+
|
|
4
|
+
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
5
|
+
variant?: GlassVariantType;
|
|
6
|
+
fullWidth?: boolean;
|
|
7
|
+
label?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Input: React.FC<InputProps> = ({
|
|
11
|
+
variant = 'light',
|
|
12
|
+
fullWidth = false,
|
|
13
|
+
label,
|
|
14
|
+
className = '',
|
|
15
|
+
id,
|
|
16
|
+
...props
|
|
17
|
+
}) => {
|
|
18
|
+
const glassClasses = getGlassClasses(variant);
|
|
19
|
+
const widthClass = fullWidth ? 'w-full' : '';
|
|
20
|
+
const inputId = id || label?.toLowerCase().replace(/\s+/g, '-');
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className={`flex flex-col gap-2 ${widthClass}`}>
|
|
24
|
+
{label && (
|
|
25
|
+
<label
|
|
26
|
+
htmlFor={inputId}
|
|
27
|
+
className="text-white text-sm font-medium pl-1"
|
|
28
|
+
>
|
|
29
|
+
{label}
|
|
30
|
+
</label>
|
|
31
|
+
)}
|
|
32
|
+
<input
|
|
33
|
+
id={inputId}
|
|
34
|
+
className={`
|
|
35
|
+
${glassClasses}
|
|
36
|
+
${widthClass}
|
|
37
|
+
px-4 py-3
|
|
38
|
+
text-white placeholder-white/50
|
|
39
|
+
transition-all duration-300
|
|
40
|
+
focus:outline-none focus:ring-2 focus:ring-white/50
|
|
41
|
+
${className}
|
|
42
|
+
`}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default Input;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { getGlassClasses, GlassVariantType } from '@/theme/glass';
|
|
3
|
+
|
|
4
|
+
export interface ModalProps {
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
variant?: GlassVariantType;
|
|
8
|
+
title?: string;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const sizeClasses = {
|
|
14
|
+
sm: 'max-w-sm',
|
|
15
|
+
md: 'max-w-md',
|
|
16
|
+
lg: 'max-w-lg',
|
|
17
|
+
xl: 'max-w-xl',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const Modal: React.FC<ModalProps> = ({
|
|
21
|
+
isOpen,
|
|
22
|
+
onClose,
|
|
23
|
+
variant = 'medium',
|
|
24
|
+
title,
|
|
25
|
+
children,
|
|
26
|
+
size = 'md',
|
|
27
|
+
}) => {
|
|
28
|
+
if (!isOpen) return null;
|
|
29
|
+
|
|
30
|
+
const glassClasses = getGlassClasses(variant);
|
|
31
|
+
const sizeClass = sizeClasses[size];
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div
|
|
35
|
+
className="fixed inset-0 z-50 flex items-center justify-center p-4 animate-fadeIn"
|
|
36
|
+
onClick={onClose}
|
|
37
|
+
>
|
|
38
|
+
{/* Backdrop */}
|
|
39
|
+
<div className="absolute inset-0 bg-black/50 backdrop-blur-sm" />
|
|
40
|
+
|
|
41
|
+
{/* Modal */}
|
|
42
|
+
<div
|
|
43
|
+
className={`
|
|
44
|
+
${glassClasses}
|
|
45
|
+
${sizeClass}
|
|
46
|
+
w-full p-6 relative z-10
|
|
47
|
+
animate-scaleIn
|
|
48
|
+
`}
|
|
49
|
+
onClick={(e) => e.stopPropagation()}
|
|
50
|
+
>
|
|
51
|
+
{/* Header */}
|
|
52
|
+
{title && (
|
|
53
|
+
<div className="flex items-center justify-between mb-4">
|
|
54
|
+
<h2 className="text-2xl font-bold text-white">{title}</h2>
|
|
55
|
+
<button
|
|
56
|
+
onClick={onClose}
|
|
57
|
+
className="text-white/70 hover:text-white transition-colors p-1"
|
|
58
|
+
aria-label="Close modal"
|
|
59
|
+
>
|
|
60
|
+
<svg
|
|
61
|
+
className="w-6 h-6"
|
|
62
|
+
fill="none"
|
|
63
|
+
strokeLinecap="round"
|
|
64
|
+
strokeLinejoin="round"
|
|
65
|
+
strokeWidth="2"
|
|
66
|
+
viewBox="0 0 24 24"
|
|
67
|
+
stroke="currentColor"
|
|
68
|
+
>
|
|
69
|
+
<path d="M6 18L18 6M6 6l12 12" />
|
|
70
|
+
</svg>
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
|
|
75
|
+
{/* Content */}
|
|
76
|
+
<div className="text-white">{children}</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default Modal;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glassmorphism Theme Configuration
|
|
3
|
+
* Define reusable glassmorphism styles and variants
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface GlassVariant {
|
|
7
|
+
background: string;
|
|
8
|
+
backdropBlur: string;
|
|
9
|
+
border: string;
|
|
10
|
+
shadow: string;
|
|
11
|
+
hover: string;
|
|
12
|
+
focus: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface AeroVariant {
|
|
16
|
+
background: string;
|
|
17
|
+
backdropBlur: string;
|
|
18
|
+
border: string;
|
|
19
|
+
shadow: string;
|
|
20
|
+
hover: string;
|
|
21
|
+
focus: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Traditional Glassmorphism styles
|
|
25
|
+
export const glassVariants = {
|
|
26
|
+
light: {
|
|
27
|
+
background: 'bg-white/5',
|
|
28
|
+
backdropBlur: 'backdrop-blur-[2px]',
|
|
29
|
+
border: 'border border-white/15',
|
|
30
|
+
shadow: 'shadow-lg',
|
|
31
|
+
hover: 'hover:bg-white/10 hover:shadow-xl',
|
|
32
|
+
focus: 'focus:outline-none focus:ring-2 focus:ring-white/10',
|
|
33
|
+
},
|
|
34
|
+
medium: {
|
|
35
|
+
background: 'bg-white/20',
|
|
36
|
+
backdropBlur: 'backdrop-blur-lg',
|
|
37
|
+
border: 'border border-white/30',
|
|
38
|
+
shadow: 'shadow-xl',
|
|
39
|
+
hover: 'hover:bg-white/30 hover:shadow-2xl',
|
|
40
|
+
focus: 'focus:outline-none focus:ring-2 focus:ring-white/50',
|
|
41
|
+
},
|
|
42
|
+
dark: {
|
|
43
|
+
background: 'bg-black/20',
|
|
44
|
+
backdropBlur: 'backdrop-blur-md',
|
|
45
|
+
border: 'border border-white/10',
|
|
46
|
+
shadow: 'shadow-lg',
|
|
47
|
+
hover: 'hover:bg-black/30 hover:shadow-xl',
|
|
48
|
+
focus: 'focus:outline-none focus:ring-2 focus:ring-white/40',
|
|
49
|
+
},
|
|
50
|
+
colored: {
|
|
51
|
+
background: 'bg-gradient-to-br from-white/20 to-white/10',
|
|
52
|
+
backdropBlur: 'backdrop-blur-xl',
|
|
53
|
+
border: 'border border-white/25',
|
|
54
|
+
shadow: 'shadow-2xl',
|
|
55
|
+
hover: 'hover:from-white/30 hover:to-white/20 hover:shadow-[0_8px_32px_0_rgba(255,255,255,0.37)]',
|
|
56
|
+
focus: 'focus:outline-none focus:ring-2 focus:ring-white/60',
|
|
57
|
+
},
|
|
58
|
+
} as const;
|
|
59
|
+
|
|
60
|
+
// Frutiger Aero specific styles
|
|
61
|
+
export const aeroVariants = {
|
|
62
|
+
light: {
|
|
63
|
+
background: 'bg-gradient-to-br from-cyan-400/20 to-blue-400/10',
|
|
64
|
+
backdropBlur: 'backdrop-blur-md',
|
|
65
|
+
border: 'border border-cyan-300/30',
|
|
66
|
+
shadow: 'shadow-aero',
|
|
67
|
+
hover: 'hover:from-cyan-400/30 hover:to-blue-400/20 hover:shadow-aero-lg',
|
|
68
|
+
focus: 'focus:outline-none focus:ring-2 focus:ring-cyan-300/50',
|
|
69
|
+
},
|
|
70
|
+
medium: {
|
|
71
|
+
background: 'bg-gradient-to-br from-sky-400/25 to-cyan-400/15',
|
|
72
|
+
backdropBlur: 'backdrop-blur-lg',
|
|
73
|
+
border: 'border border-sky-300/40',
|
|
74
|
+
shadow: 'shadow-aero-lg',
|
|
75
|
+
hover: 'hover:from-sky-400/35 hover:to-cyan-400/25 hover:shadow-[0_8px_32px_0_rgba(56,189,248,0.37)]',
|
|
76
|
+
focus: 'focus:outline-none focus:ring-2 focus:ring-sky-300/60',
|
|
77
|
+
},
|
|
78
|
+
dark: {
|
|
79
|
+
background: 'bg-gradient-to-br from-blue-500/30 to-cyan-500/20',
|
|
80
|
+
backdropBlur: 'backdrop-blur-md',
|
|
81
|
+
border: 'border border-blue-300/30',
|
|
82
|
+
shadow: 'shadow-aero',
|
|
83
|
+
hover: 'hover:from-blue-500/40 hover:to-cyan-500/30 hover:shadow-aero-lg',
|
|
84
|
+
focus: 'focus:outline-none focus:ring-2 focus:ring-blue-300/50',
|
|
85
|
+
},
|
|
86
|
+
colored: {
|
|
87
|
+
background: 'bg-gradient-to-br from-lime-400/25 via-cyan-400/20 to-blue-400/15',
|
|
88
|
+
backdropBlur: 'backdrop-blur-xl',
|
|
89
|
+
border: 'border border-lime-300/35',
|
|
90
|
+
shadow: 'shadow-aero-lg',
|
|
91
|
+
hover: 'hover:from-lime-400/35 hover:via-cyan-400/30 hover:to-blue-400/25 hover:shadow-[0_8px_32px_0_rgba(163,230,53,0.37)]',
|
|
92
|
+
focus: 'focus:outline-none focus:ring-2 focus:ring-lime-300/60',
|
|
93
|
+
},
|
|
94
|
+
} as const;
|
|
95
|
+
|
|
96
|
+
export type GlassVariantType = keyof typeof glassVariants;
|
|
97
|
+
export type AeroVariantType = keyof typeof aeroVariants;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Generate glassmorphism class string
|
|
101
|
+
*/
|
|
102
|
+
export const getGlassClasses = (
|
|
103
|
+
variant: GlassVariantType = 'light',
|
|
104
|
+
rounded: string = 'rounded-xl',
|
|
105
|
+
includeInteractions: boolean = true
|
|
106
|
+
): string => {
|
|
107
|
+
const v = glassVariants[variant];
|
|
108
|
+
const interactions = includeInteractions ? `transition-all duration-300 ${v.hover} ${v.focus} active:scale-95` : '';
|
|
109
|
+
return `${v.background} ${v.backdropBlur} ${v.border} ${v.shadow} ${rounded} ${interactions}`;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Generate Frutiger Aero class string
|
|
114
|
+
*/
|
|
115
|
+
export const getAeroClasses = (
|
|
116
|
+
variant: AeroVariantType = 'light',
|
|
117
|
+
rounded: string = 'rounded-xl',
|
|
118
|
+
includeInteractions: boolean = true
|
|
119
|
+
): string => {
|
|
120
|
+
const v = aeroVariants[variant];
|
|
121
|
+
const interactions = includeInteractions ? `transition-all duration-300 ${v.hover} ${v.focus} active:scale-95` : '';
|
|
122
|
+
return `${v.background} ${v.backdropBlur} ${v.border} ${v.shadow} ${rounded} ${interactions}`;
|
|
123
|
+
};
|
|
124
|
+
|