alexui 1.0.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/README.md +57 -0
- package/components/ActionTable.tsx +307 -0
- package/components/AlertBanner.tsx +124 -0
- package/components/AnimatedAccordion.tsx +95 -0
- package/components/Autocomplete.tsx +144 -0
- package/components/Avatar.tsx +123 -0
- package/components/Badge.tsx +80 -0
- package/components/Breadcrumb.tsx +74 -0
- package/components/Calendar.tsx +340 -0
- package/components/Card3D.tsx +117 -0
- package/components/Carousel3D.tsx +193 -0
- package/components/CascadeSelect.tsx +232 -0
- package/components/ChartShowcase.tsx +700 -0
- package/components/Checkbox.tsx +212 -0
- package/components/ChipsInput.tsx +152 -0
- package/components/CircularKnob.tsx +240 -0
- package/components/CodeVisualizer.tsx +67 -0
- package/components/Collapsible.tsx +72 -0
- package/components/ColorThemeManager.tsx +458 -0
- package/components/CommandMenu.tsx +191 -0
- package/components/ConfirmDialog.tsx +152 -0
- package/components/ContextMenu.tsx +192 -0
- package/components/DashboardLayout.tsx +115 -0
- package/components/DatePicker.tsx +108 -0
- package/components/Divider.tsx +67 -0
- package/components/Dock.tsx +93 -0
- package/components/DragDropLists.tsx +160 -0
- package/components/Drawer.tsx +161 -0
- package/components/DropdownPlus.tsx +304 -0
- package/components/EmptyState.tsx +49 -0
- package/components/ErrorPage.tsx +62 -0
- package/components/FileDropzone.tsx +206 -0
- package/components/ForgotPassword.tsx +137 -0
- package/components/FormField.tsx +81 -0
- package/components/GlassButton.tsx +56 -0
- package/components/GlassCard.tsx +82 -0
- package/components/GlassInput.tsx +96 -0
- package/components/GlassmorphicModal.tsx +108 -0
- package/components/GlowInput.tsx +111 -0
- package/components/GlowSelect.tsx +203 -0
- package/components/GlowTextArea.tsx +105 -0
- package/components/HorizontalTimeline.tsx +121 -0
- package/components/HoverCard.tsx +105 -0
- package/components/ImageLightbox.tsx +259 -0
- package/components/InputGroup.tsx +118 -0
- package/components/InputOTP.tsx +147 -0
- package/components/InteractiveNavbar.tsx +266 -0
- package/components/InteractiveSidebar.tsx +211 -0
- package/components/Kbd.tsx +51 -0
- package/components/LiteYouTube.tsx +118 -0
- package/components/LoaderCollection.tsx +368 -0
- package/components/LoginForm.tsx +192 -0
- package/components/MagneticButton.tsx +101 -0
- package/components/MaskedInput.tsx +79 -0
- package/components/MentionInput.tsx +413 -0
- package/components/MorphingSwitch.tsx +86 -0
- package/components/MultiSelect.tsx +158 -0
- package/components/NumberInput.tsx +203 -0
- package/components/Panel.tsx +104 -0
- package/components/PasswordInput.tsx +203 -0
- package/components/Popover.tsx +91 -0
- package/components/PricingTable.tsx +113 -0
- package/components/ProgressBar.tsx +152 -0
- package/components/RadioButton.tsx +211 -0
- package/components/Rating.tsx +82 -0
- package/components/ResizablePanel.tsx +114 -0
- package/components/ScrollPanel.tsx +103 -0
- package/components/SettingsPage.tsx +154 -0
- package/components/SignupForm.tsx +182 -0
- package/components/Skeleton.tsx +41 -0
- package/components/Slider.tsx +95 -0
- package/components/SlidingTabs.tsx +54 -0
- package/components/SortableList.tsx +91 -0
- package/components/SpeedDial.tsx +134 -0
- package/components/Spinner.tsx +40 -0
- package/components/Stepper.tsx +124 -0
- package/components/TabMenu.tsx +72 -0
- package/components/TableControls.tsx +77 -0
- package/components/TablePagination.tsx +88 -0
- package/components/TextEditor.tsx +329 -0
- package/components/TextReveal.tsx +99 -0
- package/components/ThemeSwitcher.tsx +133 -0
- package/components/TimelineGSAP.tsx +164 -0
- package/components/ToastSystem.tsx +110 -0
- package/components/ToggleButton.tsx +79 -0
- package/components/Tooltip.tsx +121 -0
- package/components/Tree.tsx +138 -0
- package/dist/commands/add.d.ts +7 -0
- package/dist/commands/add.js +110 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +76 -0
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +32 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +60 -0
- package/dist/registry.d.ts +6 -0
- package/dist/registry.js +38 -0
- package/dist/tui/browse.d.ts +3 -0
- package/dist/tui/browse.js +139 -0
- package/dist/tui/format.d.ts +11 -0
- package/dist/tui/format.js +52 -0
- package/dist/tui/main.d.ts +1 -0
- package/dist/tui/main.js +86 -0
- package/dist/tui/panels.d.ts +9 -0
- package/dist/tui/panels.js +50 -0
- package/dist/tui/theme.d.ts +28 -0
- package/dist/tui/theme.js +76 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.js +1 -0
- package/dist/utils/config.d.ts +6 -0
- package/dist/utils/config.js +24 -0
- package/dist/utils/copy.d.ts +9 -0
- package/dist/utils/copy.js +43 -0
- package/dist/utils/cwd.d.ts +6 -0
- package/dist/utils/cwd.js +30 -0
- package/dist/utils/deps.d.ts +1 -0
- package/dist/utils/deps.js +19 -0
- package/dist/utils/project.d.ts +5 -0
- package/dist/utils/project.js +30 -0
- package/dist/utils/theme.d.ts +1 -0
- package/dist/utils/theme.js +24 -0
- package/package.json +52 -0
- package/registry.json +1133 -0
- package/templates/theme.css +81 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect, useMemo } from 'react';
|
|
2
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
|
+
import { ChevronRight, ChevronDown, Loader2 } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
export interface CascadeOption {
|
|
6
|
+
value: string;
|
|
7
|
+
label: string;
|
|
8
|
+
icon?: React.ReactNode;
|
|
9
|
+
description?: string;
|
|
10
|
+
children?: CascadeOption[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CascadeSelectProps {
|
|
14
|
+
options: CascadeOption[];
|
|
15
|
+
value: string[]; // array representing selected path, e.g. ['arg', 'ba', 'caba']
|
|
16
|
+
onChange: (value: string[]) => void;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
label?: string;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const CascadeSelect: React.FC<CascadeSelectProps> = ({
|
|
24
|
+
options,
|
|
25
|
+
value,
|
|
26
|
+
onChange,
|
|
27
|
+
placeholder = 'Selecciona una categoría...',
|
|
28
|
+
label,
|
|
29
|
+
disabled = false,
|
|
30
|
+
className = ''
|
|
31
|
+
}) => {
|
|
32
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
33
|
+
const [activePath, setActivePath] = useState<string[]>([]);
|
|
34
|
+
const [loadingColumn, setLoadingColumn] = useState<number | null>(null);
|
|
35
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
36
|
+
|
|
37
|
+
// Close when clicking outside
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
40
|
+
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
|
41
|
+
setIsOpen(false);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
45
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
46
|
+
}, []);
|
|
47
|
+
|
|
48
|
+
// Sync activePath with value when opening
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (isOpen) {
|
|
51
|
+
setActivePath(value);
|
|
52
|
+
}
|
|
53
|
+
}, [isOpen, value]);
|
|
54
|
+
|
|
55
|
+
// Compute options for each active column level
|
|
56
|
+
const columns = useMemo(() => {
|
|
57
|
+
const list: CascadeOption[][] = [options];
|
|
58
|
+
|
|
59
|
+
for (let i = 0; i < activePath.length; i++) {
|
|
60
|
+
const parentVal = activePath[i];
|
|
61
|
+
const activeOptions = list[i];
|
|
62
|
+
const matched = activeOptions?.find((opt) => opt.value === parentVal);
|
|
63
|
+
|
|
64
|
+
if (matched && matched.children && matched.children.length > 0) {
|
|
65
|
+
list.push(matched.children);
|
|
66
|
+
} else {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return list;
|
|
72
|
+
}, [options, activePath]);
|
|
73
|
+
|
|
74
|
+
const handleOptionClick = (option: CascadeOption, levelIndex: number) => {
|
|
75
|
+
if (disabled) return;
|
|
76
|
+
|
|
77
|
+
const nextPath = [...activePath.slice(0, levelIndex), option.value];
|
|
78
|
+
setActivePath(nextPath);
|
|
79
|
+
|
|
80
|
+
if (option.children && option.children.length > 0) {
|
|
81
|
+
// Simulate loading state for next level column
|
|
82
|
+
setLoadingColumn(levelIndex + 1);
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
setLoadingColumn(null);
|
|
85
|
+
}, 500);
|
|
86
|
+
} else {
|
|
87
|
+
// Leaf option selected, submit selections
|
|
88
|
+
onChange(nextPath);
|
|
89
|
+
setIsOpen(false);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Get selected leaf option to access its icon
|
|
94
|
+
const selectedLeafOption = useMemo(() => {
|
|
95
|
+
if (value.length === 0) return null;
|
|
96
|
+
let currentOptions = options;
|
|
97
|
+
let match: CascadeOption | null = null;
|
|
98
|
+
|
|
99
|
+
for (const val of value) {
|
|
100
|
+
const found = currentOptions?.find((opt) => opt.value === val);
|
|
101
|
+
if (found) {
|
|
102
|
+
match = found;
|
|
103
|
+
currentOptions = found.children || [];
|
|
104
|
+
} else {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return match;
|
|
109
|
+
}, [options, value]);
|
|
110
|
+
|
|
111
|
+
// Get displayed text representation
|
|
112
|
+
const displayText = useMemo(() => {
|
|
113
|
+
return selectedLeafOption ? selectedLeafOption.label : '';
|
|
114
|
+
}, [selectedLeafOption]);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<div ref={containerRef} className={`relative w-full flex flex-col gap-1.5 ${className}`}>
|
|
118
|
+
{label && (
|
|
119
|
+
<span className="text-xs font-bold text-text-muted uppercase tracking-wider px-1">
|
|
120
|
+
{label}
|
|
121
|
+
</span>
|
|
122
|
+
)}
|
|
123
|
+
|
|
124
|
+
{/* Select Box Trigger */}
|
|
125
|
+
<div className="relative rounded-xl overflow-visible transition-all duration-300">
|
|
126
|
+
|
|
127
|
+
{/* Glow border ring */}
|
|
128
|
+
<motion.div
|
|
129
|
+
animate={{
|
|
130
|
+
opacity: isOpen ? 1 : 0,
|
|
131
|
+
scale: isOpen ? 1 : 0.98,
|
|
132
|
+
}}
|
|
133
|
+
transition={{ duration: 0.25 }}
|
|
134
|
+
className="absolute -inset-[1px] bg-gradient-to-r from-accent to-pink-500 rounded-xl pointer-events-none z-0 blur-[1px]"
|
|
135
|
+
/>
|
|
136
|
+
|
|
137
|
+
<div
|
|
138
|
+
onClick={() => !disabled && setIsOpen(!isOpen)}
|
|
139
|
+
className={`w-full relative bg-bg-card/60 border rounded-xl z-10 p-3 min-h-[48px] flex items-center justify-between gap-3 transition-colors duration-300 select-none ${
|
|
140
|
+
isOpen ? 'border-transparent' : 'border-border-app'
|
|
141
|
+
} ${disabled ? 'opacity-40 cursor-not-allowed bg-bg-app/10' : 'cursor-pointer'}`}
|
|
142
|
+
style={{
|
|
143
|
+
boxShadow: isOpen && !disabled ? '0 0 10px var(--color-accent-glow)' : 'none'
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
<div className="flex items-center gap-2 pl-1 truncate">
|
|
147
|
+
{selectedLeafOption?.icon && (
|
|
148
|
+
<span className="flex-shrink-0 text-sm select-none">{selectedLeafOption.icon}</span>
|
|
149
|
+
)}
|
|
150
|
+
<span className={`text-sm font-medium truncate ${
|
|
151
|
+
displayText ? 'text-text-main font-bold' : 'text-text-muted/60'
|
|
152
|
+
}`}>
|
|
153
|
+
{displayText || placeholder}
|
|
154
|
+
</span>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<motion.div
|
|
158
|
+
animate={{ rotate: isOpen ? 180 : 0 }}
|
|
159
|
+
className="text-text-muted pr-1"
|
|
160
|
+
>
|
|
161
|
+
<ChevronDown className="w-4 h-4" />
|
|
162
|
+
</motion.div>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
{/* Cascading Columns Popover Panel */}
|
|
166
|
+
<AnimatePresence>
|
|
167
|
+
{isOpen && (
|
|
168
|
+
<motion.div
|
|
169
|
+
initial={{ opacity: 0, y: 10, scale: 0.95 }}
|
|
170
|
+
animate={{ opacity: 1, y: 4, scale: 1 }}
|
|
171
|
+
exit={{ opacity: 0, y: 10, scale: 0.95 }}
|
|
172
|
+
transition={{ type: 'spring', stiffness: 350, damping: 25 }}
|
|
173
|
+
className="absolute left-0 mt-1 glass bg-bg-card rounded-2xl shadow-2xl p-2 z-50 border border-border-app/50 flex gap-1 max-h-80 overflow-visible min-w-[280px] max-w-full"
|
|
174
|
+
>
|
|
175
|
+
{columns.map((levelOptions, idx) => {
|
|
176
|
+
const isLoading = loadingColumn === idx;
|
|
177
|
+
const activeVal = activePath[idx];
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<div
|
|
181
|
+
key={idx}
|
|
182
|
+
className="flex flex-col gap-1 w-48 border-r border-border-app/30 last:border-0 pr-1 last:pr-0 overflow-y-auto max-h-72 min-h-[160px]"
|
|
183
|
+
>
|
|
184
|
+
{isLoading ? (
|
|
185
|
+
<div className="flex-1 flex items-center justify-center p-6 text-accent">
|
|
186
|
+
<Loader2 className="w-5 h-5 animate-spin" />
|
|
187
|
+
</div>
|
|
188
|
+
) : (
|
|
189
|
+
levelOptions.map((opt) => {
|
|
190
|
+
const isSelected = activeVal === opt.value;
|
|
191
|
+
const hasChildren = opt.children && opt.children.length > 0;
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<button
|
|
195
|
+
key={opt.value}
|
|
196
|
+
type="button"
|
|
197
|
+
onClick={() => handleOptionClick(opt, idx)}
|
|
198
|
+
className={`w-full px-3 py-2 rounded-xl text-xs font-semibold flex items-center justify-between transition-colors duration-200 cursor-pointer ${
|
|
199
|
+
isSelected
|
|
200
|
+
? 'bg-accent/15 text-accent font-bold'
|
|
201
|
+
: 'text-text-main hover:bg-bg-app'
|
|
202
|
+
}`}
|
|
203
|
+
>
|
|
204
|
+
<div className="flex items-center gap-2.5 min-w-0">
|
|
205
|
+
{opt.icon && <span className="flex-shrink-0">{opt.icon}</span>}
|
|
206
|
+
<div className="flex flex-col items-start min-w-0">
|
|
207
|
+
<span className="truncate w-full text-left">{opt.label}</span>
|
|
208
|
+
{opt.description && (
|
|
209
|
+
<span className="text-[9px] text-text-muted truncate w-full text-left font-normal mt-0.5">
|
|
210
|
+
{opt.description}
|
|
211
|
+
</span>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
{hasChildren && (
|
|
216
|
+
<ChevronRight className="w-3.5 h-3.5 text-text-muted/70 flex-shrink-0" />
|
|
217
|
+
)}
|
|
218
|
+
</button>
|
|
219
|
+
);
|
|
220
|
+
})
|
|
221
|
+
)}
|
|
222
|
+
</div>
|
|
223
|
+
);
|
|
224
|
+
})}
|
|
225
|
+
</motion.div>
|
|
226
|
+
)}
|
|
227
|
+
</AnimatePresence>
|
|
228
|
+
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
};
|