@alexandretav/aleui 1.0.0 → 1.1.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/cli.js +125 -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
package/cli.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const COMPONENTS_DIR = path.join(__dirname, 'src', 'components');
|
|
8
|
+
const TARGET_DIR = path.join(process.cwd(), 'src', 'components', 'ui');
|
|
9
|
+
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
const command = args[0];
|
|
12
|
+
const componentName = args[1];
|
|
13
|
+
|
|
14
|
+
const availableComponents = [
|
|
15
|
+
'button',
|
|
16
|
+
'card',
|
|
17
|
+
'input',
|
|
18
|
+
'modal',
|
|
19
|
+
'accordion',
|
|
20
|
+
'aero-bubble',
|
|
21
|
+
'aero-button',
|
|
22
|
+
'aero-card',
|
|
23
|
+
'aero-input'
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function copyComponent(name) {
|
|
27
|
+
const sourcePath = path.join(COMPONENTS_DIR, name);
|
|
28
|
+
const targetPath = path.join(TARGET_DIR, name);
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(sourcePath)) {
|
|
31
|
+
console.error(`❌ Componente "${name}" não encontrado!`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Criar diretório de destino
|
|
36
|
+
if (!fs.existsSync(targetPath)) {
|
|
37
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Copiar arquivos
|
|
41
|
+
const files = fs.readdirSync(sourcePath);
|
|
42
|
+
files.forEach(file => {
|
|
43
|
+
const sourceFile = path.join(sourcePath, file);
|
|
44
|
+
const targetFile = path.join(targetPath, file);
|
|
45
|
+
fs.copyFileSync(sourceFile, targetFile);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
console.log(`✅ Componente "${name}" copiado para ${targetPath}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function copyTheme() {
|
|
52
|
+
const themePath = path.join(__dirname, 'src', 'theme');
|
|
53
|
+
const targetThemePath = path.join(process.cwd(), 'src', 'theme');
|
|
54
|
+
|
|
55
|
+
if (!fs.existsSync(targetThemePath)) {
|
|
56
|
+
fs.mkdirSync(targetThemePath, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const files = fs.readdirSync(themePath);
|
|
60
|
+
files.forEach(file => {
|
|
61
|
+
const sourceFile = path.join(themePath, file);
|
|
62
|
+
const targetFile = path.join(targetThemePath, file);
|
|
63
|
+
fs.copyFileSync(sourceFile, targetFile);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
console.log(`✅ Tema copiado para ${targetThemePath}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function showHelp() {
|
|
70
|
+
console.log(`
|
|
71
|
+
🎨 AleUI - Glassmorphism Components
|
|
72
|
+
|
|
73
|
+
Uso:
|
|
74
|
+
npx @alexandretav/aleui add <componente>
|
|
75
|
+
npx @alexandretav/aleui add-all
|
|
76
|
+
npx @alexandretav/aleui list
|
|
77
|
+
|
|
78
|
+
Comandos:
|
|
79
|
+
add <componente> Adiciona um componente específico
|
|
80
|
+
add-all Adiciona todos os componentes
|
|
81
|
+
list Lista componentes disponíveis
|
|
82
|
+
|
|
83
|
+
Componentes disponíveis:
|
|
84
|
+
${availableComponents.map(c => ` - ${c}`).join('\n')}
|
|
85
|
+
|
|
86
|
+
Exemplos:
|
|
87
|
+
npx @alexandretav/aleui add button
|
|
88
|
+
npx @alexandretav/aleui add card
|
|
89
|
+
npx @alexandretav/aleui add-all
|
|
90
|
+
`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function listComponents() {
|
|
94
|
+
console.log('\n📦 Componentes disponíveis:\n');
|
|
95
|
+
availableComponents.forEach(c => {
|
|
96
|
+
console.log(` ✨ ${c}`);
|
|
97
|
+
});
|
|
98
|
+
console.log('');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Main
|
|
102
|
+
switch (command) {
|
|
103
|
+
case 'add':
|
|
104
|
+
if (!componentName) {
|
|
105
|
+
console.error('❌ Especifique o nome do componente!');
|
|
106
|
+
console.log('Uso: npx @alexandretav/aleui add <componente>');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
copyTheme();
|
|
110
|
+
copyComponent(componentName);
|
|
111
|
+
break;
|
|
112
|
+
|
|
113
|
+
case 'add-all':
|
|
114
|
+
copyTheme();
|
|
115
|
+
availableComponents.forEach(comp => copyComponent(comp));
|
|
116
|
+
console.log('\n✅ Todos os componentes foram copiados!');
|
|
117
|
+
break;
|
|
118
|
+
|
|
119
|
+
case 'list':
|
|
120
|
+
listComponents();
|
|
121
|
+
break;
|
|
122
|
+
|
|
123
|
+
default:
|
|
124
|
+
showHelp();
|
|
125
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexandretav/aleui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Glass UI components for React",
|
|
5
|
-
"main": "
|
|
6
|
-
"
|
|
7
|
-
"
|
|
5
|
+
"main": "src/components/index.ts",
|
|
6
|
+
"types": "src/components/index.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"aleui": "./cli.js"
|
|
9
|
+
},
|
|
8
10
|
"files": [
|
|
9
|
-
"
|
|
11
|
+
"src/components",
|
|
12
|
+
"src/theme",
|
|
13
|
+
"src/utils",
|
|
10
14
|
"README.md",
|
|
11
15
|
"LICENSE"
|
|
12
16
|
],
|
|
@@ -33,10 +37,8 @@
|
|
|
33
37
|
"scripts": {
|
|
34
38
|
"dev": "next dev",
|
|
35
39
|
"build": "next build",
|
|
36
|
-
"build:lib": "tsup src/components/index.ts --format cjs,esm --dts --external react --external react-dom --external framer-motion",
|
|
37
40
|
"start": "next start",
|
|
38
|
-
"lint": "next lint"
|
|
39
|
-
"prepublishOnly": "npm run build:lib"
|
|
41
|
+
"lint": "next lint"
|
|
40
42
|
},
|
|
41
43
|
"peerDependencies": {
|
|
42
44
|
"framer-motion": "^11.0.0",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { getGlassClasses, GlassVariantType } from '@/theme/glass';
|
|
5
|
+
|
|
6
|
+
export interface AccordionItemProps {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
content: React.ReactNode;
|
|
10
|
+
icon?: React.ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AccordionProps {
|
|
14
|
+
items: AccordionItemProps[];
|
|
15
|
+
variant?: GlassVariantType;
|
|
16
|
+
allowMultiple?: boolean;
|
|
17
|
+
defaultOpen?: string[];
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const Accordion: React.FC<AccordionProps> = ({
|
|
22
|
+
items,
|
|
23
|
+
variant = 'light',
|
|
24
|
+
allowMultiple = false,
|
|
25
|
+
defaultOpen = [],
|
|
26
|
+
className = '',
|
|
27
|
+
}) => {
|
|
28
|
+
const [openItems, setOpenItems] = useState<string[]>(defaultOpen);
|
|
29
|
+
|
|
30
|
+
const toggleItem = (id: string) => {
|
|
31
|
+
if (allowMultiple) {
|
|
32
|
+
setOpenItems((prev) =>
|
|
33
|
+
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
|
|
34
|
+
);
|
|
35
|
+
} else {
|
|
36
|
+
setOpenItems((prev) => (prev.includes(id) ? [] : [id]));
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const isOpen = (id: string) => openItems.includes(id);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className={className}>
|
|
44
|
+
{items.map((item, index) => {
|
|
45
|
+
const open = isOpen(item.id);
|
|
46
|
+
const glassClasses = getGlassClasses(variant, 'rounded-none', false);
|
|
47
|
+
const isFirst = index === 0;
|
|
48
|
+
const isLast = index === items.length - 1;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
key={item.id}
|
|
53
|
+
className={`${glassClasses} ${
|
|
54
|
+
isFirst ? 'rounded-t-lg' : ''
|
|
55
|
+
} ${
|
|
56
|
+
isLast ? 'rounded-b-lg' : ''
|
|
57
|
+
} ${
|
|
58
|
+
!isLast ? 'border-b border-white/10' : ''
|
|
59
|
+
}`}
|
|
60
|
+
>
|
|
61
|
+
<button
|
|
62
|
+
onClick={() => toggleItem(item.id)}
|
|
63
|
+
className="w-full px-4 py-4 flex items-center justify-between text-left text-white font-medium hover:underline transition-all duration-200"
|
|
64
|
+
>
|
|
65
|
+
<div className="flex items-center gap-2">
|
|
66
|
+
{item.icon && <span className="flex items-center text-base">{item.icon}</span>}
|
|
67
|
+
<span className="text-sm font-medium">{item.title}</span>
|
|
68
|
+
</div>
|
|
69
|
+
<svg
|
|
70
|
+
className={`w-4 h-4 shrink-0 transition-transform duration-200 text-white/70 ${
|
|
71
|
+
open ? 'rotate-180' : ''
|
|
72
|
+
}`}
|
|
73
|
+
fill="none"
|
|
74
|
+
stroke="currentColor"
|
|
75
|
+
strokeWidth="2"
|
|
76
|
+
viewBox="0 0 24 24"
|
|
77
|
+
>
|
|
78
|
+
<path
|
|
79
|
+
strokeLinecap="round"
|
|
80
|
+
strokeLinejoin="round"
|
|
81
|
+
d="M19 9l-7 7-7-7"
|
|
82
|
+
/>
|
|
83
|
+
</svg>
|
|
84
|
+
</button>
|
|
85
|
+
<div
|
|
86
|
+
className={`grid transition-all duration-200 ease-in-out ${
|
|
87
|
+
open ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'
|
|
88
|
+
}`}
|
|
89
|
+
>
|
|
90
|
+
<div className="overflow-hidden">
|
|
91
|
+
<div className="px-4 pb-4 pt-0 text-sm text-white/80 leading-relaxed">
|
|
92
|
+
{item.content}
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
})}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export default Accordion;
|
|
@@ -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';
|