@fakhrirafiki/theme-engine 0.2.1 → 0.2.5
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 +17 -1
- package/dist/index.d.mts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +112 -47
- package/dist/index.mjs +112 -47
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -390,6 +390,22 @@ function CustomControls() {
|
|
|
390
390
|
</button>
|
|
391
391
|
)
|
|
392
392
|
}
|
|
393
|
+
|
|
394
|
+
// Or apply a preset by ID (from built-in or custom presets)
|
|
395
|
+
function QuickPresetSwitcher() {
|
|
396
|
+
const { setThemePresetById } = useTheme()
|
|
397
|
+
|
|
398
|
+
return (
|
|
399
|
+
<div>
|
|
400
|
+
<button onClick={() => setThemePresetById('modern-minimal')}>
|
|
401
|
+
Modern Minimal
|
|
402
|
+
</button>
|
|
403
|
+
<button onClick={() => setThemePresetById('tiket-ngobrol')}>
|
|
404
|
+
Ngobrol Blue
|
|
405
|
+
</button>
|
|
406
|
+
</div>
|
|
407
|
+
)
|
|
408
|
+
}
|
|
393
409
|
```
|
|
394
410
|
|
|
395
411
|
## 🔄 Migration Guide
|
|
@@ -563,4 +579,4 @@ This architecture ensures seamless coordination between appearance modes and col
|
|
|
563
579
|
|
|
564
580
|
## 📄 License
|
|
565
581
|
|
|
566
|
-
MIT License - see the monorepo root for details.
|
|
582
|
+
MIT License - see the monorepo root for details.
|
package/dist/index.d.mts
CHANGED
|
@@ -318,7 +318,7 @@ interface UnifiedThemeContextValue {
|
|
|
318
318
|
/** Current appearance mode setting */
|
|
319
319
|
mode: Mode;
|
|
320
320
|
/** Resolved appearance mode (never 'system') */
|
|
321
|
-
resolvedMode:
|
|
321
|
+
resolvedMode: "light" | "dark";
|
|
322
322
|
/** Change the appearance mode */
|
|
323
323
|
setMode: (mode: Mode) => void;
|
|
324
324
|
/** Toggle between light and dark modes with optional animation */
|
|
@@ -330,14 +330,18 @@ interface UnifiedThemeContextValue {
|
|
|
330
330
|
/** Human-readable preset name */
|
|
331
331
|
presetName: string;
|
|
332
332
|
/** Color values for light and dark modes */
|
|
333
|
-
colors: ThemePreset[
|
|
333
|
+
colors: ThemePreset["colors"];
|
|
334
334
|
/** Timestamp when preset was applied */
|
|
335
335
|
appliedAt: number;
|
|
336
336
|
} | null;
|
|
337
337
|
/** Apply a new color preset */
|
|
338
338
|
applyPreset: (preset: ThemePreset) => void;
|
|
339
|
+
/** Apply a preset by its ID from the available preset collection */
|
|
340
|
+
setThemePresetById: (presetId: string) => void;
|
|
339
341
|
/** Clear the current preset and revert to default colors */
|
|
340
342
|
clearPreset: () => void;
|
|
343
|
+
/** Whether the currently active preset matches the defaultPreset prop */
|
|
344
|
+
isUsingDefaultPreset: boolean;
|
|
341
345
|
/** Available presets (merged built-in + custom) */
|
|
342
346
|
availablePresets: Record<string, TweakCNThemePreset>;
|
|
343
347
|
/** Built-in presets only */
|
package/dist/index.d.ts
CHANGED
|
@@ -318,7 +318,7 @@ interface UnifiedThemeContextValue {
|
|
|
318
318
|
/** Current appearance mode setting */
|
|
319
319
|
mode: Mode;
|
|
320
320
|
/** Resolved appearance mode (never 'system') */
|
|
321
|
-
resolvedMode:
|
|
321
|
+
resolvedMode: "light" | "dark";
|
|
322
322
|
/** Change the appearance mode */
|
|
323
323
|
setMode: (mode: Mode) => void;
|
|
324
324
|
/** Toggle between light and dark modes with optional animation */
|
|
@@ -330,14 +330,18 @@ interface UnifiedThemeContextValue {
|
|
|
330
330
|
/** Human-readable preset name */
|
|
331
331
|
presetName: string;
|
|
332
332
|
/** Color values for light and dark modes */
|
|
333
|
-
colors: ThemePreset[
|
|
333
|
+
colors: ThemePreset["colors"];
|
|
334
334
|
/** Timestamp when preset was applied */
|
|
335
335
|
appliedAt: number;
|
|
336
336
|
} | null;
|
|
337
337
|
/** Apply a new color preset */
|
|
338
338
|
applyPreset: (preset: ThemePreset) => void;
|
|
339
|
+
/** Apply a preset by its ID from the available preset collection */
|
|
340
|
+
setThemePresetById: (presetId: string) => void;
|
|
339
341
|
/** Clear the current preset and revert to default colors */
|
|
340
342
|
clearPreset: () => void;
|
|
343
|
+
/** Whether the currently active preset matches the defaultPreset prop */
|
|
344
|
+
isUsingDefaultPreset: boolean;
|
|
341
345
|
/** Available presets (merged built-in + custom) */
|
|
342
346
|
availablePresets: Record<string, TweakCNThemePreset>;
|
|
343
347
|
/** Built-in presets only */
|
package/dist/index.js
CHANGED
|
@@ -3748,9 +3748,12 @@ function ThemeProvider({
|
|
|
3748
3748
|
}, [includeBuiltInPresets, customPresets]);
|
|
3749
3749
|
const builtInPresets = (0, import_react.useMemo)(() => includeBuiltInPresets ? tweakcnPresets : {}, [includeBuiltInPresets]);
|
|
3750
3750
|
const normalizedCustomPresets = (0, import_react.useMemo)(() => customPresets || {}, [customPresets]);
|
|
3751
|
-
const getAvailablePresetById = (0, import_react.useCallback)(
|
|
3752
|
-
|
|
3753
|
-
|
|
3751
|
+
const getAvailablePresetById = (0, import_react.useCallback)(
|
|
3752
|
+
(id) => {
|
|
3753
|
+
return availablePresets[id] || null;
|
|
3754
|
+
},
|
|
3755
|
+
[availablePresets]
|
|
3756
|
+
);
|
|
3754
3757
|
const [currentPreset, setCurrentPreset] = (0, import_react.useState)(null);
|
|
3755
3758
|
const [isReady, setIsReady] = (0, import_react.useState)(false);
|
|
3756
3759
|
(0, import_react.useEffect)(() => {
|
|
@@ -3760,10 +3763,9 @@ function ThemeProvider({
|
|
|
3760
3763
|
}
|
|
3761
3764
|
try {
|
|
3762
3765
|
const stored = localStorage.getItem(presetStorageKey);
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
} else if (defaultPreset) {
|
|
3766
|
+
const isDevelopment = typeof window !== "undefined" && window.location?.hostname === "localhost";
|
|
3767
|
+
const applyDefaultPreset = () => {
|
|
3768
|
+
if (!defaultPreset) return;
|
|
3767
3769
|
const preset = getAvailablePresetById(defaultPreset);
|
|
3768
3770
|
if (preset) {
|
|
3769
3771
|
const presetData = {
|
|
@@ -3779,13 +3781,42 @@ function ThemeProvider({
|
|
|
3779
3781
|
} else {
|
|
3780
3782
|
console.warn("\u{1F3A8} UnifiedTheme: Default preset not found:", defaultPreset);
|
|
3781
3783
|
}
|
|
3784
|
+
};
|
|
3785
|
+
if (stored) {
|
|
3786
|
+
try {
|
|
3787
|
+
const parsed = JSON.parse(stored);
|
|
3788
|
+
const isValidObject = parsed && typeof parsed === "object";
|
|
3789
|
+
const hasColors = isValidObject && "colors" in parsed && parsed.colors && typeof parsed.colors === "object" && parsed.colors.light && parsed.colors.dark;
|
|
3790
|
+
if (hasColors) {
|
|
3791
|
+
setCurrentPreset(parsed);
|
|
3792
|
+
if (isDevelopment) {
|
|
3793
|
+
}
|
|
3794
|
+
} else {
|
|
3795
|
+
if (isDevelopment) {
|
|
3796
|
+
console.warn("\u{1F3A8} UnifiedTheme: Invalid persisted preset shape. Clearing and falling back.");
|
|
3797
|
+
}
|
|
3798
|
+
localStorage.removeItem(presetStorageKey);
|
|
3799
|
+
applyDefaultPreset();
|
|
3800
|
+
}
|
|
3801
|
+
} catch (error) {
|
|
3802
|
+
if (isDevelopment) {
|
|
3803
|
+
console.warn("\u{1F3A8} UnifiedTheme: Failed to parse persisted preset. Clearing key.", error);
|
|
3804
|
+
}
|
|
3805
|
+
localStorage.removeItem(presetStorageKey);
|
|
3806
|
+
applyDefaultPreset();
|
|
3807
|
+
}
|
|
3808
|
+
} else {
|
|
3809
|
+
applyDefaultPreset();
|
|
3782
3810
|
}
|
|
3783
3811
|
} catch (error) {
|
|
3784
|
-
|
|
3812
|
+
const isDevelopment = typeof window !== "undefined" && window.location?.hostname === "localhost";
|
|
3813
|
+
if (isDevelopment) {
|
|
3814
|
+
console.warn("\u{1F3A8} UnifiedTheme: Failed to load preset from storage:", error);
|
|
3815
|
+
}
|
|
3785
3816
|
} finally {
|
|
3786
3817
|
setIsReady(true);
|
|
3787
3818
|
}
|
|
3788
|
-
}, [presetStorageKey, enablePresets, defaultPreset]);
|
|
3819
|
+
}, [presetStorageKey, enablePresets, defaultPreset, getAvailablePresetById]);
|
|
3789
3820
|
(0, import_react.useEffect)(() => {
|
|
3790
3821
|
if (mode === "system") {
|
|
3791
3822
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
@@ -3806,11 +3837,12 @@ function ThemeProvider({
|
|
|
3806
3837
|
root.style.colorScheme = resolvedMode;
|
|
3807
3838
|
}, [resolvedMode]);
|
|
3808
3839
|
const applyPresetColors = (0, import_react.useCallback)((preset, mode2) => {
|
|
3840
|
+
if (!preset) return;
|
|
3809
3841
|
const root = document.documentElement;
|
|
3810
3842
|
const colors = preset[mode2];
|
|
3811
3843
|
if (!colors) return;
|
|
3812
3844
|
const defaultValues = {
|
|
3813
|
-
|
|
3845
|
+
spacing: "0.25rem",
|
|
3814
3846
|
"letter-spacing": "normal"
|
|
3815
3847
|
};
|
|
3816
3848
|
const allCategories = [
|
|
@@ -3849,49 +3881,79 @@ function ThemeProvider({
|
|
|
3849
3881
|
}
|
|
3850
3882
|
});
|
|
3851
3883
|
}, []);
|
|
3852
|
-
const handleModeChange = (0, import_react.useCallback)(
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3884
|
+
const handleModeChange = (0, import_react.useCallback)(
|
|
3885
|
+
(newMode) => {
|
|
3886
|
+
setMode(newMode);
|
|
3887
|
+
setStoredMode(newMode, modeStorageKey);
|
|
3888
|
+
},
|
|
3889
|
+
[modeStorageKey]
|
|
3890
|
+
);
|
|
3891
|
+
const handleModeToggle = (0, import_react.useCallback)(
|
|
3892
|
+
(coordinates) => {
|
|
3893
|
+
const newMode = resolvedMode === "light" ? "dark" : "light";
|
|
3894
|
+
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
3895
|
+
if (enableTransitions && !prefersReducedMotion && typeof document !== "undefined" && "startViewTransition" in document) {
|
|
3896
|
+
const root = document.documentElement;
|
|
3897
|
+
if (coordinates) {
|
|
3898
|
+
root.style.setProperty("--x", `${coordinates.x}px`);
|
|
3899
|
+
root.style.setProperty("--y", `${coordinates.y}px`);
|
|
3900
|
+
}
|
|
3901
|
+
document.startViewTransition(() => {
|
|
3902
|
+
handleModeChange(newMode);
|
|
3903
|
+
});
|
|
3904
|
+
} else {
|
|
3866
3905
|
handleModeChange(newMode);
|
|
3867
|
-
}
|
|
3868
|
-
}
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
}, [resolvedMode, enableTransitions, handleModeChange]);
|
|
3906
|
+
}
|
|
3907
|
+
},
|
|
3908
|
+
[resolvedMode, enableTransitions, handleModeChange]
|
|
3909
|
+
);
|
|
3872
3910
|
(0, import_react.useEffect)(() => {
|
|
3873
3911
|
if (!currentPreset || typeof window === "undefined") return;
|
|
3874
3912
|
applyPresetColors(currentPreset.colors, resolvedMode);
|
|
3875
3913
|
}, [currentPreset, resolvedMode, applyPresetColors]);
|
|
3876
|
-
const applyPreset = (0, import_react.useCallback)(
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3914
|
+
const applyPreset = (0, import_react.useCallback)(
|
|
3915
|
+
(preset) => {
|
|
3916
|
+
const presetData = {
|
|
3917
|
+
presetId: preset.id,
|
|
3918
|
+
presetName: preset.name,
|
|
3919
|
+
colors: preset.colors,
|
|
3920
|
+
appliedAt: Date.now()
|
|
3921
|
+
};
|
|
3922
|
+
setCurrentPreset(presetData);
|
|
3923
|
+
if (enablePresets && typeof window !== "undefined") {
|
|
3924
|
+
try {
|
|
3925
|
+
localStorage.setItem(presetStorageKey, JSON.stringify(presetData));
|
|
3926
|
+
} catch (error) {
|
|
3927
|
+
console.error("\u{1F3A8} UnifiedTheme: Failed to save preset:", error);
|
|
3928
|
+
}
|
|
3889
3929
|
}
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
}
|
|
3894
|
-
|
|
3930
|
+
if (typeof window !== "undefined") {
|
|
3931
|
+
applyPresetColors(preset.colors, resolvedMode);
|
|
3932
|
+
}
|
|
3933
|
+
},
|
|
3934
|
+
[presetStorageKey, enablePresets, applyPresetColors, resolvedMode]
|
|
3935
|
+
);
|
|
3936
|
+
const setThemePresetById = (0, import_react.useCallback)(
|
|
3937
|
+
(presetId) => {
|
|
3938
|
+
const preset = getAvailablePresetById(presetId);
|
|
3939
|
+
if (!preset) {
|
|
3940
|
+
if (typeof window !== "undefined") {
|
|
3941
|
+
console.warn("\u{1F3A8} UnifiedTheme: Preset not found for id:", presetId);
|
|
3942
|
+
}
|
|
3943
|
+
return;
|
|
3944
|
+
}
|
|
3945
|
+
const presetData = {
|
|
3946
|
+
id: presetId,
|
|
3947
|
+
name: preset.label,
|
|
3948
|
+
colors: {
|
|
3949
|
+
light: preset.styles.light,
|
|
3950
|
+
dark: preset.styles.dark
|
|
3951
|
+
}
|
|
3952
|
+
};
|
|
3953
|
+
applyPreset(presetData);
|
|
3954
|
+
},
|
|
3955
|
+
[getAvailablePresetById, applyPreset]
|
|
3956
|
+
);
|
|
3895
3957
|
const clearPreset = (0, import_react.useCallback)(() => {
|
|
3896
3958
|
if (enablePresets && typeof window !== "undefined") {
|
|
3897
3959
|
try {
|
|
@@ -3939,6 +4001,7 @@ function ThemeProvider({
|
|
|
3939
4001
|
if (!isReady) {
|
|
3940
4002
|
return null;
|
|
3941
4003
|
}
|
|
4004
|
+
const isUsingDefaultPreset = !!defaultPreset && currentPreset?.presetId === defaultPreset;
|
|
3942
4005
|
const contextValue = {
|
|
3943
4006
|
mode,
|
|
3944
4007
|
resolvedMode,
|
|
@@ -3946,7 +4009,9 @@ function ThemeProvider({
|
|
|
3946
4009
|
toggleMode: handleModeToggle,
|
|
3947
4010
|
currentPreset,
|
|
3948
4011
|
applyPreset,
|
|
4012
|
+
setThemePresetById,
|
|
3949
4013
|
clearPreset,
|
|
4014
|
+
isUsingDefaultPreset,
|
|
3950
4015
|
availablePresets,
|
|
3951
4016
|
builtInPresets,
|
|
3952
4017
|
customPresets: normalizedCustomPresets
|
package/dist/index.mjs
CHANGED
|
@@ -3707,9 +3707,12 @@ function ThemeProvider({
|
|
|
3707
3707
|
}, [includeBuiltInPresets, customPresets]);
|
|
3708
3708
|
const builtInPresets = useMemo(() => includeBuiltInPresets ? tweakcnPresets : {}, [includeBuiltInPresets]);
|
|
3709
3709
|
const normalizedCustomPresets = useMemo(() => customPresets || {}, [customPresets]);
|
|
3710
|
-
const getAvailablePresetById = useCallback(
|
|
3711
|
-
|
|
3712
|
-
|
|
3710
|
+
const getAvailablePresetById = useCallback(
|
|
3711
|
+
(id) => {
|
|
3712
|
+
return availablePresets[id] || null;
|
|
3713
|
+
},
|
|
3714
|
+
[availablePresets]
|
|
3715
|
+
);
|
|
3713
3716
|
const [currentPreset, setCurrentPreset] = useState(null);
|
|
3714
3717
|
const [isReady, setIsReady] = useState(false);
|
|
3715
3718
|
useEffect(() => {
|
|
@@ -3719,10 +3722,9 @@ function ThemeProvider({
|
|
|
3719
3722
|
}
|
|
3720
3723
|
try {
|
|
3721
3724
|
const stored = localStorage.getItem(presetStorageKey);
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
} else if (defaultPreset) {
|
|
3725
|
+
const isDevelopment = typeof window !== "undefined" && window.location?.hostname === "localhost";
|
|
3726
|
+
const applyDefaultPreset = () => {
|
|
3727
|
+
if (!defaultPreset) return;
|
|
3726
3728
|
const preset = getAvailablePresetById(defaultPreset);
|
|
3727
3729
|
if (preset) {
|
|
3728
3730
|
const presetData = {
|
|
@@ -3738,13 +3740,42 @@ function ThemeProvider({
|
|
|
3738
3740
|
} else {
|
|
3739
3741
|
console.warn("\u{1F3A8} UnifiedTheme: Default preset not found:", defaultPreset);
|
|
3740
3742
|
}
|
|
3743
|
+
};
|
|
3744
|
+
if (stored) {
|
|
3745
|
+
try {
|
|
3746
|
+
const parsed = JSON.parse(stored);
|
|
3747
|
+
const isValidObject = parsed && typeof parsed === "object";
|
|
3748
|
+
const hasColors = isValidObject && "colors" in parsed && parsed.colors && typeof parsed.colors === "object" && parsed.colors.light && parsed.colors.dark;
|
|
3749
|
+
if (hasColors) {
|
|
3750
|
+
setCurrentPreset(parsed);
|
|
3751
|
+
if (isDevelopment) {
|
|
3752
|
+
}
|
|
3753
|
+
} else {
|
|
3754
|
+
if (isDevelopment) {
|
|
3755
|
+
console.warn("\u{1F3A8} UnifiedTheme: Invalid persisted preset shape. Clearing and falling back.");
|
|
3756
|
+
}
|
|
3757
|
+
localStorage.removeItem(presetStorageKey);
|
|
3758
|
+
applyDefaultPreset();
|
|
3759
|
+
}
|
|
3760
|
+
} catch (error) {
|
|
3761
|
+
if (isDevelopment) {
|
|
3762
|
+
console.warn("\u{1F3A8} UnifiedTheme: Failed to parse persisted preset. Clearing key.", error);
|
|
3763
|
+
}
|
|
3764
|
+
localStorage.removeItem(presetStorageKey);
|
|
3765
|
+
applyDefaultPreset();
|
|
3766
|
+
}
|
|
3767
|
+
} else {
|
|
3768
|
+
applyDefaultPreset();
|
|
3741
3769
|
}
|
|
3742
3770
|
} catch (error) {
|
|
3743
|
-
|
|
3771
|
+
const isDevelopment = typeof window !== "undefined" && window.location?.hostname === "localhost";
|
|
3772
|
+
if (isDevelopment) {
|
|
3773
|
+
console.warn("\u{1F3A8} UnifiedTheme: Failed to load preset from storage:", error);
|
|
3774
|
+
}
|
|
3744
3775
|
} finally {
|
|
3745
3776
|
setIsReady(true);
|
|
3746
3777
|
}
|
|
3747
|
-
}, [presetStorageKey, enablePresets, defaultPreset]);
|
|
3778
|
+
}, [presetStorageKey, enablePresets, defaultPreset, getAvailablePresetById]);
|
|
3748
3779
|
useEffect(() => {
|
|
3749
3780
|
if (mode === "system") {
|
|
3750
3781
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
@@ -3765,11 +3796,12 @@ function ThemeProvider({
|
|
|
3765
3796
|
root.style.colorScheme = resolvedMode;
|
|
3766
3797
|
}, [resolvedMode]);
|
|
3767
3798
|
const applyPresetColors = useCallback((preset, mode2) => {
|
|
3799
|
+
if (!preset) return;
|
|
3768
3800
|
const root = document.documentElement;
|
|
3769
3801
|
const colors = preset[mode2];
|
|
3770
3802
|
if (!colors) return;
|
|
3771
3803
|
const defaultValues = {
|
|
3772
|
-
|
|
3804
|
+
spacing: "0.25rem",
|
|
3773
3805
|
"letter-spacing": "normal"
|
|
3774
3806
|
};
|
|
3775
3807
|
const allCategories = [
|
|
@@ -3808,49 +3840,79 @@ function ThemeProvider({
|
|
|
3808
3840
|
}
|
|
3809
3841
|
});
|
|
3810
3842
|
}, []);
|
|
3811
|
-
const handleModeChange = useCallback(
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3843
|
+
const handleModeChange = useCallback(
|
|
3844
|
+
(newMode) => {
|
|
3845
|
+
setMode(newMode);
|
|
3846
|
+
setStoredMode(newMode, modeStorageKey);
|
|
3847
|
+
},
|
|
3848
|
+
[modeStorageKey]
|
|
3849
|
+
);
|
|
3850
|
+
const handleModeToggle = useCallback(
|
|
3851
|
+
(coordinates) => {
|
|
3852
|
+
const newMode = resolvedMode === "light" ? "dark" : "light";
|
|
3853
|
+
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
3854
|
+
if (enableTransitions && !prefersReducedMotion && typeof document !== "undefined" && "startViewTransition" in document) {
|
|
3855
|
+
const root = document.documentElement;
|
|
3856
|
+
if (coordinates) {
|
|
3857
|
+
root.style.setProperty("--x", `${coordinates.x}px`);
|
|
3858
|
+
root.style.setProperty("--y", `${coordinates.y}px`);
|
|
3859
|
+
}
|
|
3860
|
+
document.startViewTransition(() => {
|
|
3861
|
+
handleModeChange(newMode);
|
|
3862
|
+
});
|
|
3863
|
+
} else {
|
|
3825
3864
|
handleModeChange(newMode);
|
|
3826
|
-
}
|
|
3827
|
-
}
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
}, [resolvedMode, enableTransitions, handleModeChange]);
|
|
3865
|
+
}
|
|
3866
|
+
},
|
|
3867
|
+
[resolvedMode, enableTransitions, handleModeChange]
|
|
3868
|
+
);
|
|
3831
3869
|
useEffect(() => {
|
|
3832
3870
|
if (!currentPreset || typeof window === "undefined") return;
|
|
3833
3871
|
applyPresetColors(currentPreset.colors, resolvedMode);
|
|
3834
3872
|
}, [currentPreset, resolvedMode, applyPresetColors]);
|
|
3835
|
-
const applyPreset = useCallback(
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3873
|
+
const applyPreset = useCallback(
|
|
3874
|
+
(preset) => {
|
|
3875
|
+
const presetData = {
|
|
3876
|
+
presetId: preset.id,
|
|
3877
|
+
presetName: preset.name,
|
|
3878
|
+
colors: preset.colors,
|
|
3879
|
+
appliedAt: Date.now()
|
|
3880
|
+
};
|
|
3881
|
+
setCurrentPreset(presetData);
|
|
3882
|
+
if (enablePresets && typeof window !== "undefined") {
|
|
3883
|
+
try {
|
|
3884
|
+
localStorage.setItem(presetStorageKey, JSON.stringify(presetData));
|
|
3885
|
+
} catch (error) {
|
|
3886
|
+
console.error("\u{1F3A8} UnifiedTheme: Failed to save preset:", error);
|
|
3887
|
+
}
|
|
3848
3888
|
}
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
}
|
|
3853
|
-
|
|
3889
|
+
if (typeof window !== "undefined") {
|
|
3890
|
+
applyPresetColors(preset.colors, resolvedMode);
|
|
3891
|
+
}
|
|
3892
|
+
},
|
|
3893
|
+
[presetStorageKey, enablePresets, applyPresetColors, resolvedMode]
|
|
3894
|
+
);
|
|
3895
|
+
const setThemePresetById = useCallback(
|
|
3896
|
+
(presetId) => {
|
|
3897
|
+
const preset = getAvailablePresetById(presetId);
|
|
3898
|
+
if (!preset) {
|
|
3899
|
+
if (typeof window !== "undefined") {
|
|
3900
|
+
console.warn("\u{1F3A8} UnifiedTheme: Preset not found for id:", presetId);
|
|
3901
|
+
}
|
|
3902
|
+
return;
|
|
3903
|
+
}
|
|
3904
|
+
const presetData = {
|
|
3905
|
+
id: presetId,
|
|
3906
|
+
name: preset.label,
|
|
3907
|
+
colors: {
|
|
3908
|
+
light: preset.styles.light,
|
|
3909
|
+
dark: preset.styles.dark
|
|
3910
|
+
}
|
|
3911
|
+
};
|
|
3912
|
+
applyPreset(presetData);
|
|
3913
|
+
},
|
|
3914
|
+
[getAvailablePresetById, applyPreset]
|
|
3915
|
+
);
|
|
3854
3916
|
const clearPreset = useCallback(() => {
|
|
3855
3917
|
if (enablePresets && typeof window !== "undefined") {
|
|
3856
3918
|
try {
|
|
@@ -3898,6 +3960,7 @@ function ThemeProvider({
|
|
|
3898
3960
|
if (!isReady) {
|
|
3899
3961
|
return null;
|
|
3900
3962
|
}
|
|
3963
|
+
const isUsingDefaultPreset = !!defaultPreset && currentPreset?.presetId === defaultPreset;
|
|
3901
3964
|
const contextValue = {
|
|
3902
3965
|
mode,
|
|
3903
3966
|
resolvedMode,
|
|
@@ -3905,7 +3968,9 @@ function ThemeProvider({
|
|
|
3905
3968
|
toggleMode: handleModeToggle,
|
|
3906
3969
|
currentPreset,
|
|
3907
3970
|
applyPreset,
|
|
3971
|
+
setThemePresetById,
|
|
3908
3972
|
clearPreset,
|
|
3973
|
+
isUsingDefaultPreset,
|
|
3909
3974
|
availablePresets,
|
|
3910
3975
|
builtInPresets,
|
|
3911
3976
|
customPresets: normalizedCustomPresets
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fakhrirafiki/theme-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Elegant theming system with smooth transitions, custom presets, semantic accent colors, and complete shadcn/ui support for modern React applications",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"dark-mode",
|
|
58
58
|
"light-mode",
|
|
59
59
|
"react",
|
|
60
|
-
"typescript",
|
|
60
|
+
"typescript",
|
|
61
61
|
"transitions",
|
|
62
62
|
"shadcn",
|
|
63
63
|
"tailwind",
|
|
@@ -70,6 +70,6 @@
|
|
|
70
70
|
"accent-colors",
|
|
71
71
|
"semantic-colors"
|
|
72
72
|
],
|
|
73
|
-
"author": "
|
|
73
|
+
"author": "Tiket Ngobrol Team",
|
|
74
74
|
"license": "MIT"
|
|
75
|
-
}
|
|
75
|
+
}
|