create-bunspace 0.4.0 → 0.5.1
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/dist/bin.js +100 -0
- package/dist/templates/fumadocs/MUST-FOLLOW-GUIDELINES.md +283 -170
- package/dist/templates/monorepo/MUST-FOLLOW-GUIDELINES.md +283 -170
- package/dist/templates/react-starter/MUST-FOLLOW-GUIDELINES.md +1845 -0
- package/dist/templates/react-starter/README.md +100 -0
- package/dist/templates/react-starter/bun.lock +1298 -0
- package/dist/templates/react-starter/components.json +27 -0
- package/dist/templates/react-starter/eslint.config.js +23 -0
- package/dist/templates/react-starter/index.html +36 -0
- package/dist/templates/react-starter/package.json +57 -0
- package/dist/templates/react-starter/public/registry.json +115 -0
- package/dist/templates/react-starter/public/themes/darkmatteviolet-dark.css +34 -0
- package/dist/templates/react-starter/public/themes/darkmatteviolet-light.css +34 -0
- package/dist/templates/react-starter/public/themes/default-dark.css +33 -0
- package/dist/templates/react-starter/public/themes/default-light.css +34 -0
- package/dist/templates/react-starter/public/themes/graphite-dark.css +34 -0
- package/dist/templates/react-starter/public/themes/graphite-light.css +34 -0
- package/dist/templates/react-starter/public/themes/synthwave84-dark.css +34 -0
- package/dist/templates/react-starter/public/themes/synthwave84-light.css +34 -0
- package/dist/templates/react-starter/public/vite.svg +1 -0
- package/dist/templates/react-starter/src/App.tsx +245 -0
- package/dist/templates/react-starter/src/assets/react.svg +1 -0
- package/dist/templates/react-starter/src/components/ThemeSelector.tsx +106 -0
- package/dist/templates/react-starter/src/components/animate-ui/components/buttons/icon.tsx +86 -0
- package/dist/templates/react-starter/src/components/animate-ui/components/buttons/theme-toggler.tsx +92 -0
- package/dist/templates/react-starter/src/components/animate-ui/primitives/animate/slot.tsx +96 -0
- package/dist/templates/react-starter/src/components/animate-ui/primitives/buttons/button.tsx +31 -0
- package/dist/templates/react-starter/src/components/animate-ui/primitives/effects/particles.tsx +155 -0
- package/dist/templates/react-starter/src/components/animate-ui/primitives/effects/theme-toggler.tsx +148 -0
- package/dist/templates/react-starter/src/components/component-example.tsx +444 -0
- package/dist/templates/react-starter/src/components/example.tsx +56 -0
- package/dist/templates/react-starter/src/index.css +131 -0
- package/dist/templates/react-starter/src/main.tsx +13 -0
- package/dist/templates/react-starter/src/providers/ThemeProvider.tsx +27 -0
- package/dist/templates/react-starter/tsconfig.app.json +36 -0
- package/dist/templates/react-starter/tsconfig.json +13 -0
- package/dist/templates/react-starter/tsconfig.node.json +26 -0
- package/dist/templates/react-starter/vite.config.ts +17 -0
- package/dist/templates/telegram-bot/MUST-FOLLOW-GUIDELINES.md +283 -170
- package/package.json +1 -1
- package/templates/fumadocs/MUST-FOLLOW-GUIDELINES.md +283 -170
- package/templates/monorepo/MUST-FOLLOW-GUIDELINES.md +283 -170
- package/templates/react-starter/MUST-FOLLOW-GUIDELINES.md +1845 -0
- package/templates/react-starter/README.md +100 -0
- package/templates/react-starter/bun.lock +1298 -0
- package/templates/react-starter/components.json +27 -0
- package/templates/react-starter/eslint.config.js +23 -0
- package/templates/react-starter/index.html +36 -0
- package/templates/react-starter/package.json +57 -0
- package/templates/react-starter/public/registry.json +115 -0
- package/templates/react-starter/public/themes/darkmatteviolet-dark.css +34 -0
- package/templates/react-starter/public/themes/darkmatteviolet-light.css +34 -0
- package/templates/react-starter/public/themes/default-dark.css +33 -0
- package/templates/react-starter/public/themes/default-light.css +34 -0
- package/templates/react-starter/public/themes/graphite-dark.css +34 -0
- package/templates/react-starter/public/themes/graphite-light.css +34 -0
- package/templates/react-starter/public/themes/synthwave84-dark.css +34 -0
- package/templates/react-starter/public/themes/synthwave84-light.css +34 -0
- package/templates/react-starter/public/vite.svg +1 -0
- package/templates/react-starter/src/App.tsx +245 -0
- package/templates/react-starter/src/assets/react.svg +1 -0
- package/templates/react-starter/src/components/ThemeSelector.tsx +106 -0
- package/templates/react-starter/src/components/animate-ui/components/buttons/icon.tsx +86 -0
- package/templates/react-starter/src/components/animate-ui/components/buttons/theme-toggler.tsx +92 -0
- package/templates/react-starter/src/components/animate-ui/primitives/animate/slot.tsx +96 -0
- package/templates/react-starter/src/components/animate-ui/primitives/buttons/button.tsx +31 -0
- package/templates/react-starter/src/components/animate-ui/primitives/effects/particles.tsx +155 -0
- package/templates/react-starter/src/components/animate-ui/primitives/effects/theme-toggler.tsx +148 -0
- package/templates/react-starter/src/components/component-example.tsx +444 -0
- package/templates/react-starter/src/components/example.tsx +56 -0
- package/templates/react-starter/src/index.css +131 -0
- package/templates/react-starter/src/main.tsx +13 -0
- package/templates/react-starter/src/providers/ThemeProvider.tsx +27 -0
- package/templates/react-starter/tsconfig.app.json +36 -0
- package/templates/react-starter/tsconfig.json +13 -0
- package/templates/react-starter/tsconfig.node.json +26 -0
- package/templates/react-starter/vite.config.ts +17 -0
- package/templates/telegram-bot/MUST-FOLLOW-GUIDELINES.md +283 -170
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Settings, Star, Heart, Zap } from "lucide-react";
|
|
3
|
+
import { ComponentExample } from "@/components/component-example";
|
|
4
|
+
import { ThemeTogglerButton } from "@/components/animate-ui/components/buttons/theme-toggler";
|
|
5
|
+
import { ThemeSelector } from "@/components/ThemeSelector";
|
|
6
|
+
import {
|
|
7
|
+
SettingsModal,
|
|
8
|
+
type IAnimationSettings
|
|
9
|
+
} from "@mks2508/theme-manager-react";
|
|
10
|
+
import {
|
|
11
|
+
Button,
|
|
12
|
+
Badge,
|
|
13
|
+
Card,
|
|
14
|
+
CardHeader,
|
|
15
|
+
CardTitle,
|
|
16
|
+
CardDescription,
|
|
17
|
+
CardContent,
|
|
18
|
+
CardFooter,
|
|
19
|
+
Progress,
|
|
20
|
+
Separator,
|
|
21
|
+
Switch,
|
|
22
|
+
Checkbox,
|
|
23
|
+
Tabs,
|
|
24
|
+
TabsList,
|
|
25
|
+
TabsTab,
|
|
26
|
+
TabsPanel
|
|
27
|
+
} from "@mks2508/mks-ui/react";
|
|
28
|
+
|
|
29
|
+
export function App() {
|
|
30
|
+
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
31
|
+
const [animationSettings, setAnimationSettings] = useState<IAnimationSettings>({
|
|
32
|
+
preset: 'wipe',
|
|
33
|
+
direction: 'ltr',
|
|
34
|
+
duration: 500,
|
|
35
|
+
});
|
|
36
|
+
const [notifications, setNotifications] = useState(true);
|
|
37
|
+
const [newsletter, setNewsletter] = useState(false);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className="min-h-screen bg-background text-foreground">
|
|
41
|
+
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
42
|
+
<div className="container flex h-16 items-center justify-between">
|
|
43
|
+
<div className="flex items-center gap-3">
|
|
44
|
+
<h1 className="text-xl font-bold">{{PROJECT_TITLE}}</h1>
|
|
45
|
+
<Badge variant="secondary">v1.0</Badge>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div className="flex items-center gap-2">
|
|
49
|
+
<ThemeSelector
|
|
50
|
+
animation={animationSettings.preset}
|
|
51
|
+
direction={animationSettings.direction}
|
|
52
|
+
duration={animationSettings.duration}
|
|
53
|
+
/>
|
|
54
|
+
<ThemeTogglerButton
|
|
55
|
+
variant="ghost"
|
|
56
|
+
animation={animationSettings.preset}
|
|
57
|
+
direction={animationSettings.direction}
|
|
58
|
+
duration={animationSettings.duration}
|
|
59
|
+
/>
|
|
60
|
+
<Button
|
|
61
|
+
variant="outline"
|
|
62
|
+
size="icon"
|
|
63
|
+
onClick={() => setSettingsOpen(true)}
|
|
64
|
+
>
|
|
65
|
+
<Settings className="size-4" />
|
|
66
|
+
</Button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</header>
|
|
70
|
+
|
|
71
|
+
<main className="container py-8">
|
|
72
|
+
{/* Hero Section */}
|
|
73
|
+
<section className="mb-12 text-center">
|
|
74
|
+
<h2 className="text-4xl font-bold mb-4">
|
|
75
|
+
Welcome to {{PROJECT_TITLE}}
|
|
76
|
+
</h2>
|
|
77
|
+
<p className="text-muted-foreground text-lg mb-6 max-w-2xl mx-auto">
|
|
78
|
+
{{DESCRIPTION}}
|
|
79
|
+
</p>
|
|
80
|
+
<div className="flex justify-center gap-4">
|
|
81
|
+
<Button size="lg">
|
|
82
|
+
Get Started
|
|
83
|
+
</Button>
|
|
84
|
+
<Button variant="outline" size="lg">
|
|
85
|
+
View on GitHub
|
|
86
|
+
</Button>
|
|
87
|
+
</div>
|
|
88
|
+
</section>
|
|
89
|
+
|
|
90
|
+
<Separator className="my-8" />
|
|
91
|
+
|
|
92
|
+
{/* Tabs Demo Section */}
|
|
93
|
+
<section className="mb-12">
|
|
94
|
+
<Tabs defaultValue="components" className="w-full">
|
|
95
|
+
<TabsList className="mb-4">
|
|
96
|
+
<TabsTab value="components">Components</TabsTab>
|
|
97
|
+
<TabsTab value="forms">Forms</TabsTab>
|
|
98
|
+
<TabsTab value="status">Status</TabsTab>
|
|
99
|
+
</TabsList>
|
|
100
|
+
|
|
101
|
+
<TabsPanel value="components">
|
|
102
|
+
<ComponentExample />
|
|
103
|
+
</TabsPanel>
|
|
104
|
+
|
|
105
|
+
<TabsPanel value="forms">
|
|
106
|
+
<Card className="max-w-md mx-auto">
|
|
107
|
+
<CardHeader>
|
|
108
|
+
<CardTitle>Preferences</CardTitle>
|
|
109
|
+
<CardDescription>Customize your experience</CardDescription>
|
|
110
|
+
</CardHeader>
|
|
111
|
+
<CardContent className="space-y-4">
|
|
112
|
+
<div className="flex items-center justify-between">
|
|
113
|
+
<div className="space-y-0.5">
|
|
114
|
+
<div className="text-sm font-medium">Notifications</div>
|
|
115
|
+
<div className="text-xs text-muted-foreground">
|
|
116
|
+
Receive email notifications
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
<Switch
|
|
120
|
+
checked={notifications}
|
|
121
|
+
onCheckedChange={setNotifications}
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
<Separator />
|
|
125
|
+
<div className="flex items-center gap-3">
|
|
126
|
+
<Checkbox
|
|
127
|
+
id="newsletter"
|
|
128
|
+
checked={newsletter}
|
|
129
|
+
onCheckedChange={(c) => setNewsletter(c === true)}
|
|
130
|
+
/>
|
|
131
|
+
<label htmlFor="newsletter" className="text-sm cursor-pointer">
|
|
132
|
+
Subscribe to newsletter
|
|
133
|
+
</label>
|
|
134
|
+
</div>
|
|
135
|
+
</CardContent>
|
|
136
|
+
<CardFooter>
|
|
137
|
+
<Button>Save Preferences</Button>
|
|
138
|
+
</CardFooter>
|
|
139
|
+
</Card>
|
|
140
|
+
</TabsPanel>
|
|
141
|
+
|
|
142
|
+
<TabsPanel value="status">
|
|
143
|
+
<Card className="max-w-md mx-auto">
|
|
144
|
+
<CardHeader>
|
|
145
|
+
<CardTitle>System Status</CardTitle>
|
|
146
|
+
<CardDescription>Current application metrics</CardDescription>
|
|
147
|
+
</CardHeader>
|
|
148
|
+
<CardContent className="space-y-6">
|
|
149
|
+
<div className="space-y-2">
|
|
150
|
+
<div className="flex justify-between text-sm">
|
|
151
|
+
<span>Build Progress</span>
|
|
152
|
+
<span>75%</span>
|
|
153
|
+
</div>
|
|
154
|
+
<Progress value={75} />
|
|
155
|
+
</div>
|
|
156
|
+
<div className="space-y-2">
|
|
157
|
+
<div className="flex justify-between text-sm">
|
|
158
|
+
<span>Test Coverage</span>
|
|
159
|
+
<span>92%</span>
|
|
160
|
+
</div>
|
|
161
|
+
<Progress value={92} />
|
|
162
|
+
</div>
|
|
163
|
+
<div className="space-y-2">
|
|
164
|
+
<div className="flex justify-between text-sm">
|
|
165
|
+
<span>Performance Score</span>
|
|
166
|
+
<span>88%</span>
|
|
167
|
+
</div>
|
|
168
|
+
<Progress value={88} />
|
|
169
|
+
</div>
|
|
170
|
+
</CardContent>
|
|
171
|
+
</Card>
|
|
172
|
+
</TabsPanel>
|
|
173
|
+
</Tabs>
|
|
174
|
+
</section>
|
|
175
|
+
|
|
176
|
+
{/* Feature Cards */}
|
|
177
|
+
<section className="grid md:grid-cols-3 gap-6 mb-12">
|
|
178
|
+
<Card>
|
|
179
|
+
<CardHeader>
|
|
180
|
+
<div className="flex items-center gap-2">
|
|
181
|
+
<Star className="size-5 text-primary" />
|
|
182
|
+
<CardTitle>Modern Stack</CardTitle>
|
|
183
|
+
</div>
|
|
184
|
+
<CardDescription>
|
|
185
|
+
React 19, Vite 8, Tailwind v4
|
|
186
|
+
</CardDescription>
|
|
187
|
+
</CardHeader>
|
|
188
|
+
<CardContent>
|
|
189
|
+
<p className="text-sm text-muted-foreground">
|
|
190
|
+
Built with the latest versions of React, Vite, and Tailwind CSS
|
|
191
|
+
for optimal developer experience and performance.
|
|
192
|
+
</p>
|
|
193
|
+
</CardContent>
|
|
194
|
+
</Card>
|
|
195
|
+
|
|
196
|
+
<Card>
|
|
197
|
+
<CardHeader>
|
|
198
|
+
<div className="flex items-center gap-2">
|
|
199
|
+
<Heart className="size-5 text-primary" />
|
|
200
|
+
<CardTitle>Beautiful Components</CardTitle>
|
|
201
|
+
</div>
|
|
202
|
+
<CardDescription>
|
|
203
|
+
BaseUI primitives + custom styling
|
|
204
|
+
</CardDescription>
|
|
205
|
+
</CardHeader>
|
|
206
|
+
<CardContent>
|
|
207
|
+
<p className="text-sm text-muted-foreground">
|
|
208
|
+
Accessible, animated components built on BaseUI primitives
|
|
209
|
+
with smooth transitions and interactions.
|
|
210
|
+
</p>
|
|
211
|
+
</CardContent>
|
|
212
|
+
</Card>
|
|
213
|
+
|
|
214
|
+
<Card>
|
|
215
|
+
<CardHeader>
|
|
216
|
+
<div className="flex items-center gap-2">
|
|
217
|
+
<Zap className="size-5 text-primary" />
|
|
218
|
+
<CardTitle>Theme System</CardTitle>
|
|
219
|
+
</div>
|
|
220
|
+
<CardDescription>
|
|
221
|
+
Multiple themes with animated transitions
|
|
222
|
+
</CardDescription>
|
|
223
|
+
</CardHeader>
|
|
224
|
+
<CardContent>
|
|
225
|
+
<p className="text-sm text-muted-foreground">
|
|
226
|
+
Switch between themes with beautiful animations.
|
|
227
|
+
Supports light, dark, and system modes.
|
|
228
|
+
</p>
|
|
229
|
+
</CardContent>
|
|
230
|
+
</Card>
|
|
231
|
+
</section>
|
|
232
|
+
</main>
|
|
233
|
+
|
|
234
|
+
<SettingsModal
|
|
235
|
+
open={settingsOpen}
|
|
236
|
+
onOpenChange={setSettingsOpen}
|
|
237
|
+
initialTab="themes"
|
|
238
|
+
animationSettings={animationSettings}
|
|
239
|
+
onAnimationSettingsChange={setAnimationSettings}
|
|
240
|
+
/>
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export default App;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Palette, Settings, Download } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
DropdownMenu,
|
|
6
|
+
DropdownMenuContent,
|
|
7
|
+
DropdownMenuGroup,
|
|
8
|
+
DropdownMenuItem,
|
|
9
|
+
DropdownMenuLabel,
|
|
10
|
+
DropdownMenuRadioGroup,
|
|
11
|
+
DropdownMenuRadioItem,
|
|
12
|
+
DropdownMenuSeparator,
|
|
13
|
+
DropdownMenuTrigger,
|
|
14
|
+
} from '@mks2508/mks-ui/react';
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
useAnimatedTheme,
|
|
18
|
+
type AnimationPreset,
|
|
19
|
+
type Direction,
|
|
20
|
+
} from '@mks2508/theme-manager-react';
|
|
21
|
+
|
|
22
|
+
interface IThemeSelectorProps {
|
|
23
|
+
onFontSettings?: () => void;
|
|
24
|
+
onThemeManagement?: () => void;
|
|
25
|
+
animation?: AnimationPreset;
|
|
26
|
+
direction?: Direction;
|
|
27
|
+
duration?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Dropdown menu for selecting a theme with animated transitions.
|
|
32
|
+
*
|
|
33
|
+
* Now supports spectacular animations via `useAnimatedTheme` hook:
|
|
34
|
+
* - `gif-mask` — Liquid ink/tinta splash with SVG blur
|
|
35
|
+
* - `wipe` — Directional curtain reveal
|
|
36
|
+
* - `circle-expand` — Circle from click point or center
|
|
37
|
+
* - `circle-shrink` — Old state shrinks in circle
|
|
38
|
+
* - `diamond` — Diamond polygon wipe from center
|
|
39
|
+
* - `crossfade` — Simple opacity fade
|
|
40
|
+
* - `slide` — Directional slide + fade
|
|
41
|
+
* - `none` — Instant swap, no animation
|
|
42
|
+
*/
|
|
43
|
+
function ThemeSelector({
|
|
44
|
+
onFontSettings,
|
|
45
|
+
onThemeManagement,
|
|
46
|
+
animation = 'wipe',
|
|
47
|
+
direction = 'ltr',
|
|
48
|
+
duration = 500,
|
|
49
|
+
}: IThemeSelectorProps) {
|
|
50
|
+
const { currentTheme, themes, setTheme } = useAnimatedTheme({
|
|
51
|
+
animation,
|
|
52
|
+
direction,
|
|
53
|
+
duration,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<DropdownMenu>
|
|
58
|
+
<DropdownMenuTrigger render={<Button variant="ghost" size="icon" />}>
|
|
59
|
+
<Palette className="size-4" />
|
|
60
|
+
<span className="sr-only">Select theme</span>
|
|
61
|
+
</DropdownMenuTrigger>
|
|
62
|
+
|
|
63
|
+
<DropdownMenuContent align="end" className="w-52">
|
|
64
|
+
<DropdownMenuGroup>
|
|
65
|
+
<DropdownMenuLabel>Themes</DropdownMenuLabel>
|
|
66
|
+
<DropdownMenuRadioGroup
|
|
67
|
+
value={currentTheme}
|
|
68
|
+
onValueChange={(value) => void setTheme(value)}
|
|
69
|
+
>
|
|
70
|
+
{themes.map((t) => (
|
|
71
|
+
<DropdownMenuRadioItem key={t.id} value={t.id}>
|
|
72
|
+
<span
|
|
73
|
+
className="size-3 shrink-0 rounded-full border border-foreground/15"
|
|
74
|
+
style={{ backgroundColor: t.preview?.primary ?? 'currentColor' }}
|
|
75
|
+
/>
|
|
76
|
+
{t.label}
|
|
77
|
+
</DropdownMenuRadioItem>
|
|
78
|
+
))}
|
|
79
|
+
</DropdownMenuRadioGroup>
|
|
80
|
+
</DropdownMenuGroup>
|
|
81
|
+
|
|
82
|
+
{(onFontSettings || onThemeManagement) && (
|
|
83
|
+
<>
|
|
84
|
+
<DropdownMenuSeparator />
|
|
85
|
+
<DropdownMenuGroup>
|
|
86
|
+
{onFontSettings && (
|
|
87
|
+
<DropdownMenuItem onSelect={onFontSettings}>
|
|
88
|
+
<Settings className="size-4" />
|
|
89
|
+
Font Settings
|
|
90
|
+
</DropdownMenuItem>
|
|
91
|
+
)}
|
|
92
|
+
{onThemeManagement && (
|
|
93
|
+
<DropdownMenuItem onSelect={onThemeManagement}>
|
|
94
|
+
<Download className="size-4" />
|
|
95
|
+
Manage Themes
|
|
96
|
+
</DropdownMenuItem>
|
|
97
|
+
)}
|
|
98
|
+
</DropdownMenuGroup>
|
|
99
|
+
</>
|
|
100
|
+
)}
|
|
101
|
+
</DropdownMenuContent>
|
|
102
|
+
</DropdownMenu>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { ThemeSelector, type IThemeSelectorProps };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Button as ButtonPrimitive,
|
|
8
|
+
type ButtonProps as ButtonPrimitiveProps,
|
|
9
|
+
} from '@/components/animate-ui/primitives/buttons/button';
|
|
10
|
+
import { cn } from '@mks2508/mks-ui/react';
|
|
11
|
+
import {
|
|
12
|
+
Particles,
|
|
13
|
+
ParticlesEffect,
|
|
14
|
+
} from '@/components/animate-ui/primitives/effects/particles';
|
|
15
|
+
|
|
16
|
+
const buttonVariants = cva(
|
|
17
|
+
"flex items-center justify-center rounded-md transition-[box-shadow,_color,_background-color,_border-color,_outline-color,_text-decoration-color,_fill,_stroke] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
18
|
+
{
|
|
19
|
+
variants: {
|
|
20
|
+
variant: {
|
|
21
|
+
default:
|
|
22
|
+
'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
|
|
23
|
+
accent: 'bg-accent text-accent-foreground shadow-xs hover:bg-accent/90',
|
|
24
|
+
destructive:
|
|
25
|
+
'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
|
26
|
+
outline:
|
|
27
|
+
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
|
|
28
|
+
secondary:
|
|
29
|
+
'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
|
|
30
|
+
ghost:
|
|
31
|
+
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
|
32
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
33
|
+
},
|
|
34
|
+
size: {
|
|
35
|
+
default: 'size-9',
|
|
36
|
+
xs: "size-7 [&_svg:not([class*='size-'])]:size-3.5 rounded-md",
|
|
37
|
+
sm: 'size-8 rounded-md',
|
|
38
|
+
lg: 'size-10 rounded-md',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
defaultVariants: {
|
|
42
|
+
variant: 'default',
|
|
43
|
+
size: 'default',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
type IconButtonProps = Omit<ButtonPrimitiveProps, 'asChild'> &
|
|
49
|
+
VariantProps<typeof buttonVariants> & {
|
|
50
|
+
children?: React.ReactNode;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function IconButton({
|
|
54
|
+
className,
|
|
55
|
+
onClick,
|
|
56
|
+
variant,
|
|
57
|
+
size,
|
|
58
|
+
children,
|
|
59
|
+
...props
|
|
60
|
+
}: IconButtonProps) {
|
|
61
|
+
const [isActive, setIsActive] = React.useState(false);
|
|
62
|
+
const [key, setKey] = React.useState(0);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Particles animate={isActive} key={key}>
|
|
66
|
+
<ButtonPrimitive
|
|
67
|
+
data-slot="icon-button"
|
|
68
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
69
|
+
onClick={(e) => {
|
|
70
|
+
setKey((prev) => prev + 1);
|
|
71
|
+
setIsActive(true);
|
|
72
|
+
onClick?.(e);
|
|
73
|
+
}}
|
|
74
|
+
{...props}
|
|
75
|
+
>
|
|
76
|
+
{children}
|
|
77
|
+
</ButtonPrimitive>
|
|
78
|
+
<ParticlesEffect
|
|
79
|
+
data-variant={variant}
|
|
80
|
+
className="bg-neutral-500 size-1 rounded-full"
|
|
81
|
+
/>
|
|
82
|
+
</Particles>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { IconButton, buttonVariants, type IconButtonProps };
|
package/dist/templates/react-starter/src/components/animate-ui/components/buttons/theme-toggler.tsx
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Monitor, Moon, Sun } from 'lucide-react';
|
|
5
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
6
|
+
|
|
7
|
+
import type { ThemeSelection } from '@/components/animate-ui/primitives/effects/theme-toggler';
|
|
8
|
+
import { buttonVariants } from '@/components/animate-ui/components/buttons/icon';
|
|
9
|
+
import { cn } from '@mks2508/mks-ui/react';
|
|
10
|
+
import {
|
|
11
|
+
useAnimatedTheme,
|
|
12
|
+
type AnimationPreset,
|
|
13
|
+
type Direction,
|
|
14
|
+
} from '@mks2508/theme-manager-react';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve display mode to icon shown in button.
|
|
18
|
+
*/
|
|
19
|
+
function getIcon(mode: ThemeSelection, modes: ThemeSelection[]) {
|
|
20
|
+
if (modes.includes('system') && mode === 'system') return <Monitor />;
|
|
21
|
+
if (mode === 'dark') return <Moon />;
|
|
22
|
+
return <Sun />;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type ThemeTogglerButtonProps = React.ComponentProps<'button'> &
|
|
26
|
+
VariantProps<typeof buttonVariants> & {
|
|
27
|
+
modes?: ThemeSelection[];
|
|
28
|
+
animation?: AnimationPreset;
|
|
29
|
+
direction?: Direction;
|
|
30
|
+
duration?: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Animated theme-mode toggle button with configurable animation presets.
|
|
35
|
+
*
|
|
36
|
+
* Animation options include:
|
|
37
|
+
* - `'wipe'` — Directional curtain reveal (default)
|
|
38
|
+
* - `'circle-expand'` — Circle grows from click point
|
|
39
|
+
* - `'circle-shrink'` — Old state shrinks in circle
|
|
40
|
+
* - `'diamond'` — Diamond polygon wipe from center
|
|
41
|
+
* - `'crossfade'` — Simple opacity fade
|
|
42
|
+
* - `'slide'` — Directional slide + fade
|
|
43
|
+
* - `'none'` — Instant swap, no animation
|
|
44
|
+
*/
|
|
45
|
+
function ThemeTogglerButton({
|
|
46
|
+
variant = 'default',
|
|
47
|
+
size = 'default',
|
|
48
|
+
modes = ['light', 'dark'],
|
|
49
|
+
animation = 'wipe',
|
|
50
|
+
direction = 'ltr',
|
|
51
|
+
duration = 500,
|
|
52
|
+
onClick,
|
|
53
|
+
className,
|
|
54
|
+
...props
|
|
55
|
+
}: ThemeTogglerButtonProps) {
|
|
56
|
+
const { currentTheme, currentMode, setTheme } = useAnimatedTheme({ animation, direction, duration });
|
|
57
|
+
|
|
58
|
+
const [displayMode, setDisplayMode] = React.useState<ThemeSelection>(() =>
|
|
59
|
+
currentMode === 'auto' ? 'system' : currentMode,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
React.useEffect(() => {
|
|
63
|
+
setDisplayMode(currentMode === 'auto' ? 'system' : currentMode);
|
|
64
|
+
}, [currentMode]);
|
|
65
|
+
|
|
66
|
+
const handleToggle = React.useCallback(
|
|
67
|
+
(e: React.MouseEvent<HTMLButtonElement>) => {
|
|
68
|
+
onClick?.(e);
|
|
69
|
+
|
|
70
|
+
const idx = modes.indexOf(displayMode);
|
|
71
|
+
const next = modes[(idx + 1) % modes.length];
|
|
72
|
+
const nextCoreMode = next === 'system' ? 'auto' : next;
|
|
73
|
+
|
|
74
|
+
setDisplayMode(next);
|
|
75
|
+
setTheme(currentTheme, nextCoreMode, e);
|
|
76
|
+
},
|
|
77
|
+
[onClick, modes, displayMode, currentTheme, setTheme, animation, direction, duration],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<button
|
|
82
|
+
data-slot="theme-toggler-button"
|
|
83
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
84
|
+
onClick={handleToggle}
|
|
85
|
+
{...props}
|
|
86
|
+
>
|
|
87
|
+
{getIcon(displayMode, modes)}
|
|
88
|
+
</button>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export { ThemeTogglerButton, type ThemeTogglerButtonProps };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { motion, isMotionComponent, type HTMLMotionProps } from 'motion/react';
|
|
5
|
+
import { cn } from '@mks2508/mks-ui/react';
|
|
6
|
+
|
|
7
|
+
type AnyProps = Record<string, unknown>;
|
|
8
|
+
|
|
9
|
+
type DOMMotionProps<T extends HTMLElement = HTMLElement> = Omit<
|
|
10
|
+
HTMLMotionProps<keyof HTMLElementTagNameMap>,
|
|
11
|
+
'ref'
|
|
12
|
+
> & { ref?: React.Ref<T> };
|
|
13
|
+
|
|
14
|
+
type WithAsChild<Base extends object> =
|
|
15
|
+
| (Base & { asChild: true; children: React.ReactElement })
|
|
16
|
+
| (Base & { asChild?: false | undefined });
|
|
17
|
+
|
|
18
|
+
type SlotProps<T extends HTMLElement = HTMLElement> = {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
+
children?: any;
|
|
21
|
+
} & DOMMotionProps<T>;
|
|
22
|
+
|
|
23
|
+
function mergeRefs<T>(
|
|
24
|
+
...refs: (React.Ref<T> | undefined)[]
|
|
25
|
+
): React.RefCallback<T> {
|
|
26
|
+
return (node) => {
|
|
27
|
+
refs.forEach((ref) => {
|
|
28
|
+
if (!ref) return;
|
|
29
|
+
if (typeof ref === 'function') {
|
|
30
|
+
ref(node);
|
|
31
|
+
} else {
|
|
32
|
+
(ref as React.RefObject<T | null>).current = node;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function mergeProps<T extends HTMLElement>(
|
|
39
|
+
childProps: AnyProps,
|
|
40
|
+
slotProps: DOMMotionProps<T>,
|
|
41
|
+
): AnyProps {
|
|
42
|
+
const merged: AnyProps = { ...childProps, ...slotProps };
|
|
43
|
+
|
|
44
|
+
if (childProps.className || slotProps.className) {
|
|
45
|
+
merged.className = cn(
|
|
46
|
+
childProps.className as string,
|
|
47
|
+
slotProps.className as string,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (childProps.style || slotProps.style) {
|
|
52
|
+
merged.style = {
|
|
53
|
+
...(childProps.style as React.CSSProperties),
|
|
54
|
+
...(slotProps.style as React.CSSProperties),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return merged;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function Slot<T extends HTMLElement = HTMLElement>({
|
|
62
|
+
children,
|
|
63
|
+
ref,
|
|
64
|
+
...props
|
|
65
|
+
}: SlotProps<T>) {
|
|
66
|
+
const isAlreadyMotion =
|
|
67
|
+
typeof children.type === 'object' &&
|
|
68
|
+
children.type !== null &&
|
|
69
|
+
isMotionComponent(children.type);
|
|
70
|
+
|
|
71
|
+
const Base = React.useMemo(
|
|
72
|
+
() =>
|
|
73
|
+
isAlreadyMotion
|
|
74
|
+
? (children.type as React.ElementType)
|
|
75
|
+
: motion.create(children.type as React.ElementType),
|
|
76
|
+
[isAlreadyMotion, children.type],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (!React.isValidElement(children)) return null;
|
|
80
|
+
|
|
81
|
+
const { ref: childRef, ...childProps } = children.props as AnyProps;
|
|
82
|
+
|
|
83
|
+
const mergedProps = mergeProps(childProps, props);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<Base {...mergedProps} ref={mergeRefs(childRef as React.Ref<T>, ref)} />
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
Slot,
|
|
92
|
+
type SlotProps,
|
|
93
|
+
type WithAsChild,
|
|
94
|
+
type DOMMotionProps,
|
|
95
|
+
type AnyProps,
|
|
96
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { motion, type HTMLMotionProps } from 'motion/react';
|
|
4
|
+
|
|
5
|
+
import { Slot, type WithAsChild } from '@/components/animate-ui/primitives/animate/slot';
|
|
6
|
+
|
|
7
|
+
type ButtonProps = WithAsChild<
|
|
8
|
+
HTMLMotionProps<'button'> & {
|
|
9
|
+
hoverScale?: number;
|
|
10
|
+
tapScale?: number;
|
|
11
|
+
}
|
|
12
|
+
>;
|
|
13
|
+
|
|
14
|
+
function Button({
|
|
15
|
+
hoverScale = 1.05,
|
|
16
|
+
tapScale = 0.95,
|
|
17
|
+
asChild = false,
|
|
18
|
+
...props
|
|
19
|
+
}: ButtonProps) {
|
|
20
|
+
const Component = asChild ? Slot : motion.button;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Component
|
|
24
|
+
whileTap={{ scale: tapScale }}
|
|
25
|
+
whileHover={{ scale: hoverScale }}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { Button, type ButtonProps };
|