@space-man/react-theme-animation 1.0.2 → 1.0.4
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 +172 -91
- package/dist/cjs/components/ThemeSelector.js +3 -3
- package/dist/cjs/components/ThemeSwitcher.js +10 -29
- package/dist/cjs/components/ViteThemeProvider.d.ts +19 -0
- package/dist/cjs/components/ViteThemeProvider.js +85 -0
- package/dist/cjs/components/ui/select.d.ts +3 -3
- package/dist/cjs/components/ui/select.js +9 -9
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +4 -1
- package/dist/esm/components/ThemeSelector.js +3 -3
- package/dist/esm/components/ThemeSwitcher.js +10 -29
- package/dist/esm/components/ViteThemeProvider.d.ts +19 -0
- package/dist/esm/components/ViteThemeProvider.js +80 -0
- package/dist/esm/components/ui/select.d.ts +3 -3
- package/dist/esm/components/ui/select.js +11 -11
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +1 -0
- package/package.json +14 -5
package/README.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
React theme switching with smooth view transition animations, multi-theme support, and synchronized state management.
|
|
4
4
|
|
|
5
|
+
## Demo
|
|
6
|
+
|
|
7
|
+
<video src="https://tonyedgal.github.io/spaceman-react-theme-animation/Demo.mov" controls width="600" loop autoplay muted></video>
|
|
8
|
+
|
|
9
|
+
## Live Demo
|
|
10
|
+
|
|
11
|
+
[Live Demo Link](https://spaceman-rta-vite.netlify.app/)
|
|
12
|
+
|
|
5
13
|
## Features
|
|
6
14
|
|
|
7
15
|
- 🎨 **Smooth Animations**: Beautiful view transition animations for theme switching with customizable origins
|
|
@@ -25,14 +33,17 @@ npm install @space-man/react-theme-animation
|
|
|
25
33
|
|
|
26
34
|
## Usage Patterns
|
|
27
35
|
|
|
28
|
-
This library supports
|
|
36
|
+
This library supports multiple usage patterns:
|
|
29
37
|
|
|
30
38
|
1. **Hook-Only Usage**: Direct hook usage for custom implementations
|
|
31
39
|
2. **Provider Pattern**: Centralized state management (recommended)
|
|
40
|
+
3. **ViteThemeProvider**: Specialized provider for Vite React SPAs
|
|
32
41
|
|
|
33
42
|
📚 **[See the complete Getting Started Guide](./getting-started.md)** for detailed examples, advanced configurations, and best practices.
|
|
34
43
|
|
|
35
|
-
🚀 **[View the Code Example](./example/)** to see this package in action with a complete implementation of both hook and provider pattern.
|
|
44
|
+
🚀 **[View the Next.js Code Example](./example/)** to see this package in action with a complete implementation of both hook and provider pattern.
|
|
45
|
+
|
|
46
|
+
⚡ **[View the Vite React SPA Demo](https://github.com/tonyedgal/spaceman-rta-framework-guides/tree/main/example-vite)** for a complete Vite implementation using ViteThemeProvider.
|
|
36
47
|
|
|
37
48
|
---
|
|
38
49
|
|
|
@@ -43,16 +54,16 @@ Perfect for custom theme toggle buttons and complete control over animated theme
|
|
|
43
54
|
### Basic Theme Toggle
|
|
44
55
|
|
|
45
56
|
```tsx
|
|
46
|
-
import { useThemeAnimation } from '@space-man/react-theme-animation'
|
|
57
|
+
import { useThemeAnimation } from '@space-man/react-theme-animation'
|
|
47
58
|
|
|
48
59
|
function ThemeToggle() {
|
|
49
|
-
const { theme, toggleTheme, ref } = useThemeAnimation()
|
|
60
|
+
const { theme, toggleTheme, ref } = useThemeAnimation()
|
|
50
61
|
|
|
51
62
|
return (
|
|
52
63
|
<button ref={ref} onClick={toggleTheme} className="theme-toggle-btn">
|
|
53
64
|
{theme === 'light' ? '🌙' : '🌞'} {theme}
|
|
54
65
|
</button>
|
|
55
|
-
)
|
|
66
|
+
)
|
|
56
67
|
}
|
|
57
68
|
```
|
|
58
69
|
|
|
@@ -69,7 +80,7 @@ import {
|
|
|
69
80
|
SpacemanThemeProvider,
|
|
70
81
|
ThemeSwitcher,
|
|
71
82
|
ThemeSelector,
|
|
72
|
-
} from '@space-man/react-theme-animation'
|
|
83
|
+
} from '@space-man/react-theme-animation'
|
|
73
84
|
|
|
74
85
|
function App() {
|
|
75
86
|
return (
|
|
@@ -80,7 +91,7 @@ function App() {
|
|
|
80
91
|
</header>
|
|
81
92
|
|
|
82
93
|
<aside>
|
|
83
|
-
<ThemeSelector colorThemes={['default', '
|
|
94
|
+
<ThemeSelector colorThemes={['default', 'supabase', 'mono']} />
|
|
84
95
|
</aside>
|
|
85
96
|
|
|
86
97
|
<main>
|
|
@@ -88,7 +99,117 @@ function App() {
|
|
|
88
99
|
</main>
|
|
89
100
|
</div>
|
|
90
101
|
</SpacemanThemeProvider>
|
|
91
|
-
)
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 3. ViteThemeProvider (Vite React SPAs)
|
|
109
|
+
|
|
110
|
+
Specialized theme provider optimized for Vite React single-page applications. Provides essential theme management without animation features.
|
|
111
|
+
|
|
112
|
+
### Basic Vite Setup
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { ViteThemeProvider, useViteTheme } from '@space-man/react-theme-animation'
|
|
116
|
+
|
|
117
|
+
function App() {
|
|
118
|
+
return (
|
|
119
|
+
<ViteThemeProvider defaultTheme="system" storageKey="my-app-theme" attribute="class">
|
|
120
|
+
<div className="app">
|
|
121
|
+
<header>
|
|
122
|
+
<ThemeToggle />
|
|
123
|
+
</header>
|
|
124
|
+
<main>
|
|
125
|
+
<ThemeSection />
|
|
126
|
+
</main>
|
|
127
|
+
</div>
|
|
128
|
+
</ViteThemeProvider>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function ThemeSection() {
|
|
133
|
+
return (
|
|
134
|
+
<SpacemanThemeProvider>
|
|
135
|
+
<div className="app">
|
|
136
|
+
<header>
|
|
137
|
+
<ThemeSwitcher />
|
|
138
|
+
</header>
|
|
139
|
+
|
|
140
|
+
<aside>
|
|
141
|
+
<ThemeSelector colorThemes={['default', 'supabase', 'mono']} />
|
|
142
|
+
</aside>
|
|
143
|
+
|
|
144
|
+
<main>
|
|
145
|
+
<YourAppContent />
|
|
146
|
+
</main>
|
|
147
|
+
</div>
|
|
148
|
+
</SpacemanThemeProvider>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function ThemeToggle() {
|
|
153
|
+
const { theme, setTheme, resolvedTheme } = useViteTheme()
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
|
|
157
|
+
{resolvedTheme === 'dark' ? '🌞' : '🌙'} {theme}
|
|
158
|
+
</button>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### ViteThemeProvider Props
|
|
164
|
+
|
|
165
|
+
| Prop | Type | Default | Description |
|
|
166
|
+
| --------------------------- | ------------------------------- | -------------- | ------------------------------------------- |
|
|
167
|
+
| `children` | `ReactNode` | - | React children |
|
|
168
|
+
| `attribute` | `'class' \| 'data-theme'` | `'class'` | How to apply theme to DOM |
|
|
169
|
+
| `defaultTheme` | `'light' \| 'dark' \| 'system'` | `'system'` | Default theme |
|
|
170
|
+
| `enableSystem` | `boolean` | `true` | Enable system theme detection |
|
|
171
|
+
| `disableTransitionOnChange` | `boolean` | `false` | Disable CSS transitions during theme change |
|
|
172
|
+
| `storageKey` | `string` | `'vite-theme'` | localStorage key for persistence |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Defining Application color theme as CSS Variables
|
|
177
|
+
|
|
178
|
+
The library uses CSS custom properties for theming. Define these in your **MAIN** CSS file. _Note_: do not define the other theme variables in a separate CSS file and import it, as the `:root` variables for light mode will override the light mode variables for the other themes. Keep everything in the same file.
|
|
179
|
+
|
|
180
|
+
```css
|
|
181
|
+
/* Base theme variables */
|
|
182
|
+
:root {
|
|
183
|
+
--background: 0 0% 100%;
|
|
184
|
+
--foreground: 222.2 84% 4.9%;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Dark theme */
|
|
188
|
+
.dark {
|
|
189
|
+
--background: 222.2 84% 4.9%;
|
|
190
|
+
--foreground: 210 40% 98%;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Add other theme variants from design system or tweakcn */
|
|
194
|
+
|
|
195
|
+
.theme-supabase {
|
|
196
|
+
--background: oklch(0.9911 0 0);
|
|
197
|
+
--foreground: oklch(0.2046 0 0);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.theme-supabase.dark {
|
|
201
|
+
--background: oklch(0.1822 0 0);
|
|
202
|
+
--foreground: oklch(0.9288 0.0126 255.5078);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.theme-mono {
|
|
206
|
+
--background: oklch(1 0 0);
|
|
207
|
+
--foreground: oklch(0.1448 0 0);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.theme-mono.dark {
|
|
211
|
+
--background: oklch(0.1448 0 0);
|
|
212
|
+
--foreground: oklch(1 0 0);
|
|
92
213
|
}
|
|
93
214
|
```
|
|
94
215
|
|
|
@@ -98,7 +219,7 @@ function App() {
|
|
|
98
219
|
|
|
99
220
|
### SpacemanThemeProvider
|
|
100
221
|
|
|
101
|
-
Context provider for centralized theme state management.
|
|
222
|
+
Context provider for centralized theme state management for the ThemeSelector and ThemeSwitcher components. The SpacemanThemeProvider allows you to manage themes and color themes in your application with smooth animations and synchronized state and provide a state reference for the ThemeSelector and ThemeSwitcher components.
|
|
102
223
|
|
|
103
224
|
```tsx
|
|
104
225
|
<SpacemanThemeProvider
|
|
@@ -111,7 +232,7 @@ Context provider for centralized theme state management.
|
|
|
111
232
|
onThemeChange={theme => console.log('Theme:', theme)}
|
|
112
233
|
onColorThemeChange={colorTheme => console.log('Color:', colorTheme)}
|
|
113
234
|
>
|
|
114
|
-
|
|
235
|
+
{children}
|
|
115
236
|
</SpacemanThemeProvider>
|
|
116
237
|
```
|
|
117
238
|
|
|
@@ -135,7 +256,7 @@ Hook to access theme state from SpacemanThemeProvider context.
|
|
|
135
256
|
|
|
136
257
|
```tsx
|
|
137
258
|
const { theme, colorTheme, switchTheme, setColorTheme, switchThemeFromElement, ref } =
|
|
138
|
-
useSpacemanTheme()
|
|
259
|
+
useSpacemanTheme()
|
|
139
260
|
```
|
|
140
261
|
|
|
141
262
|
#### Returns
|
|
@@ -161,7 +282,7 @@ const { theme, colorTheme, switchTheme, setColorTheme, toggleTheme, ref } = useT
|
|
|
161
282
|
duration: 800,
|
|
162
283
|
onThemeChange: theme => console.log('Theme changed:', theme),
|
|
163
284
|
onColorThemeChange: colorTheme => console.log('Color changed:', colorTheme),
|
|
164
|
-
})
|
|
285
|
+
})
|
|
165
286
|
```
|
|
166
287
|
|
|
167
288
|
#### Options
|
|
@@ -258,104 +379,64 @@ enum ThemeAnimationType {
|
|
|
258
379
|
### Types
|
|
259
380
|
|
|
260
381
|
```tsx
|
|
261
|
-
type Theme = 'light' | 'dark' | 'system'
|
|
262
|
-
type ColorTheme = string
|
|
382
|
+
type Theme = 'light' | 'dark' | 'system'
|
|
383
|
+
type ColorTheme = string // e.g., 'default', 'blue', 'green', etc.
|
|
263
384
|
|
|
264
385
|
interface SpacemanThemeProviderProps {
|
|
265
|
-
children: React.ReactNode
|
|
266
|
-
defaultTheme?: Theme
|
|
267
|
-
defaultColorTheme?: ColorTheme
|
|
268
|
-
themes?: Theme[]
|
|
269
|
-
colorThemes?: ColorTheme[]
|
|
270
|
-
animationType?: ThemeAnimationType
|
|
271
|
-
duration?: number
|
|
272
|
-
blurAmount?: number
|
|
273
|
-
onThemeChange?: (theme: Theme) => void
|
|
274
|
-
onColorThemeChange?: (colorTheme: ColorTheme) => void
|
|
386
|
+
children: React.ReactNode
|
|
387
|
+
defaultTheme?: Theme
|
|
388
|
+
defaultColorTheme?: ColorTheme
|
|
389
|
+
themes?: Theme[]
|
|
390
|
+
colorThemes?: ColorTheme[]
|
|
391
|
+
animationType?: ThemeAnimationType
|
|
392
|
+
duration?: number
|
|
393
|
+
blurAmount?: number
|
|
394
|
+
onThemeChange?: (theme: Theme) => void
|
|
395
|
+
onColorThemeChange?: (colorTheme: ColorTheme) => void
|
|
275
396
|
}
|
|
276
397
|
|
|
277
398
|
interface UseThemeAnimationProps {
|
|
278
|
-
themes?: Theme[]
|
|
279
|
-
colorThemes?: ColorTheme[]
|
|
280
|
-
theme?: Theme
|
|
281
|
-
colorTheme?: ColorTheme
|
|
282
|
-
animationType?: ThemeAnimationType
|
|
283
|
-
duration?: number
|
|
284
|
-
blurAmount?: number
|
|
285
|
-
onThemeChange?: (theme: Theme) => void
|
|
286
|
-
onColorThemeChange?: (colorTheme: ColorTheme) => void
|
|
399
|
+
themes?: Theme[]
|
|
400
|
+
colorThemes?: ColorTheme[]
|
|
401
|
+
theme?: Theme
|
|
402
|
+
colorTheme?: ColorTheme
|
|
403
|
+
animationType?: ThemeAnimationType
|
|
404
|
+
duration?: number
|
|
405
|
+
blurAmount?: number
|
|
406
|
+
onThemeChange?: (theme: Theme) => void
|
|
407
|
+
onColorThemeChange?: (colorTheme: ColorTheme) => void
|
|
287
408
|
}
|
|
288
409
|
|
|
289
410
|
interface UseThemeAnimationReturn {
|
|
290
|
-
theme: Theme
|
|
291
|
-
colorTheme: ColorTheme
|
|
292
|
-
switchTheme: (theme: Theme) => Promise<void
|
|
293
|
-
setColorTheme: (colorTheme: ColorTheme) => void
|
|
294
|
-
toggleTheme: () => Promise<void
|
|
295
|
-
ref: RefObject<HTMLElement
|
|
411
|
+
theme: Theme
|
|
412
|
+
colorTheme: ColorTheme
|
|
413
|
+
switchTheme: (theme: Theme) => Promise<void>
|
|
414
|
+
setColorTheme: (colorTheme: ColorTheme) => void
|
|
415
|
+
toggleTheme: () => Promise<void>
|
|
416
|
+
ref: RefObject<HTMLElement>
|
|
296
417
|
}
|
|
297
418
|
|
|
298
419
|
interface ThemeSwitcherProps {
|
|
299
|
-
themes?: Theme[]
|
|
300
|
-
currentTheme?: Theme
|
|
301
|
-
onThemeChange?: (theme: Theme) => void
|
|
302
|
-
animationType?: ThemeAnimationType
|
|
303
|
-
duration?: number
|
|
304
|
-
className?: string
|
|
420
|
+
themes?: Theme[]
|
|
421
|
+
currentTheme?: Theme
|
|
422
|
+
onThemeChange?: (theme: Theme) => void
|
|
423
|
+
animationType?: ThemeAnimationType
|
|
424
|
+
duration?: number
|
|
425
|
+
className?: string
|
|
305
426
|
}
|
|
306
427
|
|
|
307
428
|
interface ThemeSelectorProps {
|
|
308
|
-
themes?: Theme[]
|
|
309
|
-
colorThemes?: ColorTheme[]
|
|
310
|
-
currentColorTheme?: ColorTheme
|
|
311
|
-
onColorThemeChange?: (colorTheme: ColorTheme) => void
|
|
312
|
-
animationType?: ThemeAnimationType
|
|
313
|
-
duration?: number
|
|
429
|
+
themes?: Theme[]
|
|
430
|
+
colorThemes?: ColorTheme[]
|
|
431
|
+
currentColorTheme?: ColorTheme
|
|
432
|
+
onColorThemeChange?: (colorTheme: ColorTheme) => void
|
|
433
|
+
animationType?: ThemeAnimationType
|
|
434
|
+
duration?: number
|
|
314
435
|
}
|
|
315
436
|
```
|
|
316
437
|
|
|
317
438
|
---
|
|
318
439
|
|
|
319
|
-
## CSS Variables
|
|
320
|
-
|
|
321
|
-
The library uses CSS custom properties for theming. Define these in your CSS:
|
|
322
|
-
|
|
323
|
-
```css
|
|
324
|
-
/* Base theme variables */
|
|
325
|
-
:root {
|
|
326
|
-
--background: 0 0% 100%;
|
|
327
|
-
--foreground: 222.2 84% 4.9%;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/* Dark theme */
|
|
331
|
-
.dark {
|
|
332
|
-
--background: 222.2 84% 4.9%;
|
|
333
|
-
--foreground: 210 40% 98%;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/* Add other theme variants from design system or tweakcn */
|
|
337
|
-
|
|
338
|
-
.theme-supabase {
|
|
339
|
-
--background: oklch(0.9911 0 0);
|
|
340
|
-
--foreground: oklch(0.2046 0 0);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
.theme-supabase.dark {
|
|
344
|
-
--background: oklch(0.1822 0 0);
|
|
345
|
-
--foreground: oklch(0.9288 0.0126 255.5078);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
.theme-mono {
|
|
349
|
-
--background: oklch(1 0 0);
|
|
350
|
-
--foreground: oklch(0.1448 0 0);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
.theme-mono.dark {
|
|
354
|
-
--background: oklch(0.1448 0 0);
|
|
355
|
-
--foreground: oklch(1 0 0);
|
|
356
|
-
}
|
|
357
|
-
```
|
|
358
|
-
|
|
359
440
|
---
|
|
360
441
|
|
|
361
442
|
## Browser Support
|
|
@@ -24,9 +24,9 @@ const ThemeSelector = ({ themes = ['light', 'dark', 'system'], colorThemes = ['d
|
|
|
24
24
|
onColorThemeChange,
|
|
25
25
|
});
|
|
26
26
|
const isControlled = contextTheme !== null;
|
|
27
|
-
const { colorTheme, setColorTheme } = isControlled
|
|
28
|
-
{ colorTheme: contextTheme.colorTheme, setColorTheme: contextTheme.setColorTheme }
|
|
29
|
-
standaloneHook;
|
|
27
|
+
const { colorTheme, setColorTheme } = isControlled
|
|
28
|
+
? { colorTheme: contextTheme.colorTheme, setColorTheme: contextTheme.setColorTheme }
|
|
29
|
+
: standaloneHook;
|
|
30
30
|
const handleColorThemeChange = (newColorTheme) => {
|
|
31
31
|
setColorTheme(newColorTheme);
|
|
32
32
|
// Also call the callback if provided
|
|
@@ -11,9 +11,9 @@ const SunIcon = () => ((0, jsx_runtime_1.jsxs)("svg", { width: "16", height: "16
|
|
|
11
11
|
const MoonIcon = () => ((0, jsx_runtime_1.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: (0, jsx_runtime_1.jsx)("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }) }));
|
|
12
12
|
const MonitorIcon = () => ((0, jsx_runtime_1.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [(0, jsx_runtime_1.jsx)("rect", { width: "20", height: "14", x: "2", y: "3", rx: "2" }), (0, jsx_runtime_1.jsx)("line", { x1: "8", x2: "16", y1: "21", y2: "21" }), (0, jsx_runtime_1.jsx)("line", { x1: "12", x2: "12", y1: "17", y2: "21" })] }));
|
|
13
13
|
const ThemeOption = ({ icon, value, isActive, isHovered, onClick, onMouseEnter, onMouseLeave, buttonRef, }) => {
|
|
14
|
-
return ((0, jsx_runtime_1.jsxs)("button", { ref: isActive ? buttonRef : undefined, className: (0, clsx_1.clsx)('relative flex h-9 w-12 cursor-pointer items-center justify-center
|
|
14
|
+
return ((0, jsx_runtime_1.jsxs)("button", { ref: isActive ? buttonRef : undefined, className: (0, clsx_1.clsx)('relative flex h-9 w-12 cursor-pointer items-center justify-center', 'text-muted-foreground hover:text-foreground', isActive && 'text-foreground font-medium'), role: "radio", "aria-checked": isActive, "aria-label": `Switch to ${value} theme`, onClick: event => onClick(value, event), onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, style: {
|
|
15
15
|
borderRadius: 'var(--radius)',
|
|
16
|
-
}, children: [isHovered && ((0, jsx_runtime_1.jsx)(react_2.motion.div, { layoutId: "theme-hover
|
|
16
|
+
}, children: [isHovered && ((0, jsx_runtime_1.jsx)(react_2.motion.div, { layoutId: "theme-hover", className: "absolute inset-0 bg-muted", style: {
|
|
17
17
|
borderRadius: 'var(--radius)',
|
|
18
18
|
}, transition: {
|
|
19
19
|
type: 'spring',
|
|
@@ -21,18 +21,8 @@ const ThemeOption = ({ icon, value, isActive, isHovered, onClick, onMouseEnter,
|
|
|
21
21
|
stiffness: 100,
|
|
22
22
|
damping: 10,
|
|
23
23
|
duration: 0.3,
|
|
24
|
-
} })), (0, jsx_runtime_1.jsx)(
|
|
25
|
-
scale: isActive ? 1.1 : 1,
|
|
26
|
-
}, transition: {
|
|
27
|
-
type: 'spring',
|
|
28
|
-
bounce: 0.2,
|
|
29
|
-
duration: 0.4,
|
|
30
|
-
}, children: (0, jsx_runtime_1.jsx)("div", { className: "[&_svg]:size-4", children: icon }) }), isActive && ((0, jsx_runtime_1.jsx)(react_2.motion.div, { layoutId: "theme-active-indicator", className: "absolute bottom-0 left-1/2 h-0.5 w-6 -translate-x-1/2 rounded-full", style: {
|
|
24
|
+
} })), (0, jsx_runtime_1.jsx)("div", { className: "relative z-10 flex items-center justify-center", children: (0, jsx_runtime_1.jsx)("div", { className: "[&_svg]:size-4", children: icon }) }), isActive && ((0, jsx_runtime_1.jsx)("div", { className: "absolute bottom-0 left-1/2 h-0.5 w-6 -translate-x-1/2 rounded-full", style: {
|
|
31
25
|
backgroundColor: 'hsl(var(--primary))',
|
|
32
|
-
}, transition: {
|
|
33
|
-
type: 'spring',
|
|
34
|
-
bounce: 0.3,
|
|
35
|
-
duration: 0.6,
|
|
36
26
|
} }))] }));
|
|
37
27
|
};
|
|
38
28
|
const THEME_OPTIONS = [
|
|
@@ -50,15 +40,11 @@ const THEME_OPTIONS = [
|
|
|
50
40
|
},
|
|
51
41
|
];
|
|
52
42
|
const ThemeSwitcher = ({ themes = ['light', 'dark', 'system'], currentTheme, onThemeChange, animationType, duration, className, }) => {
|
|
53
|
-
// Try to use SpacemanTheme context if available (controlled mode)
|
|
54
43
|
let contextTheme = null;
|
|
55
44
|
try {
|
|
56
45
|
contextTheme = (0, SpacemanThemeProvider_1.useSpacemanTheme)();
|
|
57
46
|
}
|
|
58
|
-
catch {
|
|
59
|
-
// Context not available, use standalone mode
|
|
60
|
-
}
|
|
61
|
-
// Use context if available, otherwise fall back to standalone hook
|
|
47
|
+
catch { }
|
|
62
48
|
const standaloneHook = (0, use_theme_animation_1.useThemeAnimation)({
|
|
63
49
|
animationType,
|
|
64
50
|
duration,
|
|
@@ -67,34 +53,29 @@ const ThemeSwitcher = ({ themes = ['light', 'dark', 'system'], currentTheme, onT
|
|
|
67
53
|
onThemeChange,
|
|
68
54
|
});
|
|
69
55
|
const isControlled = contextTheme !== null;
|
|
70
|
-
const { ref, theme, switchTheme } = isControlled
|
|
71
|
-
{ ref: contextTheme.ref, theme: contextTheme.theme, switchTheme: contextTheme.switchTheme }
|
|
72
|
-
standaloneHook;
|
|
56
|
+
const { ref, theme, switchTheme } = isControlled
|
|
57
|
+
? { ref: contextTheme.ref, theme: contextTheme.theme, switchTheme: contextTheme.switchTheme }
|
|
58
|
+
: standaloneHook;
|
|
73
59
|
const [isMounted, setIsMounted] = (0, react_1.useState)(false);
|
|
60
|
+
const [hoveredTheme, setHoveredTheme] = (0, react_1.useState)(null);
|
|
74
61
|
(0, react_1.useEffect)(() => {
|
|
75
62
|
setIsMounted(true);
|
|
76
63
|
}, []);
|
|
77
64
|
const handleThemeChange = async (newTheme, event) => {
|
|
78
65
|
if (isControlled && contextTheme.switchThemeFromElement && event) {
|
|
79
|
-
// Use controlled mode with animation from clicked element
|
|
80
66
|
await contextTheme.switchThemeFromElement(newTheme, event.currentTarget);
|
|
81
|
-
// Also call the callback if provided
|
|
82
67
|
if (onThemeChange) {
|
|
83
68
|
onThemeChange(newTheme);
|
|
84
69
|
}
|
|
85
70
|
}
|
|
86
71
|
else {
|
|
87
|
-
// Use standalone mode
|
|
88
72
|
await switchTheme(newTheme);
|
|
89
73
|
}
|
|
90
74
|
};
|
|
91
|
-
if (!isMounted) {
|
|
92
|
-
return (0, jsx_runtime_1.jsx)("div", { className: "flex h-9 w-fit" });
|
|
93
|
-
}
|
|
94
75
|
const filteredOptions = THEME_OPTIONS.filter(option => themes.includes(option.value));
|
|
95
|
-
return ((0, jsx_runtime_1.jsx)(
|
|
76
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: (0, clsx_1.clsx)('inline-flex h-9 items-center overflow-hidden bg-background shadow-sm border border-border', isMounted ? 'opacity-100' : 'opacity-0', className), style: {
|
|
96
77
|
borderRadius: 'var(--radius)',
|
|
97
78
|
backgroundColor: 'hsl(var(--background))',
|
|
98
|
-
}, role: "radiogroup", children: filteredOptions.map(option => ((0, jsx_runtime_1.jsx)(ThemeOption, { icon: option.icon, value: option.value, isActive: theme === option.value, isHovered:
|
|
79
|
+
}, role: "radiogroup", onMouseLeave: () => setHoveredTheme(null), children: filteredOptions.map(option => ((0, jsx_runtime_1.jsx)(ThemeOption, { icon: option.icon, value: option.value, isActive: theme === option.value, isHovered: hoveredTheme === option.value, onClick: handleThemeChange, onMouseEnter: () => setHoveredTheme(option.value), onMouseLeave: () => { }, buttonRef: theme === option.value ? ref : undefined }, option.value))) }));
|
|
99
80
|
};
|
|
100
81
|
exports.ThemeSwitcher = ThemeSwitcher;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type Theme = 'light' | 'dark' | 'system';
|
|
3
|
+
export interface ViteThemeContextType {
|
|
4
|
+
theme: Theme;
|
|
5
|
+
resolvedTheme: 'light' | 'dark';
|
|
6
|
+
setTheme: (theme: Theme) => void;
|
|
7
|
+
systemTheme: 'light' | 'dark';
|
|
8
|
+
}
|
|
9
|
+
interface ViteThemeProviderProps {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
attribute?: 'class' | 'data-theme';
|
|
12
|
+
defaultTheme?: Theme;
|
|
13
|
+
enableSystem?: boolean;
|
|
14
|
+
disableTransitionOnChange?: boolean;
|
|
15
|
+
storageKey?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const ViteThemeProvider: React.FC<ViteThemeProviderProps>;
|
|
18
|
+
export declare const useViteTheme: () => ViteThemeContextType;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useViteTheme = exports.ViteThemeProvider = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const ViteThemeContext = (0, react_1.createContext)(undefined);
|
|
7
|
+
const ViteThemeProvider = ({ children, attribute = 'class', defaultTheme = 'system', enableSystem = true, disableTransitionOnChange = false, storageKey = 'vite-theme', }) => {
|
|
8
|
+
const [mounted, setMounted] = (0, react_1.useState)(false);
|
|
9
|
+
const [systemTheme, setSystemTheme] = (0, react_1.useState)('light');
|
|
10
|
+
const [theme, setThemeState] = (0, react_1.useState)(() => {
|
|
11
|
+
if (typeof window === 'undefined')
|
|
12
|
+
return defaultTheme;
|
|
13
|
+
try {
|
|
14
|
+
const savedTheme = localStorage.getItem(storageKey);
|
|
15
|
+
return savedTheme && ['light', 'dark', 'system'].includes(savedTheme)
|
|
16
|
+
? savedTheme
|
|
17
|
+
: defaultTheme;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return defaultTheme;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
(0, react_1.useEffect)(() => {
|
|
24
|
+
if (!enableSystem)
|
|
25
|
+
return;
|
|
26
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
27
|
+
const updateSystemTheme = () => {
|
|
28
|
+
setSystemTheme(mediaQuery.matches ? 'dark' : 'light');
|
|
29
|
+
};
|
|
30
|
+
updateSystemTheme();
|
|
31
|
+
mediaQuery.addEventListener('change', updateSystemTheme);
|
|
32
|
+
return () => mediaQuery.removeEventListener('change', updateSystemTheme);
|
|
33
|
+
}, [enableSystem]);
|
|
34
|
+
const resolvedTheme = theme === 'system' ? systemTheme : theme;
|
|
35
|
+
(0, react_1.useEffect)(() => {
|
|
36
|
+
if (!mounted)
|
|
37
|
+
return;
|
|
38
|
+
const root = document.documentElement;
|
|
39
|
+
if (disableTransitionOnChange) {
|
|
40
|
+
const css = document.createElement('style');
|
|
41
|
+
css.textContent = `*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}`;
|
|
42
|
+
document.head.appendChild(css);
|
|
43
|
+
window.getComputedStyle(document.body);
|
|
44
|
+
setTimeout(() => {
|
|
45
|
+
document.head.removeChild(css);
|
|
46
|
+
}, 1);
|
|
47
|
+
}
|
|
48
|
+
if (attribute === 'class') {
|
|
49
|
+
root.classList.remove('light', 'dark');
|
|
50
|
+
root.classList.add(resolvedTheme);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
root.setAttribute(attribute, resolvedTheme);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
localStorage.setItem(storageKey, theme);
|
|
57
|
+
}
|
|
58
|
+
catch { }
|
|
59
|
+
}, [theme, resolvedTheme, mounted, attribute, disableTransitionOnChange, storageKey]);
|
|
60
|
+
(0, react_1.useEffect)(() => {
|
|
61
|
+
setMounted(true);
|
|
62
|
+
}, []);
|
|
63
|
+
const setTheme = (0, react_1.useCallback)((newTheme) => {
|
|
64
|
+
setThemeState(newTheme);
|
|
65
|
+
}, []);
|
|
66
|
+
if (!mounted) {
|
|
67
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
|
|
68
|
+
}
|
|
69
|
+
const value = {
|
|
70
|
+
theme,
|
|
71
|
+
resolvedTheme,
|
|
72
|
+
setTheme,
|
|
73
|
+
systemTheme,
|
|
74
|
+
};
|
|
75
|
+
return (0, jsx_runtime_1.jsx)(ViteThemeContext.Provider, { value: value, children: children });
|
|
76
|
+
};
|
|
77
|
+
exports.ViteThemeProvider = ViteThemeProvider;
|
|
78
|
+
const useViteTheme = () => {
|
|
79
|
+
const context = (0, react_1.useContext)(ViteThemeContext);
|
|
80
|
+
if (context === undefined) {
|
|
81
|
+
throw new Error('useViteTheme must be used within a ViteThemeProvider');
|
|
82
|
+
}
|
|
83
|
+
return context;
|
|
84
|
+
};
|
|
85
|
+
exports.useViteTheme = useViteTheme;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import * as SelectPrimitive from
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
3
3
|
declare function Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
declare function SelectGroup({ ...props }: React.ComponentProps<typeof SelectPrimitive.Group>): import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
declare function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>): import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
declare function SelectTrigger({ className, size, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
|
7
|
-
size?:
|
|
7
|
+
size?: 'sm' | 'default';
|
|
8
8
|
}): import("react/jsx-runtime").JSX.Element;
|
|
9
9
|
declare function SelectContent({ className, children, position, ...props }: React.ComponentProps<typeof SelectPrimitive.Content>): import("react/jsx-runtime").JSX.Element;
|
|
10
10
|
declare function SelectLabel({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Label>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -58,26 +58,26 @@ function SelectValue({ ...props }) {
|
|
|
58
58
|
const ChevronDownIcon = () => ((0, jsx_runtime_1.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: (0, jsx_runtime_1.jsx)("path", { d: "m6 9 6 6 6-6" }) }));
|
|
59
59
|
const ChevronUpIcon = () => ((0, jsx_runtime_1.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: (0, jsx_runtime_1.jsx)("path", { d: "m18 15-6-6-6 6" }) }));
|
|
60
60
|
const CheckIcon = () => ((0, jsx_runtime_1.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: (0, jsx_runtime_1.jsx)("path", { d: "M20 6 9 17l-5-5" }) }));
|
|
61
|
-
function SelectTrigger({ className, size =
|
|
61
|
+
function SelectTrigger({ className, size = 'default', children, ...props }) {
|
|
62
62
|
return ((0, jsx_runtime_1.jsxs)(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: (0, clsx_1.clsx)("border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props, children: [children, (0, jsx_runtime_1.jsx)(SelectPrimitive.Icon, { asChild: true, children: (0, jsx_runtime_1.jsx)(ChevronDownIcon, {}) })] }));
|
|
63
63
|
}
|
|
64
|
-
function SelectContent({ className, children, position =
|
|
65
|
-
return ((0, jsx_runtime_1.jsx)(SelectPrimitive.Portal, { children: (0, jsx_runtime_1.jsx)(SelectPrimitive.Content, { "data-slot": "select-content", className: (0, clsx_1.clsx)(
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
function SelectContent({ className, children, position = 'popper', ...props }) {
|
|
65
|
+
return ((0, jsx_runtime_1.jsx)(SelectPrimitive.Portal, { children: (0, jsx_runtime_1.jsx)(SelectPrimitive.Content, { "data-slot": "select-content", className: (0, clsx_1.clsx)('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md', position === 'popper' &&
|
|
66
|
+
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', className), position: position, ...props, children: (0, jsx_runtime_1.jsx)(SelectPrimitive.Viewport, { className: (0, clsx_1.clsx)('p-1', position === 'popper' &&
|
|
67
|
+
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'), children: children }) }) }));
|
|
68
68
|
}
|
|
69
69
|
function SelectLabel({ className, ...props }) {
|
|
70
|
-
return ((0, jsx_runtime_1.jsx)(SelectPrimitive.Label, { "data-slot": "select-label", className: (0, clsx_1.clsx)(
|
|
70
|
+
return ((0, jsx_runtime_1.jsx)(SelectPrimitive.Label, { "data-slot": "select-label", className: (0, clsx_1.clsx)('py-1.5 pl-8 pr-2 text-sm font-semibold', className), ...props }));
|
|
71
71
|
}
|
|
72
72
|
function SelectItem({ className, children, ...props }) {
|
|
73
73
|
return ((0, jsx_runtime_1.jsxs)(SelectPrimitive.Item, { "data-slot": "select-item", className: (0, clsx_1.clsx)("focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className), ...props, children: [(0, jsx_runtime_1.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: (0, jsx_runtime_1.jsx)(SelectPrimitive.ItemIndicator, { children: (0, jsx_runtime_1.jsx)(CheckIcon, {}) }) }), (0, jsx_runtime_1.jsx)(SelectPrimitive.ItemText, { children: children })] }));
|
|
74
74
|
}
|
|
75
75
|
function SelectSeparator({ className, ...props }) {
|
|
76
|
-
return ((0, jsx_runtime_1.jsx)(SelectPrimitive.Separator, { "data-slot": "select-separator", className: (0, clsx_1.clsx)(
|
|
76
|
+
return ((0, jsx_runtime_1.jsx)(SelectPrimitive.Separator, { "data-slot": "select-separator", className: (0, clsx_1.clsx)('-mx-1 my-1 h-px bg-muted', className), ...props }));
|
|
77
77
|
}
|
|
78
78
|
function SelectScrollUpButton({ className, ...props }) {
|
|
79
|
-
return ((0, jsx_runtime_1.jsx)(SelectPrimitive.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: (0, clsx_1.clsx)(
|
|
79
|
+
return ((0, jsx_runtime_1.jsx)(SelectPrimitive.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: (0, clsx_1.clsx)('flex cursor-default items-center justify-center py-1', className), ...props, children: (0, jsx_runtime_1.jsx)(ChevronUpIcon, {}) }));
|
|
80
80
|
}
|
|
81
81
|
function SelectScrollDownButton({ className, ...props }) {
|
|
82
|
-
return ((0, jsx_runtime_1.jsx)(SelectPrimitive.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: (0, clsx_1.clsx)(
|
|
82
|
+
return ((0, jsx_runtime_1.jsx)(SelectPrimitive.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: (0, clsx_1.clsx)('flex cursor-default items-center justify-center py-1', className), ...props, children: (0, jsx_runtime_1.jsx)(ChevronDownIcon, {}) }));
|
|
83
83
|
}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { useThemeAnimation } from './hooks/use-theme-animation';
|
|
|
2
2
|
export { ThemeSwitcher } from './components/ThemeSwitcher';
|
|
3
3
|
export { ThemeSelector } from './components/ThemeSelector';
|
|
4
4
|
export { SpacemanThemeProvider, useSpacemanTheme } from './components/SpacemanThemeProvider';
|
|
5
|
+
export { ViteThemeProvider, useViteTheme } from './components/ViteThemeProvider';
|
|
5
6
|
export type { UseThemeAnimationProps, UseThemeAnimationReturn, ThemeSwitcherProps, ThemeSelectorProps, Theme, ColorTheme, } from './types';
|
|
7
|
+
export type { ViteThemeContextType } from './components/ViteThemeProvider';
|
|
6
8
|
export { ThemeAnimationType } from './types';
|
|
7
9
|
export { injectBaseStyles, getSystemTheme, resolveTheme, supportsViewTransitions, prefersReducedMotion, } from './utils/animations';
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.prefersReducedMotion = exports.supportsViewTransitions = exports.resolveTheme = exports.getSystemTheme = exports.injectBaseStyles = exports.ThemeAnimationType = exports.useSpacemanTheme = exports.SpacemanThemeProvider = exports.ThemeSelector = exports.ThemeSwitcher = exports.useThemeAnimation = void 0;
|
|
3
|
+
exports.prefersReducedMotion = exports.supportsViewTransitions = exports.resolveTheme = exports.getSystemTheme = exports.injectBaseStyles = exports.ThemeAnimationType = exports.useViteTheme = exports.ViteThemeProvider = exports.useSpacemanTheme = exports.SpacemanThemeProvider = exports.ThemeSelector = exports.ThemeSwitcher = exports.useThemeAnimation = void 0;
|
|
4
4
|
var use_theme_animation_1 = require("./hooks/use-theme-animation");
|
|
5
5
|
Object.defineProperty(exports, "useThemeAnimation", { enumerable: true, get: function () { return use_theme_animation_1.useThemeAnimation; } });
|
|
6
6
|
var ThemeSwitcher_1 = require("./components/ThemeSwitcher");
|
|
@@ -10,6 +10,9 @@ Object.defineProperty(exports, "ThemeSelector", { enumerable: true, get: functio
|
|
|
10
10
|
var SpacemanThemeProvider_1 = require("./components/SpacemanThemeProvider");
|
|
11
11
|
Object.defineProperty(exports, "SpacemanThemeProvider", { enumerable: true, get: function () { return SpacemanThemeProvider_1.SpacemanThemeProvider; } });
|
|
12
12
|
Object.defineProperty(exports, "useSpacemanTheme", { enumerable: true, get: function () { return SpacemanThemeProvider_1.useSpacemanTheme; } });
|
|
13
|
+
var ViteThemeProvider_1 = require("./components/ViteThemeProvider");
|
|
14
|
+
Object.defineProperty(exports, "ViteThemeProvider", { enumerable: true, get: function () { return ViteThemeProvider_1.ViteThemeProvider; } });
|
|
15
|
+
Object.defineProperty(exports, "useViteTheme", { enumerable: true, get: function () { return ViteThemeProvider_1.useViteTheme; } });
|
|
13
16
|
var types_1 = require("./types");
|
|
14
17
|
Object.defineProperty(exports, "ThemeAnimationType", { enumerable: true, get: function () { return types_1.ThemeAnimationType; } });
|
|
15
18
|
var animations_1 = require("./utils/animations");
|
|
@@ -21,9 +21,9 @@ export const ThemeSelector = ({ themes = ['light', 'dark', 'system'], colorTheme
|
|
|
21
21
|
onColorThemeChange,
|
|
22
22
|
});
|
|
23
23
|
const isControlled = contextTheme !== null;
|
|
24
|
-
const { colorTheme, setColorTheme } = isControlled
|
|
25
|
-
{ colorTheme: contextTheme.colorTheme, setColorTheme: contextTheme.setColorTheme }
|
|
26
|
-
standaloneHook;
|
|
24
|
+
const { colorTheme, setColorTheme } = isControlled
|
|
25
|
+
? { colorTheme: contextTheme.colorTheme, setColorTheme: contextTheme.setColorTheme }
|
|
26
|
+
: standaloneHook;
|
|
27
27
|
const handleColorThemeChange = (newColorTheme) => {
|
|
28
28
|
setColorTheme(newColorTheme);
|
|
29
29
|
// Also call the callback if provided
|
|
@@ -8,9 +8,9 @@ const SunIcon = () => (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 2
|
|
|
8
8
|
const MoonIcon = () => (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }) }));
|
|
9
9
|
const MonitorIcon = () => (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { width: "20", height: "14", x: "2", y: "3", rx: "2" }), _jsx("line", { x1: "8", x2: "16", y1: "21", y2: "21" }), _jsx("line", { x1: "12", x2: "12", y1: "17", y2: "21" })] }));
|
|
10
10
|
const ThemeOption = ({ icon, value, isActive, isHovered, onClick, onMouseEnter, onMouseLeave, buttonRef, }) => {
|
|
11
|
-
return (_jsxs("button", { ref: isActive ? buttonRef : undefined, className: clsx('relative flex h-9 w-12 cursor-pointer items-center justify-center
|
|
11
|
+
return (_jsxs("button", { ref: isActive ? buttonRef : undefined, className: clsx('relative flex h-9 w-12 cursor-pointer items-center justify-center', 'text-muted-foreground hover:text-foreground', isActive && 'text-foreground font-medium'), role: "radio", "aria-checked": isActive, "aria-label": `Switch to ${value} theme`, onClick: event => onClick(value, event), onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, style: {
|
|
12
12
|
borderRadius: 'var(--radius)',
|
|
13
|
-
}, children: [isHovered && (_jsx(motion.div, { layoutId: "theme-hover
|
|
13
|
+
}, children: [isHovered && (_jsx(motion.div, { layoutId: "theme-hover", className: "absolute inset-0 bg-muted", style: {
|
|
14
14
|
borderRadius: 'var(--radius)',
|
|
15
15
|
}, transition: {
|
|
16
16
|
type: 'spring',
|
|
@@ -18,18 +18,8 @@ const ThemeOption = ({ icon, value, isActive, isHovered, onClick, onMouseEnter,
|
|
|
18
18
|
stiffness: 100,
|
|
19
19
|
damping: 10,
|
|
20
20
|
duration: 0.3,
|
|
21
|
-
} })), _jsx(
|
|
22
|
-
scale: isActive ? 1.1 : 1,
|
|
23
|
-
}, transition: {
|
|
24
|
-
type: 'spring',
|
|
25
|
-
bounce: 0.2,
|
|
26
|
-
duration: 0.4,
|
|
27
|
-
}, children: _jsx("div", { className: "[&_svg]:size-4", children: icon }) }), isActive && (_jsx(motion.div, { layoutId: "theme-active-indicator", className: "absolute bottom-0 left-1/2 h-0.5 w-6 -translate-x-1/2 rounded-full", style: {
|
|
21
|
+
} })), _jsx("div", { className: "relative z-10 flex items-center justify-center", children: _jsx("div", { className: "[&_svg]:size-4", children: icon }) }), isActive && (_jsx("div", { className: "absolute bottom-0 left-1/2 h-0.5 w-6 -translate-x-1/2 rounded-full", style: {
|
|
28
22
|
backgroundColor: 'hsl(var(--primary))',
|
|
29
|
-
}, transition: {
|
|
30
|
-
type: 'spring',
|
|
31
|
-
bounce: 0.3,
|
|
32
|
-
duration: 0.6,
|
|
33
23
|
} }))] }));
|
|
34
24
|
};
|
|
35
25
|
const THEME_OPTIONS = [
|
|
@@ -47,15 +37,11 @@ const THEME_OPTIONS = [
|
|
|
47
37
|
},
|
|
48
38
|
];
|
|
49
39
|
export const ThemeSwitcher = ({ themes = ['light', 'dark', 'system'], currentTheme, onThemeChange, animationType, duration, className, }) => {
|
|
50
|
-
// Try to use SpacemanTheme context if available (controlled mode)
|
|
51
40
|
let contextTheme = null;
|
|
52
41
|
try {
|
|
53
42
|
contextTheme = useSpacemanTheme();
|
|
54
43
|
}
|
|
55
|
-
catch {
|
|
56
|
-
// Context not available, use standalone mode
|
|
57
|
-
}
|
|
58
|
-
// Use context if available, otherwise fall back to standalone hook
|
|
44
|
+
catch { }
|
|
59
45
|
const standaloneHook = useThemeAnimation({
|
|
60
46
|
animationType,
|
|
61
47
|
duration,
|
|
@@ -64,33 +50,28 @@ export const ThemeSwitcher = ({ themes = ['light', 'dark', 'system'], currentThe
|
|
|
64
50
|
onThemeChange,
|
|
65
51
|
});
|
|
66
52
|
const isControlled = contextTheme !== null;
|
|
67
|
-
const { ref, theme, switchTheme } = isControlled
|
|
68
|
-
{ ref: contextTheme.ref, theme: contextTheme.theme, switchTheme: contextTheme.switchTheme }
|
|
69
|
-
standaloneHook;
|
|
53
|
+
const { ref, theme, switchTheme } = isControlled
|
|
54
|
+
? { ref: contextTheme.ref, theme: contextTheme.theme, switchTheme: contextTheme.switchTheme }
|
|
55
|
+
: standaloneHook;
|
|
70
56
|
const [isMounted, setIsMounted] = useState(false);
|
|
57
|
+
const [hoveredTheme, setHoveredTheme] = useState(null);
|
|
71
58
|
useEffect(() => {
|
|
72
59
|
setIsMounted(true);
|
|
73
60
|
}, []);
|
|
74
61
|
const handleThemeChange = async (newTheme, event) => {
|
|
75
62
|
if (isControlled && contextTheme.switchThemeFromElement && event) {
|
|
76
|
-
// Use controlled mode with animation from clicked element
|
|
77
63
|
await contextTheme.switchThemeFromElement(newTheme, event.currentTarget);
|
|
78
|
-
// Also call the callback if provided
|
|
79
64
|
if (onThemeChange) {
|
|
80
65
|
onThemeChange(newTheme);
|
|
81
66
|
}
|
|
82
67
|
}
|
|
83
68
|
else {
|
|
84
|
-
// Use standalone mode
|
|
85
69
|
await switchTheme(newTheme);
|
|
86
70
|
}
|
|
87
71
|
};
|
|
88
|
-
if (!isMounted) {
|
|
89
|
-
return _jsx("div", { className: "flex h-9 w-fit" });
|
|
90
|
-
}
|
|
91
72
|
const filteredOptions = THEME_OPTIONS.filter(option => themes.includes(option.value));
|
|
92
|
-
return (_jsx(
|
|
73
|
+
return (_jsx("div", { className: clsx('inline-flex h-9 items-center overflow-hidden bg-background shadow-sm border border-border', isMounted ? 'opacity-100' : 'opacity-0', className), style: {
|
|
93
74
|
borderRadius: 'var(--radius)',
|
|
94
75
|
backgroundColor: 'hsl(var(--background))',
|
|
95
|
-
}, role: "radiogroup", children: filteredOptions.map(option => (_jsx(ThemeOption, { icon: option.icon, value: option.value, isActive: theme === option.value, isHovered:
|
|
76
|
+
}, role: "radiogroup", onMouseLeave: () => setHoveredTheme(null), children: filteredOptions.map(option => (_jsx(ThemeOption, { icon: option.icon, value: option.value, isActive: theme === option.value, isHovered: hoveredTheme === option.value, onClick: handleThemeChange, onMouseEnter: () => setHoveredTheme(option.value), onMouseLeave: () => { }, buttonRef: theme === option.value ? ref : undefined }, option.value))) }));
|
|
96
77
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type Theme = 'light' | 'dark' | 'system';
|
|
3
|
+
export interface ViteThemeContextType {
|
|
4
|
+
theme: Theme;
|
|
5
|
+
resolvedTheme: 'light' | 'dark';
|
|
6
|
+
setTheme: (theme: Theme) => void;
|
|
7
|
+
systemTheme: 'light' | 'dark';
|
|
8
|
+
}
|
|
9
|
+
interface ViteThemeProviderProps {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
attribute?: 'class' | 'data-theme';
|
|
12
|
+
defaultTheme?: Theme;
|
|
13
|
+
enableSystem?: boolean;
|
|
14
|
+
disableTransitionOnChange?: boolean;
|
|
15
|
+
storageKey?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const ViteThemeProvider: React.FC<ViteThemeProviderProps>;
|
|
18
|
+
export declare const useViteTheme: () => ViteThemeContextType;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useState, useCallback } from 'react';
|
|
3
|
+
const ViteThemeContext = createContext(undefined);
|
|
4
|
+
export const ViteThemeProvider = ({ children, attribute = 'class', defaultTheme = 'system', enableSystem = true, disableTransitionOnChange = false, storageKey = 'vite-theme', }) => {
|
|
5
|
+
const [mounted, setMounted] = useState(false);
|
|
6
|
+
const [systemTheme, setSystemTheme] = useState('light');
|
|
7
|
+
const [theme, setThemeState] = useState(() => {
|
|
8
|
+
if (typeof window === 'undefined')
|
|
9
|
+
return defaultTheme;
|
|
10
|
+
try {
|
|
11
|
+
const savedTheme = localStorage.getItem(storageKey);
|
|
12
|
+
return savedTheme && ['light', 'dark', 'system'].includes(savedTheme)
|
|
13
|
+
? savedTheme
|
|
14
|
+
: defaultTheme;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return defaultTheme;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!enableSystem)
|
|
22
|
+
return;
|
|
23
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
24
|
+
const updateSystemTheme = () => {
|
|
25
|
+
setSystemTheme(mediaQuery.matches ? 'dark' : 'light');
|
|
26
|
+
};
|
|
27
|
+
updateSystemTheme();
|
|
28
|
+
mediaQuery.addEventListener('change', updateSystemTheme);
|
|
29
|
+
return () => mediaQuery.removeEventListener('change', updateSystemTheme);
|
|
30
|
+
}, [enableSystem]);
|
|
31
|
+
const resolvedTheme = theme === 'system' ? systemTheme : theme;
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!mounted)
|
|
34
|
+
return;
|
|
35
|
+
const root = document.documentElement;
|
|
36
|
+
if (disableTransitionOnChange) {
|
|
37
|
+
const css = document.createElement('style');
|
|
38
|
+
css.textContent = `*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}`;
|
|
39
|
+
document.head.appendChild(css);
|
|
40
|
+
window.getComputedStyle(document.body);
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
document.head.removeChild(css);
|
|
43
|
+
}, 1);
|
|
44
|
+
}
|
|
45
|
+
if (attribute === 'class') {
|
|
46
|
+
root.classList.remove('light', 'dark');
|
|
47
|
+
root.classList.add(resolvedTheme);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
root.setAttribute(attribute, resolvedTheme);
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
localStorage.setItem(storageKey, theme);
|
|
54
|
+
}
|
|
55
|
+
catch { }
|
|
56
|
+
}, [theme, resolvedTheme, mounted, attribute, disableTransitionOnChange, storageKey]);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
setMounted(true);
|
|
59
|
+
}, []);
|
|
60
|
+
const setTheme = useCallback((newTheme) => {
|
|
61
|
+
setThemeState(newTheme);
|
|
62
|
+
}, []);
|
|
63
|
+
if (!mounted) {
|
|
64
|
+
return _jsx(_Fragment, { children: children });
|
|
65
|
+
}
|
|
66
|
+
const value = {
|
|
67
|
+
theme,
|
|
68
|
+
resolvedTheme,
|
|
69
|
+
setTheme,
|
|
70
|
+
systemTheme,
|
|
71
|
+
};
|
|
72
|
+
return _jsx(ViteThemeContext.Provider, { value: value, children: children });
|
|
73
|
+
};
|
|
74
|
+
export const useViteTheme = () => {
|
|
75
|
+
const context = useContext(ViteThemeContext);
|
|
76
|
+
if (context === undefined) {
|
|
77
|
+
throw new Error('useViteTheme must be used within a ViteThemeProvider');
|
|
78
|
+
}
|
|
79
|
+
return context;
|
|
80
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import * as SelectPrimitive from
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
3
3
|
declare function Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
declare function SelectGroup({ ...props }: React.ComponentProps<typeof SelectPrimitive.Group>): import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
declare function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>): import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
declare function SelectTrigger({ className, size, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
|
7
|
-
size?:
|
|
7
|
+
size?: 'sm' | 'default';
|
|
8
8
|
}): import("react/jsx-runtime").JSX.Element;
|
|
9
9
|
declare function SelectContent({ className, children, position, ...props }: React.ComponentProps<typeof SelectPrimitive.Content>): import("react/jsx-runtime").JSX.Element;
|
|
10
10
|
declare function SelectLabel({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Label>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import * as SelectPrimitive from
|
|
3
|
-
import { clsx } from
|
|
2
|
+
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
4
|
function Select({ ...props }) {
|
|
5
5
|
return _jsx(SelectPrimitive.Root, { "data-slot": "select", ...props });
|
|
6
6
|
}
|
|
@@ -13,27 +13,27 @@ function SelectValue({ ...props }) {
|
|
|
13
13
|
const ChevronDownIcon = () => (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "m6 9 6 6 6-6" }) }));
|
|
14
14
|
const ChevronUpIcon = () => (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "m18 15-6-6-6 6" }) }));
|
|
15
15
|
const CheckIcon = () => (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) }));
|
|
16
|
-
function SelectTrigger({ className, size =
|
|
16
|
+
function SelectTrigger({ className, size = 'default', children, ...props }) {
|
|
17
17
|
return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: clsx("border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDownIcon, {}) })] }));
|
|
18
18
|
}
|
|
19
|
-
function SelectContent({ className, children, position =
|
|
20
|
-
return (_jsx(SelectPrimitive.Portal, { children: _jsx(SelectPrimitive.Content, { "data-slot": "select-content", className: clsx(
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
function SelectContent({ className, children, position = 'popper', ...props }) {
|
|
20
|
+
return (_jsx(SelectPrimitive.Portal, { children: _jsx(SelectPrimitive.Content, { "data-slot": "select-content", className: clsx('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md', position === 'popper' &&
|
|
21
|
+
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', className), position: position, ...props, children: _jsx(SelectPrimitive.Viewport, { className: clsx('p-1', position === 'popper' &&
|
|
22
|
+
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'), children: children }) }) }));
|
|
23
23
|
}
|
|
24
24
|
function SelectLabel({ className, ...props }) {
|
|
25
|
-
return (_jsx(SelectPrimitive.Label, { "data-slot": "select-label", className: clsx(
|
|
25
|
+
return (_jsx(SelectPrimitive.Label, { "data-slot": "select-label", className: clsx('py-1.5 pl-8 pr-2 text-sm font-semibold', className), ...props }));
|
|
26
26
|
}
|
|
27
27
|
function SelectItem({ className, children, ...props }) {
|
|
28
28
|
return (_jsxs(SelectPrimitive.Item, { "data-slot": "select-item", className: clsx("focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className), ...props, children: [_jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(CheckIcon, {}) }) }), _jsx(SelectPrimitive.ItemText, { children: children })] }));
|
|
29
29
|
}
|
|
30
30
|
function SelectSeparator({ className, ...props }) {
|
|
31
|
-
return (_jsx(SelectPrimitive.Separator, { "data-slot": "select-separator", className: clsx(
|
|
31
|
+
return (_jsx(SelectPrimitive.Separator, { "data-slot": "select-separator", className: clsx('-mx-1 my-1 h-px bg-muted', className), ...props }));
|
|
32
32
|
}
|
|
33
33
|
function SelectScrollUpButton({ className, ...props }) {
|
|
34
|
-
return (_jsx(SelectPrimitive.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: clsx(
|
|
34
|
+
return (_jsx(SelectPrimitive.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: clsx('flex cursor-default items-center justify-center py-1', className), ...props, children: _jsx(ChevronUpIcon, {}) }));
|
|
35
35
|
}
|
|
36
36
|
function SelectScrollDownButton({ className, ...props }) {
|
|
37
|
-
return (_jsx(SelectPrimitive.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: clsx(
|
|
37
|
+
return (_jsx(SelectPrimitive.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: clsx('flex cursor-default items-center justify-center py-1', className), ...props, children: _jsx(ChevronDownIcon, {}) }));
|
|
38
38
|
}
|
|
39
39
|
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { useThemeAnimation } from './hooks/use-theme-animation';
|
|
|
2
2
|
export { ThemeSwitcher } from './components/ThemeSwitcher';
|
|
3
3
|
export { ThemeSelector } from './components/ThemeSelector';
|
|
4
4
|
export { SpacemanThemeProvider, useSpacemanTheme } from './components/SpacemanThemeProvider';
|
|
5
|
+
export { ViteThemeProvider, useViteTheme } from './components/ViteThemeProvider';
|
|
5
6
|
export type { UseThemeAnimationProps, UseThemeAnimationReturn, ThemeSwitcherProps, ThemeSelectorProps, Theme, ColorTheme, } from './types';
|
|
7
|
+
export type { ViteThemeContextType } from './components/ViteThemeProvider';
|
|
6
8
|
export { ThemeAnimationType } from './types';
|
|
7
9
|
export { injectBaseStyles, getSystemTheme, resolveTheme, supportsViewTransitions, prefersReducedMotion, } from './utils/animations';
|
package/dist/esm/index.js
CHANGED
|
@@ -2,5 +2,6 @@ export { useThemeAnimation } from './hooks/use-theme-animation';
|
|
|
2
2
|
export { ThemeSwitcher } from './components/ThemeSwitcher';
|
|
3
3
|
export { ThemeSelector } from './components/ThemeSelector';
|
|
4
4
|
export { SpacemanThemeProvider, useSpacemanTheme } from './components/SpacemanThemeProvider';
|
|
5
|
+
export { ViteThemeProvider, useViteTheme } from './components/ViteThemeProvider';
|
|
5
6
|
export { ThemeAnimationType } from './types';
|
|
6
7
|
export { injectBaseStyles, getSystemTheme, resolveTheme, supportsViewTransitions, prefersReducedMotion, } from './utils/animations';
|
package/package.json
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@space-man/react-theme-animation",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "React theme switching with smooth view transition animations and multi-theme support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
7
|
-
"reactjs",
|
|
8
|
-
"hooks",
|
|
9
7
|
"theme",
|
|
8
|
+
"dark mode",
|
|
9
|
+
"light mode",
|
|
10
|
+
"theme switching",
|
|
11
|
+
"view transitions",
|
|
12
|
+
"hooks",
|
|
10
13
|
"animation",
|
|
11
14
|
"switch",
|
|
12
15
|
"typescript",
|
|
13
|
-
"nextjs",
|
|
14
16
|
"dark",
|
|
15
17
|
"light",
|
|
16
18
|
"view-transition",
|
|
19
|
+
"view-transition-api",
|
|
20
|
+
"tailwind",
|
|
17
21
|
"tailwindcss",
|
|
18
|
-
"shadcn"
|
|
22
|
+
"shadcn",
|
|
23
|
+
"reactjs",
|
|
24
|
+
"nextjs",
|
|
25
|
+
"vite"
|
|
19
26
|
],
|
|
20
27
|
"publishConfig": {
|
|
21
28
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -50,6 +57,8 @@
|
|
|
50
57
|
"lint": "eslint \"**/*.{js,jsx,ts,tsx}\"",
|
|
51
58
|
"lint:fix": "eslint --fix . --ext .{js,jsx,ts,tsx}",
|
|
52
59
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
60
|
+
"prettier:check": "prettier --check . --config ./.prettierrc",
|
|
61
|
+
"prettier:fix": "prettier --write . --config ./.prettierrc",
|
|
53
62
|
"changeset": "changeset add --type patch",
|
|
54
63
|
"changeset:patch": "changeset add --type patch",
|
|
55
64
|
"changeset:feature": "changeset add --type minor",
|