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.
Files changed (124) hide show
  1. package/README.md +57 -0
  2. package/components/ActionTable.tsx +307 -0
  3. package/components/AlertBanner.tsx +124 -0
  4. package/components/AnimatedAccordion.tsx +95 -0
  5. package/components/Autocomplete.tsx +144 -0
  6. package/components/Avatar.tsx +123 -0
  7. package/components/Badge.tsx +80 -0
  8. package/components/Breadcrumb.tsx +74 -0
  9. package/components/Calendar.tsx +340 -0
  10. package/components/Card3D.tsx +117 -0
  11. package/components/Carousel3D.tsx +193 -0
  12. package/components/CascadeSelect.tsx +232 -0
  13. package/components/ChartShowcase.tsx +700 -0
  14. package/components/Checkbox.tsx +212 -0
  15. package/components/ChipsInput.tsx +152 -0
  16. package/components/CircularKnob.tsx +240 -0
  17. package/components/CodeVisualizer.tsx +67 -0
  18. package/components/Collapsible.tsx +72 -0
  19. package/components/ColorThemeManager.tsx +458 -0
  20. package/components/CommandMenu.tsx +191 -0
  21. package/components/ConfirmDialog.tsx +152 -0
  22. package/components/ContextMenu.tsx +192 -0
  23. package/components/DashboardLayout.tsx +115 -0
  24. package/components/DatePicker.tsx +108 -0
  25. package/components/Divider.tsx +67 -0
  26. package/components/Dock.tsx +93 -0
  27. package/components/DragDropLists.tsx +160 -0
  28. package/components/Drawer.tsx +161 -0
  29. package/components/DropdownPlus.tsx +304 -0
  30. package/components/EmptyState.tsx +49 -0
  31. package/components/ErrorPage.tsx +62 -0
  32. package/components/FileDropzone.tsx +206 -0
  33. package/components/ForgotPassword.tsx +137 -0
  34. package/components/FormField.tsx +81 -0
  35. package/components/GlassButton.tsx +56 -0
  36. package/components/GlassCard.tsx +82 -0
  37. package/components/GlassInput.tsx +96 -0
  38. package/components/GlassmorphicModal.tsx +108 -0
  39. package/components/GlowInput.tsx +111 -0
  40. package/components/GlowSelect.tsx +203 -0
  41. package/components/GlowTextArea.tsx +105 -0
  42. package/components/HorizontalTimeline.tsx +121 -0
  43. package/components/HoverCard.tsx +105 -0
  44. package/components/ImageLightbox.tsx +259 -0
  45. package/components/InputGroup.tsx +118 -0
  46. package/components/InputOTP.tsx +147 -0
  47. package/components/InteractiveNavbar.tsx +266 -0
  48. package/components/InteractiveSidebar.tsx +211 -0
  49. package/components/Kbd.tsx +51 -0
  50. package/components/LiteYouTube.tsx +118 -0
  51. package/components/LoaderCollection.tsx +368 -0
  52. package/components/LoginForm.tsx +192 -0
  53. package/components/MagneticButton.tsx +101 -0
  54. package/components/MaskedInput.tsx +79 -0
  55. package/components/MentionInput.tsx +413 -0
  56. package/components/MorphingSwitch.tsx +86 -0
  57. package/components/MultiSelect.tsx +158 -0
  58. package/components/NumberInput.tsx +203 -0
  59. package/components/Panel.tsx +104 -0
  60. package/components/PasswordInput.tsx +203 -0
  61. package/components/Popover.tsx +91 -0
  62. package/components/PricingTable.tsx +113 -0
  63. package/components/ProgressBar.tsx +152 -0
  64. package/components/RadioButton.tsx +211 -0
  65. package/components/Rating.tsx +82 -0
  66. package/components/ResizablePanel.tsx +114 -0
  67. package/components/ScrollPanel.tsx +103 -0
  68. package/components/SettingsPage.tsx +154 -0
  69. package/components/SignupForm.tsx +182 -0
  70. package/components/Skeleton.tsx +41 -0
  71. package/components/Slider.tsx +95 -0
  72. package/components/SlidingTabs.tsx +54 -0
  73. package/components/SortableList.tsx +91 -0
  74. package/components/SpeedDial.tsx +134 -0
  75. package/components/Spinner.tsx +40 -0
  76. package/components/Stepper.tsx +124 -0
  77. package/components/TabMenu.tsx +72 -0
  78. package/components/TableControls.tsx +77 -0
  79. package/components/TablePagination.tsx +88 -0
  80. package/components/TextEditor.tsx +329 -0
  81. package/components/TextReveal.tsx +99 -0
  82. package/components/ThemeSwitcher.tsx +133 -0
  83. package/components/TimelineGSAP.tsx +164 -0
  84. package/components/ToastSystem.tsx +110 -0
  85. package/components/ToggleButton.tsx +79 -0
  86. package/components/Tooltip.tsx +121 -0
  87. package/components/Tree.tsx +138 -0
  88. package/dist/commands/add.d.ts +7 -0
  89. package/dist/commands/add.js +110 -0
  90. package/dist/commands/init.d.ts +5 -0
  91. package/dist/commands/init.js +76 -0
  92. package/dist/commands/list.d.ts +1 -0
  93. package/dist/commands/list.js +32 -0
  94. package/dist/index.d.ts +2 -0
  95. package/dist/index.js +60 -0
  96. package/dist/registry.d.ts +6 -0
  97. package/dist/registry.js +38 -0
  98. package/dist/tui/browse.d.ts +3 -0
  99. package/dist/tui/browse.js +139 -0
  100. package/dist/tui/format.d.ts +11 -0
  101. package/dist/tui/format.js +52 -0
  102. package/dist/tui/main.d.ts +1 -0
  103. package/dist/tui/main.js +86 -0
  104. package/dist/tui/panels.d.ts +9 -0
  105. package/dist/tui/panels.js +50 -0
  106. package/dist/tui/theme.d.ts +28 -0
  107. package/dist/tui/theme.js +76 -0
  108. package/dist/types.d.ts +28 -0
  109. package/dist/types.js +1 -0
  110. package/dist/utils/config.d.ts +6 -0
  111. package/dist/utils/config.js +24 -0
  112. package/dist/utils/copy.d.ts +9 -0
  113. package/dist/utils/copy.js +43 -0
  114. package/dist/utils/cwd.d.ts +6 -0
  115. package/dist/utils/cwd.js +30 -0
  116. package/dist/utils/deps.d.ts +1 -0
  117. package/dist/utils/deps.js +19 -0
  118. package/dist/utils/project.d.ts +5 -0
  119. package/dist/utils/project.js +30 -0
  120. package/dist/utils/theme.d.ts +1 -0
  121. package/dist/utils/theme.js +24 -0
  122. package/package.json +52 -0
  123. package/registry.json +1133 -0
  124. 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
+ };