nextworks 0.2.0-alpha.11 → 0.2.0-alpha.13
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 +283 -282
- package/dist/cli_manifests/blocks_manifest.json +198 -175
- package/dist/kits/blocks/.nextworks/docs/BLOCKS_QUICKSTART.md +101 -100
- package/dist/kits/blocks/.nextworks/docs/BLOCKS_README.md +105 -104
- package/dist/kits/blocks/.nextworks/docs/THEME_GUIDE.md +1 -1
- package/dist/kits/blocks/app/templates/aiworkflow/PresetThemeVars.tsx +58 -0
- package/dist/kits/blocks/app/templates/aiworkflow/README.md +46 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/CTA.tsx +44 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Contact.tsx +105 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/FAQ.tsx +63 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Features.tsx +65 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Footer.tsx +109 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Hero.tsx +636 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Navbar.tsx +90 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Pricing.tsx +86 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/ProcessTimeline.tsx +103 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Testimonials.tsx +56 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/TrustBadges.tsx +59 -0
- package/dist/kits/blocks/app/templates/aiworkflow/page.tsx +43 -0
- package/dist/kits/blocks/app/templates/digitalagency/PresetThemeVars.tsx +80 -80
- package/dist/kits/blocks/app/templates/digitalagency/README.md +42 -42
- package/dist/kits/blocks/app/templates/digitalagency/components/Pricing.tsx +114 -114
- package/dist/kits/blocks/app/templates/digitalagency/components/Process.tsx +59 -59
- package/dist/kits/blocks/app/templates/digitalagency/components/Services.tsx +55 -55
- package/dist/kits/blocks/app/templates/digitalagency/components/Team.tsx +28 -28
- package/dist/kits/blocks/app/templates/digitalagency/components/Testimonials.tsx +65 -65
- package/dist/kits/blocks/app/templates/digitalagency/page.tsx +38 -38
- package/dist/kits/blocks/app/templates/gallery/PresetThemeVars.tsx +84 -84
- package/dist/kits/blocks/app/templates/productlaunch/PresetThemeVars.tsx +75 -75
- package/dist/kits/blocks/app/templates/productlaunch/README.md +62 -62
- package/dist/kits/blocks/app/templates/productlaunch/components/About.tsx +84 -84
- package/dist/kits/blocks/app/templates/productlaunch/components/CTA.tsx +50 -50
- package/dist/kits/blocks/app/templates/productlaunch/components/Contact.tsx +231 -231
- package/dist/kits/blocks/app/templates/productlaunch/components/FAQ.tsx +86 -86
- package/dist/kits/blocks/app/templates/productlaunch/components/Features.tsx +83 -83
- package/dist/kits/blocks/app/templates/productlaunch/components/Footer.tsx +132 -132
- package/dist/kits/blocks/app/templates/productlaunch/components/Hero.tsx +88 -88
- package/dist/kits/blocks/app/templates/productlaunch/components/Navbar.tsx +116 -116
- package/dist/kits/blocks/app/templates/productlaunch/components/Pricing.tsx +106 -106
- package/dist/kits/blocks/app/templates/productlaunch/components/ProcessTimeline.tsx +110 -110
- package/dist/kits/blocks/app/templates/productlaunch/components/ServicesGrid.tsx +68 -68
- package/dist/kits/blocks/app/templates/productlaunch/components/Team.tsx +104 -104
- package/dist/kits/blocks/app/templates/productlaunch/components/Testimonials.tsx +90 -90
- package/dist/kits/blocks/app/templates/productlaunch/components/TrustBadges.tsx +76 -76
- package/dist/kits/blocks/app/templates/productlaunch/page.tsx +43 -43
- package/dist/kits/blocks/app/templates/saasdashboard/PresetThemeVars.tsx +80 -80
- package/dist/kits/blocks/app/templates/saasdashboard/README.md +44 -44
- package/dist/kits/blocks/app/templates/saasdashboard/components/Contact.tsx +129 -129
- package/dist/kits/blocks/app/templates/saasdashboard/components/Dashboard.tsx +293 -293
- package/dist/kits/blocks/app/templates/saasdashboard/components/FAQ.tsx +55 -55
- package/dist/kits/blocks/app/templates/saasdashboard/components/Features.tsx +90 -90
- package/dist/kits/blocks/app/templates/saasdashboard/components/Footer.tsx +77 -77
- package/dist/kits/blocks/app/templates/saasdashboard/components/Hero.tsx +104 -104
- package/dist/kits/blocks/app/templates/saasdashboard/components/Hero_mask.tsx +126 -126
- package/dist/kits/blocks/app/templates/saasdashboard/components/Navbar.tsx +117 -117
- package/dist/kits/blocks/app/templates/saasdashboard/components/Pricing.tsx +90 -90
- package/dist/kits/blocks/app/templates/saasdashboard/components/SmoothScroll.tsx +96 -96
- package/dist/kits/blocks/app/templates/saasdashboard/components/Testimonials.tsx +72 -72
- package/dist/kits/blocks/app/templates/saasdashboard/components/TrustBadges.tsx +53 -53
- package/dist/kits/blocks/app/templates/saasdashboard/page.tsx +39 -39
- package/dist/kits/blocks/components/enhanced-theme-provider.tsx +195 -195
- package/dist/kits/blocks/components/providers/BlocksAppProviders.tsx +27 -27
- package/dist/kits/blocks/components/sections/About.tsx +291 -291
- package/dist/kits/blocks/components/sections/CTA.tsx +257 -257
- package/dist/kits/blocks/components/sections/Contact.tsx +267 -267
- package/dist/kits/blocks/components/sections/FAQ.tsx +214 -214
- package/dist/kits/blocks/components/sections/Features.tsx +268 -268
- package/dist/kits/blocks/components/sections/Footer.tsx +302 -302
- package/dist/kits/blocks/components/sections/HeroMotion.tsx +308 -308
- package/dist/kits/blocks/components/sections/HeroOverlay.tsx +358 -358
- package/dist/kits/blocks/components/sections/HeroProductDemo.tsx +236 -0
- package/dist/kits/blocks/components/sections/HeroSplit.tsx +352 -352
- package/dist/kits/blocks/components/sections/Navbar.tsx +350 -350
- package/dist/kits/blocks/components/sections/PortfolioSimple.tsx +549 -549
- package/dist/kits/blocks/components/sections/Pricing.tsx +264 -264
- package/dist/kits/blocks/components/sections/ProcessTimeline.tsx +325 -325
- package/dist/kits/blocks/components/sections/ServicesGrid.tsx +210 -210
- package/dist/kits/blocks/components/sections/Team.tsx +309 -309
- package/dist/kits/blocks/components/sections/Testimonials.tsx +158 -158
- package/dist/kits/blocks/components/sections/TrustBadges.tsx +162 -162
- package/dist/kits/blocks/components/sections/product-demo/ApprovalInboxPanel.tsx +125 -0
- package/dist/kits/blocks/components/sections/product-demo/DemoStage.tsx +397 -0
- package/dist/kits/blocks/components/sections/product-demo/DemoWindow.tsx +128 -0
- package/dist/kits/blocks/components/sections/product-demo/KnowledgePanel.tsx +127 -0
- package/dist/kits/blocks/components/sections/product-demo/RunConsolePanel.tsx +150 -0
- package/dist/kits/blocks/components/sections/product-demo/WorkflowStudioPanel.tsx +191 -0
- package/dist/kits/blocks/components/sections/product-demo/types.ts +193 -0
- package/dist/kits/blocks/components/theme-provider.tsx +1 -1
- package/dist/kits/blocks/components/ui/alert-dialog.tsx +134 -134
- package/dist/kits/blocks/components/ui/brand-node.tsx +121 -121
- package/dist/kits/blocks/components/ui/button.tsx +122 -122
- package/dist/kits/blocks/components/ui/card.tsx +95 -95
- package/dist/kits/blocks/components/ui/checkbox.tsx +30 -30
- package/dist/kits/blocks/components/ui/cta-button.tsx +125 -125
- package/dist/kits/blocks/components/ui/dropdown-menu.tsx +201 -201
- package/dist/kits/blocks/components/ui/feature-card.tsx +91 -91
- package/dist/kits/blocks/components/ui/input.tsx +27 -27
- package/dist/kits/blocks/components/ui/label.tsx +29 -29
- package/dist/kits/blocks/components/ui/pricing-card.tsx +120 -120
- package/dist/kits/blocks/components/ui/select.tsx +25 -25
- package/dist/kits/blocks/components/ui/skeleton.tsx +13 -13
- package/dist/kits/blocks/components/ui/table.tsx +98 -98
- package/dist/kits/blocks/components/ui/testimonial-card.tsx +108 -108
- package/dist/kits/blocks/components/ui/textarea.tsx +26 -26
- package/dist/kits/blocks/components/ui/theme-selector.tsx +243 -243
- package/dist/kits/blocks/components/ui/theme-toggle.tsx +74 -74
- package/dist/kits/blocks/components/ui/toaster.tsx +7 -7
- package/dist/kits/blocks/lib/themes.ts +400 -400
- package/dist/kits/blocks/lib/utils.ts +6 -6
- package/dist/kits/blocks/package-deps.json +3 -3
- package/package.json +1 -1
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { Navbar } from "./components/Navbar";
|
|
2
|
-
import { Hero } from "./components/Hero";
|
|
3
|
-
import { Features } from "./components/Features";
|
|
4
|
-
import { Testimonials } from "./components/Testimonials";
|
|
5
|
-
import { TrustBadges } from "./components/TrustBadges";
|
|
6
|
-
import { Contact } from "./components/Contact";
|
|
7
|
-
import { Pricing } from "./components/Pricing";
|
|
8
|
-
import { FAQ } from "./components/FAQ";
|
|
9
|
-
import { Footer } from "./components/Footer";
|
|
10
|
-
import { SmoothScroll } from "./components/SmoothScroll";
|
|
11
|
-
|
|
12
|
-
import { PresetThemeVars } from "./PresetThemeVars";
|
|
13
|
-
|
|
14
|
-
export default function SaaSDashboardPage() {
|
|
15
|
-
return (
|
|
16
|
-
<PresetThemeVars>
|
|
17
|
-
<div>
|
|
18
|
-
<SmoothScroll />
|
|
19
|
-
<Navbar />
|
|
20
|
-
<section id="home">
|
|
21
|
-
<Hero />
|
|
22
|
-
</section>
|
|
23
|
-
<Features />
|
|
24
|
-
<Testimonials />
|
|
25
|
-
<TrustBadges />
|
|
26
|
-
<section id="pricing">
|
|
27
|
-
<Pricing />
|
|
28
|
-
</section>
|
|
29
|
-
<section id="contact">
|
|
30
|
-
<Contact />
|
|
31
|
-
</section>
|
|
32
|
-
<section id="faq">
|
|
33
|
-
<FAQ />
|
|
34
|
-
</section>
|
|
35
|
-
<Footer />
|
|
36
|
-
</div>
|
|
37
|
-
</PresetThemeVars>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
1
|
+
import { Navbar } from "./components/Navbar";
|
|
2
|
+
import { Hero } from "./components/Hero";
|
|
3
|
+
import { Features } from "./components/Features";
|
|
4
|
+
import { Testimonials } from "./components/Testimonials";
|
|
5
|
+
import { TrustBadges } from "./components/TrustBadges";
|
|
6
|
+
import { Contact } from "./components/Contact";
|
|
7
|
+
import { Pricing } from "./components/Pricing";
|
|
8
|
+
import { FAQ } from "./components/FAQ";
|
|
9
|
+
import { Footer } from "./components/Footer";
|
|
10
|
+
import { SmoothScroll } from "./components/SmoothScroll";
|
|
11
|
+
|
|
12
|
+
import { PresetThemeVars } from "./PresetThemeVars";
|
|
13
|
+
|
|
14
|
+
export default function SaaSDashboardPage() {
|
|
15
|
+
return (
|
|
16
|
+
<PresetThemeVars>
|
|
17
|
+
<div>
|
|
18
|
+
<SmoothScroll />
|
|
19
|
+
<Navbar />
|
|
20
|
+
<section id="home">
|
|
21
|
+
<Hero />
|
|
22
|
+
</section>
|
|
23
|
+
<Features />
|
|
24
|
+
<Testimonials />
|
|
25
|
+
<TrustBadges />
|
|
26
|
+
<section id="pricing">
|
|
27
|
+
<Pricing />
|
|
28
|
+
</section>
|
|
29
|
+
<section id="contact">
|
|
30
|
+
<Contact />
|
|
31
|
+
</section>
|
|
32
|
+
<section id="faq">
|
|
33
|
+
<FAQ />
|
|
34
|
+
</section>
|
|
35
|
+
<Footer />
|
|
36
|
+
</div>
|
|
37
|
+
</PresetThemeVars>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -1,195 +1,195 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
|
5
|
-
import type { ThemeProviderProps as NextThemesProviderProps } from "next-themes";
|
|
6
|
-
import {
|
|
7
|
-
ThemeVariant,
|
|
8
|
-
themes,
|
|
9
|
-
darkThemes,
|
|
10
|
-
type ThemeConfig,
|
|
11
|
-
} from "@/lib/themes";
|
|
12
|
-
|
|
13
|
-
type ColorTokens = ThemeConfig["colors"];
|
|
14
|
-
const CUSTOM_STORAGE_KEY = "nxw-theme-custom";
|
|
15
|
-
|
|
16
|
-
interface EnhancedThemeProviderProps {
|
|
17
|
-
children: React.ReactNode;
|
|
18
|
-
attribute?: NextThemesProviderProps["attribute"];
|
|
19
|
-
defaultTheme?: NextThemesProviderProps["defaultTheme"];
|
|
20
|
-
enableSystem?: NextThemesProviderProps["enableSystem"];
|
|
21
|
-
disableTransitionOnChange?: NextThemesProviderProps["disableTransitionOnChange"];
|
|
22
|
-
defaultThemeVariant?: ThemeVariant;
|
|
23
|
-
defaultCustomTokens?: Partial<ColorTokens> | null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface ThemeContextType {
|
|
27
|
-
themeVariant: ThemeVariant;
|
|
28
|
-
setThemeVariant: (variant: ThemeVariant) => void;
|
|
29
|
-
|
|
30
|
-
customTheme: Partial<ColorTokens> | null;
|
|
31
|
-
setCustomTheme: (tokens: Partial<ColorTokens> | null) => void;
|
|
32
|
-
setCustomBrandColors: (tokens: Partial<ColorTokens>) => void;
|
|
33
|
-
|
|
34
|
-
applyTheme: (variant: ThemeVariant, isDark?: boolean) => void;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const ThemeContext = React.createContext<ThemeContextType | undefined>(
|
|
38
|
-
undefined,
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
export function useThemeVariant() {
|
|
42
|
-
const context = React.useContext(ThemeContext);
|
|
43
|
-
if (context === undefined) {
|
|
44
|
-
throw new Error(
|
|
45
|
-
"useThemeVariant must be used within an EnhancedThemeProvider",
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
return context;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function EnhancedThemeProvider({
|
|
52
|
-
children,
|
|
53
|
-
attribute = "class",
|
|
54
|
-
defaultTheme = "system",
|
|
55
|
-
enableSystem = true,
|
|
56
|
-
disableTransitionOnChange = false,
|
|
57
|
-
defaultThemeVariant = "monochrome",
|
|
58
|
-
defaultCustomTokens = null,
|
|
59
|
-
}: EnhancedThemeProviderProps) {
|
|
60
|
-
const [themeVariant, setThemeVariant] =
|
|
61
|
-
React.useState<ThemeVariant>(defaultThemeVariant);
|
|
62
|
-
const [customTheme, setCustomThemeState] =
|
|
63
|
-
React.useState<Partial<ColorTokens> | null>(defaultCustomTokens);
|
|
64
|
-
|
|
65
|
-
const writeCustomCookies = React.useCallback(
|
|
66
|
-
(tokens: Partial<ColorTokens> | null) => {
|
|
67
|
-
if (tokens) {
|
|
68
|
-
document.cookie = `theme-variant=custom; Path=/; Max-Age=31536000; SameSite=Lax`;
|
|
69
|
-
document.cookie = `theme-custom=${encodeURIComponent(JSON.stringify(tokens))}; Path=/; Max-Age=31536000; SameSite=Lax`;
|
|
70
|
-
} else {
|
|
71
|
-
document.cookie = `theme-custom=; Path=/; Max-Age=0; SameSite=Lax`;
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
[],
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
const setCustomTheme = React.useCallback(
|
|
78
|
-
(tokens: Partial<ColorTokens> | null) => {
|
|
79
|
-
setCustomThemeState(tokens);
|
|
80
|
-
try {
|
|
81
|
-
if (tokens) {
|
|
82
|
-
localStorage.setItem(CUSTOM_STORAGE_KEY, JSON.stringify(tokens));
|
|
83
|
-
} else {
|
|
84
|
-
localStorage.removeItem(CUSTOM_STORAGE_KEY);
|
|
85
|
-
}
|
|
86
|
-
} catch {
|
|
87
|
-
// ignore
|
|
88
|
-
}
|
|
89
|
-
writeCustomCookies(tokens ?? null);
|
|
90
|
-
},
|
|
91
|
-
[writeCustomCookies],
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const setCustomBrandColors = React.useCallback(
|
|
95
|
-
(tokens: Partial<ColorTokens>) => {
|
|
96
|
-
setThemeVariant("custom");
|
|
97
|
-
setCustomTheme(tokens);
|
|
98
|
-
},
|
|
99
|
-
[setCustomTheme],
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
const applyTheme = React.useCallback(
|
|
103
|
-
(variant: ThemeVariant, isDark = false) => {
|
|
104
|
-
const base = isDark ? darkThemes[variant].colors : themes[variant].colors;
|
|
105
|
-
const merged =
|
|
106
|
-
variant === "custom" && customTheme
|
|
107
|
-
? { ...base, ...customTheme }
|
|
108
|
-
: base;
|
|
109
|
-
|
|
110
|
-
// Apply CSS custom properties to the document root
|
|
111
|
-
const root = document.documentElement;
|
|
112
|
-
|
|
113
|
-
// Reflect the current variant for CSS selectors
|
|
114
|
-
root.setAttribute("data-theme-variant", variant);
|
|
115
|
-
|
|
116
|
-
Object.entries(merged).forEach(([key, value]) => {
|
|
117
|
-
const cssVar = `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
|
|
118
|
-
root.style.setProperty(cssVar, value as string);
|
|
119
|
-
});
|
|
120
|
-
},
|
|
121
|
-
[customTheme],
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// Apply theme when variant or custom tokens change
|
|
125
|
-
React.useEffect(() => {
|
|
126
|
-
const isDark = document.documentElement.classList.contains("dark");
|
|
127
|
-
applyTheme(themeVariant, isDark);
|
|
128
|
-
}, [themeVariant, customTheme, applyTheme]);
|
|
129
|
-
|
|
130
|
-
// Load custom tokens from localStorage on mount (overrides defaultCustomTokens if present)
|
|
131
|
-
React.useEffect(() => {
|
|
132
|
-
try {
|
|
133
|
-
const raw = localStorage.getItem(CUSTOM_STORAGE_KEY);
|
|
134
|
-
if (raw) {
|
|
135
|
-
const parsed = JSON.parse(raw) as Partial<ColorTokens>;
|
|
136
|
-
setCustomThemeState(parsed);
|
|
137
|
-
}
|
|
138
|
-
} catch {
|
|
139
|
-
// ignore
|
|
140
|
-
}
|
|
141
|
-
}, []);
|
|
142
|
-
|
|
143
|
-
// Listen for theme changes from next-themes
|
|
144
|
-
React.useEffect(() => {
|
|
145
|
-
const observer = new MutationObserver((mutations) => {
|
|
146
|
-
mutations.forEach((mutation) => {
|
|
147
|
-
if (
|
|
148
|
-
mutation.type === "attributes" &&
|
|
149
|
-
mutation.attributeName === "class"
|
|
150
|
-
) {
|
|
151
|
-
const isDark = document.documentElement.classList.contains("dark");
|
|
152
|
-
applyTheme(themeVariant, isDark);
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
observer.observe(document.documentElement, {
|
|
158
|
-
attributes: true,
|
|
159
|
-
attributeFilter: ["class"],
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
return () => observer.disconnect();
|
|
163
|
-
}, [themeVariant, applyTheme]);
|
|
164
|
-
|
|
165
|
-
const contextValue = React.useMemo(
|
|
166
|
-
() => ({
|
|
167
|
-
themeVariant,
|
|
168
|
-
setThemeVariant,
|
|
169
|
-
customTheme,
|
|
170
|
-
setCustomTheme,
|
|
171
|
-
setCustomBrandColors,
|
|
172
|
-
applyTheme,
|
|
173
|
-
}),
|
|
174
|
-
[
|
|
175
|
-
themeVariant,
|
|
176
|
-
customTheme,
|
|
177
|
-
setCustomTheme,
|
|
178
|
-
setCustomBrandColors,
|
|
179
|
-
applyTheme,
|
|
180
|
-
],
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
return (
|
|
184
|
-
<NextThemesProvider
|
|
185
|
-
attribute={attribute}
|
|
186
|
-
defaultTheme={defaultTheme}
|
|
187
|
-
enableSystem={enableSystem}
|
|
188
|
-
disableTransitionOnChange={disableTransitionOnChange}
|
|
189
|
-
>
|
|
190
|
-
<ThemeContext.Provider value={contextValue}>
|
|
191
|
-
{children}
|
|
192
|
-
</ThemeContext.Provider>
|
|
193
|
-
</NextThemesProvider>
|
|
194
|
-
);
|
|
195
|
-
}
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
|
5
|
+
import type { ThemeProviderProps as NextThemesProviderProps } from "next-themes";
|
|
6
|
+
import {
|
|
7
|
+
ThemeVariant,
|
|
8
|
+
themes,
|
|
9
|
+
darkThemes,
|
|
10
|
+
type ThemeConfig,
|
|
11
|
+
} from "@/lib/themes";
|
|
12
|
+
|
|
13
|
+
type ColorTokens = ThemeConfig["colors"];
|
|
14
|
+
const CUSTOM_STORAGE_KEY = "nxw-theme-custom";
|
|
15
|
+
|
|
16
|
+
interface EnhancedThemeProviderProps {
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
attribute?: NextThemesProviderProps["attribute"];
|
|
19
|
+
defaultTheme?: NextThemesProviderProps["defaultTheme"];
|
|
20
|
+
enableSystem?: NextThemesProviderProps["enableSystem"];
|
|
21
|
+
disableTransitionOnChange?: NextThemesProviderProps["disableTransitionOnChange"];
|
|
22
|
+
defaultThemeVariant?: ThemeVariant;
|
|
23
|
+
defaultCustomTokens?: Partial<ColorTokens> | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ThemeContextType {
|
|
27
|
+
themeVariant: ThemeVariant;
|
|
28
|
+
setThemeVariant: (variant: ThemeVariant) => void;
|
|
29
|
+
|
|
30
|
+
customTheme: Partial<ColorTokens> | null;
|
|
31
|
+
setCustomTheme: (tokens: Partial<ColorTokens> | null) => void;
|
|
32
|
+
setCustomBrandColors: (tokens: Partial<ColorTokens>) => void;
|
|
33
|
+
|
|
34
|
+
applyTheme: (variant: ThemeVariant, isDark?: boolean) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const ThemeContext = React.createContext<ThemeContextType | undefined>(
|
|
38
|
+
undefined,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export function useThemeVariant() {
|
|
42
|
+
const context = React.useContext(ThemeContext);
|
|
43
|
+
if (context === undefined) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
"useThemeVariant must be used within an EnhancedThemeProvider",
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return context;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function EnhancedThemeProvider({
|
|
52
|
+
children,
|
|
53
|
+
attribute = "class",
|
|
54
|
+
defaultTheme = "system",
|
|
55
|
+
enableSystem = true,
|
|
56
|
+
disableTransitionOnChange = false,
|
|
57
|
+
defaultThemeVariant = "monochrome",
|
|
58
|
+
defaultCustomTokens = null,
|
|
59
|
+
}: EnhancedThemeProviderProps) {
|
|
60
|
+
const [themeVariant, setThemeVariant] =
|
|
61
|
+
React.useState<ThemeVariant>(defaultThemeVariant);
|
|
62
|
+
const [customTheme, setCustomThemeState] =
|
|
63
|
+
React.useState<Partial<ColorTokens> | null>(defaultCustomTokens);
|
|
64
|
+
|
|
65
|
+
const writeCustomCookies = React.useCallback(
|
|
66
|
+
(tokens: Partial<ColorTokens> | null) => {
|
|
67
|
+
if (tokens) {
|
|
68
|
+
document.cookie = `theme-variant=custom; Path=/; Max-Age=31536000; SameSite=Lax`;
|
|
69
|
+
document.cookie = `theme-custom=${encodeURIComponent(JSON.stringify(tokens))}; Path=/; Max-Age=31536000; SameSite=Lax`;
|
|
70
|
+
} else {
|
|
71
|
+
document.cookie = `theme-custom=; Path=/; Max-Age=0; SameSite=Lax`;
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
[],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const setCustomTheme = React.useCallback(
|
|
78
|
+
(tokens: Partial<ColorTokens> | null) => {
|
|
79
|
+
setCustomThemeState(tokens);
|
|
80
|
+
try {
|
|
81
|
+
if (tokens) {
|
|
82
|
+
localStorage.setItem(CUSTOM_STORAGE_KEY, JSON.stringify(tokens));
|
|
83
|
+
} else {
|
|
84
|
+
localStorage.removeItem(CUSTOM_STORAGE_KEY);
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
// ignore
|
|
88
|
+
}
|
|
89
|
+
writeCustomCookies(tokens ?? null);
|
|
90
|
+
},
|
|
91
|
+
[writeCustomCookies],
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const setCustomBrandColors = React.useCallback(
|
|
95
|
+
(tokens: Partial<ColorTokens>) => {
|
|
96
|
+
setThemeVariant("custom");
|
|
97
|
+
setCustomTheme(tokens);
|
|
98
|
+
},
|
|
99
|
+
[setCustomTheme],
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const applyTheme = React.useCallback(
|
|
103
|
+
(variant: ThemeVariant, isDark = false) => {
|
|
104
|
+
const base = isDark ? darkThemes[variant].colors : themes[variant].colors;
|
|
105
|
+
const merged =
|
|
106
|
+
variant === "custom" && customTheme
|
|
107
|
+
? { ...base, ...customTheme }
|
|
108
|
+
: base;
|
|
109
|
+
|
|
110
|
+
// Apply CSS custom properties to the document root
|
|
111
|
+
const root = document.documentElement;
|
|
112
|
+
|
|
113
|
+
// Reflect the current variant for CSS selectors
|
|
114
|
+
root.setAttribute("data-theme-variant", variant);
|
|
115
|
+
|
|
116
|
+
Object.entries(merged).forEach(([key, value]) => {
|
|
117
|
+
const cssVar = `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
|
|
118
|
+
root.style.setProperty(cssVar, value as string);
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
[customTheme],
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Apply theme when variant or custom tokens change
|
|
125
|
+
React.useEffect(() => {
|
|
126
|
+
const isDark = document.documentElement.classList.contains("dark");
|
|
127
|
+
applyTheme(themeVariant, isDark);
|
|
128
|
+
}, [themeVariant, customTheme, applyTheme]);
|
|
129
|
+
|
|
130
|
+
// Load custom tokens from localStorage on mount (overrides defaultCustomTokens if present)
|
|
131
|
+
React.useEffect(() => {
|
|
132
|
+
try {
|
|
133
|
+
const raw = localStorage.getItem(CUSTOM_STORAGE_KEY);
|
|
134
|
+
if (raw) {
|
|
135
|
+
const parsed = JSON.parse(raw) as Partial<ColorTokens>;
|
|
136
|
+
setCustomThemeState(parsed);
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
// ignore
|
|
140
|
+
}
|
|
141
|
+
}, []);
|
|
142
|
+
|
|
143
|
+
// Listen for theme changes from next-themes
|
|
144
|
+
React.useEffect(() => {
|
|
145
|
+
const observer = new MutationObserver((mutations) => {
|
|
146
|
+
mutations.forEach((mutation) => {
|
|
147
|
+
if (
|
|
148
|
+
mutation.type === "attributes" &&
|
|
149
|
+
mutation.attributeName === "class"
|
|
150
|
+
) {
|
|
151
|
+
const isDark = document.documentElement.classList.contains("dark");
|
|
152
|
+
applyTheme(themeVariant, isDark);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
observer.observe(document.documentElement, {
|
|
158
|
+
attributes: true,
|
|
159
|
+
attributeFilter: ["class"],
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return () => observer.disconnect();
|
|
163
|
+
}, [themeVariant, applyTheme]);
|
|
164
|
+
|
|
165
|
+
const contextValue = React.useMemo(
|
|
166
|
+
() => ({
|
|
167
|
+
themeVariant,
|
|
168
|
+
setThemeVariant,
|
|
169
|
+
customTheme,
|
|
170
|
+
setCustomTheme,
|
|
171
|
+
setCustomBrandColors,
|
|
172
|
+
applyTheme,
|
|
173
|
+
}),
|
|
174
|
+
[
|
|
175
|
+
themeVariant,
|
|
176
|
+
customTheme,
|
|
177
|
+
setCustomTheme,
|
|
178
|
+
setCustomBrandColors,
|
|
179
|
+
applyTheme,
|
|
180
|
+
],
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<NextThemesProvider
|
|
185
|
+
attribute={attribute}
|
|
186
|
+
defaultTheme={defaultTheme}
|
|
187
|
+
enableSystem={enableSystem}
|
|
188
|
+
disableTransitionOnChange={disableTransitionOnChange}
|
|
189
|
+
>
|
|
190
|
+
<ThemeContext.Provider value={contextValue}>
|
|
191
|
+
{children}
|
|
192
|
+
</ThemeContext.Provider>
|
|
193
|
+
</NextThemesProvider>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { EnhancedThemeProvider } from "../enhanced-theme-provider";
|
|
5
|
-
import type { ThemeVariant } from "../../lib/themes";
|
|
6
|
-
|
|
7
|
-
type BlocksAppProvidersProps = {
|
|
8
|
-
children: React.ReactNode;
|
|
9
|
-
defaultThemeVariant?: ThemeVariant;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Client-safe provider wrapper for Nextworks blocks (kit-local).
|
|
14
|
-
*
|
|
15
|
-
* This intentionally avoids importing providers/hooks from @nextworks/blocks-core
|
|
16
|
-
* to prevent duplicate React context instances in Turbopack dev.
|
|
17
|
-
*/
|
|
18
|
-
export function BlocksAppProviders({
|
|
19
|
-
children,
|
|
20
|
-
defaultThemeVariant = "monochrome",
|
|
21
|
-
}: BlocksAppProvidersProps) {
|
|
22
|
-
return (
|
|
23
|
-
<EnhancedThemeProvider defaultThemeVariant={defaultThemeVariant}>
|
|
24
|
-
{children}
|
|
25
|
-
</EnhancedThemeProvider>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { EnhancedThemeProvider } from "../enhanced-theme-provider";
|
|
5
|
+
import type { ThemeVariant } from "../../lib/themes";
|
|
6
|
+
|
|
7
|
+
type BlocksAppProvidersProps = {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
defaultThemeVariant?: ThemeVariant;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Client-safe provider wrapper for Nextworks blocks (kit-local).
|
|
14
|
+
*
|
|
15
|
+
* This intentionally avoids importing providers/hooks from @nextworks/blocks-core
|
|
16
|
+
* to prevent duplicate React context instances in Turbopack dev.
|
|
17
|
+
*/
|
|
18
|
+
export function BlocksAppProviders({
|
|
19
|
+
children,
|
|
20
|
+
defaultThemeVariant = "monochrome",
|
|
21
|
+
}: BlocksAppProvidersProps) {
|
|
22
|
+
return (
|
|
23
|
+
<EnhancedThemeProvider defaultThemeVariant={defaultThemeVariant}>
|
|
24
|
+
{children}
|
|
25
|
+
</EnhancedThemeProvider>
|
|
26
|
+
);
|
|
27
|
+
}
|