@kilivi/payloadcms-theme-management 0.5.7 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/InitTheme.d.ts +3 -0
- package/dist/components/InitTheme.d.ts.map +1 -1
- package/dist/components/InitTheme.js +9 -7
- package/dist/components/InitTheme.js.map +1 -1
- package/dist/fields/ThemePresetImportField.d.ts +4 -0
- package/dist/fields/ThemePresetImportField.d.ts.map +1 -0
- package/dist/fields/ThemePresetImportField.js +219 -0
- package/dist/fields/ThemePresetImportField.js.map +1 -0
- package/dist/fields/ThemePreviewField.d.ts.map +1 -1
- package/dist/fields/ThemePreviewField.js +32 -25
- package/dist/fields/ThemePreviewField.js.map +1 -1
- package/dist/fields/themeConfigurationField.d.ts.map +1 -1
- package/dist/fields/themeConfigurationField.js +44 -10
- package/dist/fields/themeConfigurationField.js.map +1 -1
- package/dist/utils/customThemePresets.d.ts +5 -0
- package/dist/utils/customThemePresets.d.ts.map +1 -0
- package/dist/utils/customThemePresets.js +82 -0
- package/dist/utils/customThemePresets.js.map +1 -0
- package/dist/utils/extendedThemeHelpers.d.ts +1 -1
- package/dist/utils/extendedThemeHelpers.d.ts.map +1 -1
- package/dist/utils/extendedThemeHelpers.js +16 -16
- package/dist/utils/extendedThemeHelpers.js.map +1 -1
- package/package.json +3 -1
|
@@ -7,6 +7,9 @@ import React from 'react';
|
|
|
7
7
|
* - User preference from localStorage
|
|
8
8
|
* - Admin-controlled color mode toggle
|
|
9
9
|
* - Auto mode with system preference fallback
|
|
10
|
+
*
|
|
11
|
+
* Note: Uses Next.js Script component with beforeInteractive strategy
|
|
12
|
+
* to ensure theme is applied before React hydration, preventing flash of unstyled content.
|
|
10
13
|
*/
|
|
11
14
|
export declare const InitTheme: React.FC;
|
|
12
15
|
//# sourceMappingURL=InitTheme.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InitTheme.d.ts","sourceRoot":"","sources":["../../src/components/InitTheme.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"InitTheme.d.ts","sourceRoot":"","sources":["../../src/components/InitTheme.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAyD7B,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import Script from 'next/script';
|
|
3
2
|
import React from 'react';
|
|
3
|
+
import Script from 'next/script';
|
|
4
4
|
import { modeLocalStorageKey } from '../providers/Theme/shared.js';
|
|
5
5
|
/**
|
|
6
6
|
* Client-side theme initialization script
|
|
@@ -10,10 +10,11 @@ import { modeLocalStorageKey } from '../providers/Theme/shared.js';
|
|
|
10
10
|
* - User preference from localStorage
|
|
11
11
|
* - Admin-controlled color mode toggle
|
|
12
12
|
* - Auto mode with system preference fallback
|
|
13
|
+
*
|
|
14
|
+
* Note: Uses Next.js Script component with beforeInteractive strategy
|
|
15
|
+
* to ensure theme is applied before React hydration, preventing flash of unstyled content.
|
|
13
16
|
*/ export const InitTheme = ()=>{
|
|
14
|
-
|
|
15
|
-
dangerouslySetInnerHTML: {
|
|
16
|
-
__html: `
|
|
17
|
+
const scriptContent = `
|
|
17
18
|
(function () {
|
|
18
19
|
function getImplicitModePreference() {
|
|
19
20
|
var mediaQuery = '(prefers-color-scheme: dark)'
|
|
@@ -62,10 +63,11 @@ import { modeLocalStorageKey } from '../providers/Theme/shared.js';
|
|
|
62
63
|
})
|
|
63
64
|
}
|
|
64
65
|
})();
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
`;
|
|
67
|
+
return /*#__PURE__*/ _jsx(Script, {
|
|
67
68
|
id: "theme-script",
|
|
68
|
-
strategy: "beforeInteractive"
|
|
69
|
+
strategy: "beforeInteractive",
|
|
70
|
+
children: scriptContent
|
|
69
71
|
});
|
|
70
72
|
};
|
|
71
73
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/InitTheme.tsx"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../../src/components/InitTheme.tsx"],"sourcesContent":["import React from 'react'\nimport Script from 'next/script'\nimport { modeLocalStorageKey } from '../providers/Theme/shared.js'\n\n/**\n * Client-side theme initialization script\n * Prevents FOUC by applying theme mode before React hydration\n * Handles:\n * - System preference detection\n * - User preference from localStorage\n * - Admin-controlled color mode toggle\n * - Auto mode with system preference fallback\n *\n * Note: Uses Next.js Script component with beforeInteractive strategy\n * to ensure theme is applied before React hydration, preventing flash of unstyled content.\n */\nexport const InitTheme: React.FC = () => {\n const scriptContent = `\n (function () {\n function getImplicitModePreference() {\n var mediaQuery = '(prefers-color-scheme: dark)'\n var mql = window.matchMedia(mediaQuery)\n return mql.matches ? 'dark' : 'light'\n }\n\n function modeIsValid(mode) {\n return mode === 'light' || mode === 'dark'\n }\n\n // Get current mode from server-set attribute\n var currentMode = document.documentElement.getAttribute('data-theme-mode')\n var allowToggle = document.documentElement.getAttribute('data-allow-color-mode-toggle') === 'true'\n \n // Only override mode if admin allows toggle and user has preference\n if (allowToggle) {\n var modePreference = window.localStorage.getItem('${modeLocalStorageKey}')\n \n if (modeIsValid(modePreference)) {\n currentMode = modePreference\n } else if (currentMode === 'auto') {\n currentMode = getImplicitModePreference()\n }\n } else if (currentMode === 'auto') {\n // Admin set auto mode - use system preference\n currentMode = getImplicitModePreference()\n }\n\n // Apply the determined mode\n if (currentMode && modeIsValid(currentMode)) {\n document.documentElement.setAttribute('data-theme-mode', currentMode)\n }\n\n // Listen for system theme changes only when needed\n if (allowToggle || currentMode === 'auto') {\n var mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n mediaQuery.addEventListener('change', function(e) {\n var shouldUpdate = allowToggle \n ? !window.localStorage.getItem('${modeLocalStorageKey}')\n : document.documentElement.getAttribute('data-theme-mode') === 'auto'\n \n if (shouldUpdate) {\n document.documentElement.setAttribute('data-theme-mode', e.matches ? 'dark' : 'light')\n }\n })\n }\n })();\n `\n\n return (\n <Script id=\"theme-script\" strategy=\"beforeInteractive\">\n {scriptContent}\n </Script>\n )\n}\n"],"names":["React","Script","modeLocalStorageKey","InitTheme","scriptContent","id","strategy"],"mappings":";AAAA,OAAOA,WAAW,QAAO;AACzB,OAAOC,YAAY,cAAa;AAChC,SAASC,mBAAmB,QAAQ,+BAA8B;AAElE;;;;;;;;;;;CAWC,GACD,OAAO,MAAMC,YAAsB;IACjC,MAAMC,gBAAgB,CAAC;;;;;;;;;;;;;;;;;;wDAkB+B,EAAEF,oBAAoB;;;;;;;;;;;;;;;;;;;;;;0CAsBpC,EAAEA,oBAAoB;;;;;;;;;EAS9D,CAAC;IAED,qBACE,KAACD;QAAOI,IAAG;QAAeC,UAAS;kBAChCF;;AAGP,EAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemePresetImportField.d.ts","sourceRoot":"","sources":["../../src/fields/ThemePresetImportField.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AASnD,QAAA,MAAM,sBAAsB,GAAI,iBAAiB,oBAAoB,gCAyJpE,CAAA;AAED,eAAe,sBAAsB,CAAA"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { Button, Drawer, useField, useModal } from '@payloadcms/ui';
|
|
4
|
+
import { toast } from 'sonner';
|
|
5
|
+
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
6
|
+
import { parseThemePresetInput } from '../utils/customThemePresets.js';
|
|
7
|
+
const baseClass = 'theme-preset-import-field';
|
|
8
|
+
const drawerSlug = 'import-theme-presets';
|
|
9
|
+
const ThemePresetImportField = ({ field, path })=>{
|
|
10
|
+
const { value, setValue } = useField({
|
|
11
|
+
path
|
|
12
|
+
});
|
|
13
|
+
const [textValue, setTextValue] = useState('');
|
|
14
|
+
const { closeModal, openModal } = useModal();
|
|
15
|
+
const fileInputRef = useRef(null);
|
|
16
|
+
const presetSummary = useMemo(()=>{
|
|
17
|
+
if (!value || value.length === 0) {
|
|
18
|
+
return 'No custom presets imported';
|
|
19
|
+
}
|
|
20
|
+
const names = value.map((preset)=>preset.name).join(', ');
|
|
21
|
+
return `${value.length} preset${value.length === 1 ? '' : 's'}: ${names}`;
|
|
22
|
+
}, [
|
|
23
|
+
value
|
|
24
|
+
]);
|
|
25
|
+
const applyFromText = useCallback((raw)=>{
|
|
26
|
+
if (!raw.trim()) {
|
|
27
|
+
setValue(null);
|
|
28
|
+
closeModal(drawerSlug);
|
|
29
|
+
toast.success('Cleared custom presets');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const presets = parseThemePresetInput(raw);
|
|
33
|
+
if (presets.length === 0) {
|
|
34
|
+
toast.error('No valid presets found. Ensure each entry has a name and label.');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
setValue(presets);
|
|
38
|
+
closeModal(drawerSlug);
|
|
39
|
+
toast.success(`Imported ${presets.length} preset${presets.length === 1 ? '' : 's'}`);
|
|
40
|
+
setTextValue('');
|
|
41
|
+
}, [
|
|
42
|
+
setValue,
|
|
43
|
+
closeModal
|
|
44
|
+
]);
|
|
45
|
+
const handleApplyClick = useCallback(()=>{
|
|
46
|
+
applyFromText(textValue);
|
|
47
|
+
}, [
|
|
48
|
+
applyFromText,
|
|
49
|
+
textValue
|
|
50
|
+
]);
|
|
51
|
+
const handleFileChange = useCallback(async (event)=>{
|
|
52
|
+
const file = event.target?.files?.[0];
|
|
53
|
+
if (!file) return;
|
|
54
|
+
try {
|
|
55
|
+
const content = await file.text();
|
|
56
|
+
setTextValue(content);
|
|
57
|
+
applyFromText(content);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
toast.error(error.message);
|
|
60
|
+
} finally{
|
|
61
|
+
if (event.target) {
|
|
62
|
+
event.target.value = '';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}, [
|
|
66
|
+
applyFromText
|
|
67
|
+
]);
|
|
68
|
+
const handleImportClick = useCallback(()=>{
|
|
69
|
+
fileInputRef.current?.click();
|
|
70
|
+
}, []);
|
|
71
|
+
const handleClear = useCallback(()=>{
|
|
72
|
+
setValue(null);
|
|
73
|
+
toast.success('Cleared custom presets');
|
|
74
|
+
}, [
|
|
75
|
+
setValue
|
|
76
|
+
]);
|
|
77
|
+
const label = typeof field.label === 'string' ? field.label : field.label?.en ?? 'Custom Presets';
|
|
78
|
+
const description = typeof field.admin?.description === 'string' ? field.admin.description : field.admin?.description?.en;
|
|
79
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
80
|
+
children: [
|
|
81
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
82
|
+
className: baseClass,
|
|
83
|
+
style: {
|
|
84
|
+
display: 'grid',
|
|
85
|
+
gap: '0.75rem'
|
|
86
|
+
},
|
|
87
|
+
children: [
|
|
88
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
89
|
+
style: {
|
|
90
|
+
display: 'flex',
|
|
91
|
+
flexDirection: 'column',
|
|
92
|
+
gap: '0.25rem'
|
|
93
|
+
},
|
|
94
|
+
children: [
|
|
95
|
+
/*#__PURE__*/ _jsx("label", {
|
|
96
|
+
style: {
|
|
97
|
+
fontWeight: 600
|
|
98
|
+
},
|
|
99
|
+
children: label
|
|
100
|
+
}),
|
|
101
|
+
description && /*#__PURE__*/ _jsx("p", {
|
|
102
|
+
style: {
|
|
103
|
+
margin: 0,
|
|
104
|
+
color: 'var(--theme-elevation-500)',
|
|
105
|
+
fontSize: '0.9rem'
|
|
106
|
+
},
|
|
107
|
+
children: description
|
|
108
|
+
})
|
|
109
|
+
]
|
|
110
|
+
}),
|
|
111
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
112
|
+
style: {
|
|
113
|
+
display: 'flex',
|
|
114
|
+
alignItems: 'center',
|
|
115
|
+
gap: '0.5rem',
|
|
116
|
+
flexWrap: 'wrap'
|
|
117
|
+
},
|
|
118
|
+
children: [
|
|
119
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
120
|
+
buttonStyle: "secondary",
|
|
121
|
+
onClick: ()=>openModal(drawerSlug),
|
|
122
|
+
size: "small",
|
|
123
|
+
children: "Import Presets"
|
|
124
|
+
}),
|
|
125
|
+
value && value.length > 0 && /*#__PURE__*/ _jsx(Button, {
|
|
126
|
+
buttonStyle: "secondary",
|
|
127
|
+
onClick: handleClear,
|
|
128
|
+
size: "small",
|
|
129
|
+
children: "Clear"
|
|
130
|
+
}),
|
|
131
|
+
/*#__PURE__*/ _jsx("span", {
|
|
132
|
+
style: {
|
|
133
|
+
fontSize: '0.875rem',
|
|
134
|
+
color: 'var(--theme-elevation-600)'
|
|
135
|
+
},
|
|
136
|
+
children: presetSummary
|
|
137
|
+
})
|
|
138
|
+
]
|
|
139
|
+
})
|
|
140
|
+
]
|
|
141
|
+
}),
|
|
142
|
+
/*#__PURE__*/ _jsx(Drawer, {
|
|
143
|
+
slug: drawerSlug,
|
|
144
|
+
title: "Import Theme Presets",
|
|
145
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
146
|
+
style: {
|
|
147
|
+
padding: '1.5rem',
|
|
148
|
+
display: 'grid',
|
|
149
|
+
gap: '1rem'
|
|
150
|
+
},
|
|
151
|
+
children: [
|
|
152
|
+
/*#__PURE__*/ _jsx("p", {
|
|
153
|
+
style: {
|
|
154
|
+
margin: 0,
|
|
155
|
+
color: 'var(--theme-elevation-600)'
|
|
156
|
+
},
|
|
157
|
+
children: "Paste JSON or upload a .json file containing theme preset definitions."
|
|
158
|
+
}),
|
|
159
|
+
/*#__PURE__*/ _jsx("textarea", {
|
|
160
|
+
value: textValue,
|
|
161
|
+
onChange: (event)=>setTextValue(event.target.value),
|
|
162
|
+
rows: 15,
|
|
163
|
+
spellCheck: false,
|
|
164
|
+
style: {
|
|
165
|
+
width: '100%',
|
|
166
|
+
fontFamily: 'monospace',
|
|
167
|
+
borderRadius: '8px',
|
|
168
|
+
border: '1px solid var(--theme-elevation-200)',
|
|
169
|
+
padding: '12px',
|
|
170
|
+
fontSize: '0.875rem'
|
|
171
|
+
},
|
|
172
|
+
placeholder: `[
|
|
173
|
+
{
|
|
174
|
+
"name": "brand-dark",
|
|
175
|
+
"label": "Brand Dark",
|
|
176
|
+
"borderRadius": "medium",
|
|
177
|
+
"lightMode": { "background": "#ffffff", "foreground": "#111" },
|
|
178
|
+
"darkMode": { "background": "#0a0a0a", "foreground": "#fff" }
|
|
179
|
+
}
|
|
180
|
+
]`
|
|
181
|
+
}),
|
|
182
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
183
|
+
style: {
|
|
184
|
+
display: 'flex',
|
|
185
|
+
gap: '0.5rem',
|
|
186
|
+
justifyContent: 'flex-end'
|
|
187
|
+
},
|
|
188
|
+
children: [
|
|
189
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
190
|
+
buttonStyle: "secondary",
|
|
191
|
+
onClick: handleImportClick,
|
|
192
|
+
children: "Upload JSON File"
|
|
193
|
+
}),
|
|
194
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
195
|
+
buttonStyle: "primary",
|
|
196
|
+
onClick: handleApplyClick,
|
|
197
|
+
disabled: !textValue.trim(),
|
|
198
|
+
children: "Apply"
|
|
199
|
+
})
|
|
200
|
+
]
|
|
201
|
+
}),
|
|
202
|
+
/*#__PURE__*/ _jsx("input", {
|
|
203
|
+
type: "file",
|
|
204
|
+
accept: "application/json",
|
|
205
|
+
ref: fileInputRef,
|
|
206
|
+
onChange: handleFileChange,
|
|
207
|
+
style: {
|
|
208
|
+
display: 'none'
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
]
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
]
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
export default ThemePresetImportField;
|
|
218
|
+
|
|
219
|
+
//# sourceMappingURL=ThemePresetImportField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/fields/ThemePresetImportField.tsx"],"sourcesContent":["'use client'\n\nimport { Button, Drawer, useField, useModal } from '@payloadcms/ui'\nimport type { JSONFieldClientProps } from 'payload'\nimport { toast } from 'sonner'\nimport { useCallback, useMemo, useRef, useState, type ChangeEvent } from 'react'\nimport type { ThemePreset } from '../presets.js'\nimport { parseThemePresetInput } from '../utils/customThemePresets.js'\n\nconst baseClass = 'theme-preset-import-field'\nconst drawerSlug = 'import-theme-presets'\n\nconst ThemePresetImportField = ({ field, path }: JSONFieldClientProps) => {\n const { value, setValue } = useField<ThemePreset[] | null>({ path })\n const [textValue, setTextValue] = useState('')\n const { closeModal, openModal } = useModal()\n const fileInputRef = useRef<HTMLInputElement | null>(null)\n\n const presetSummary = useMemo(() => {\n if (!value || value.length === 0) {\n return 'No custom presets imported'\n }\n const names = value.map((preset) => preset.name).join(', ')\n return `${value.length} preset${value.length === 1 ? '' : 's'}: ${names}`\n }, [value])\n\n const applyFromText = useCallback(\n (raw: string) => {\n if (!raw.trim()) {\n setValue(null)\n closeModal(drawerSlug)\n toast.success('Cleared custom presets')\n return\n }\n\n const presets = parseThemePresetInput(raw)\n if (presets.length === 0) {\n toast.error('No valid presets found. Ensure each entry has a name and label.')\n return\n }\n\n setValue(presets)\n closeModal(drawerSlug)\n toast.success(`Imported ${presets.length} preset${presets.length === 1 ? '' : 's'}`)\n setTextValue('')\n },\n [setValue, closeModal],\n )\n\n const handleApplyClick = useCallback(() => {\n applyFromText(textValue)\n }, [applyFromText, textValue])\n\n const handleFileChange = useCallback(\n async (event: ChangeEvent<HTMLInputElement>) => {\n const file = event.target?.files?.[0]\n if (!file) return\n try {\n const content = await file.text()\n setTextValue(content)\n applyFromText(content)\n } catch (error) {\n toast.error((error as Error).message)\n } finally {\n if (event.target) {\n event.target.value = ''\n }\n }\n },\n [applyFromText],\n )\n\n const handleImportClick = useCallback(() => {\n fileInputRef.current?.click()\n }, [])\n\n const handleClear = useCallback(() => {\n setValue(null)\n toast.success('Cleared custom presets')\n }, [setValue])\n\n const label =\n typeof field.label === 'string' ? field.label : (field.label?.en ?? 'Custom Presets')\n const description =\n typeof field.admin?.description === 'string'\n ? field.admin.description\n : (field.admin?.description as Record<string, string>)?.en\n\n return (\n <>\n <div className={baseClass} style={{ display: 'grid', gap: '0.75rem' }}>\n <div style={{ display: 'flex', flexDirection: 'column', gap: '0.25rem' }}>\n <label style={{ fontWeight: 600 }}>{label}</label>\n {description && (\n <p style={{ margin: 0, color: 'var(--theme-elevation-500)', fontSize: '0.9rem' }}>\n {description}\n </p>\n )}\n </div>\n\n <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', flexWrap: 'wrap' }}>\n <Button buttonStyle=\"secondary\" onClick={() => openModal(drawerSlug)} size=\"small\">\n Import Presets\n </Button>\n {value && value.length > 0 && (\n <Button buttonStyle=\"secondary\" onClick={handleClear} size=\"small\">\n Clear\n </Button>\n )}\n <span style={{ fontSize: '0.875rem', color: 'var(--theme-elevation-600)' }}>\n {presetSummary}\n </span>\n </div>\n </div>\n\n <Drawer slug={drawerSlug} title=\"Import Theme Presets\">\n <div style={{ padding: '1.5rem', display: 'grid', gap: '1rem' }}>\n <p style={{ margin: 0, color: 'var(--theme-elevation-600)' }}>\n Paste JSON or upload a .json file containing theme preset definitions.\n </p>\n\n <textarea\n value={textValue}\n onChange={(event) => setTextValue(event.target.value)}\n rows={15}\n spellCheck={false}\n style={{\n width: '100%',\n fontFamily: 'monospace',\n borderRadius: '8px',\n border: '1px solid var(--theme-elevation-200)',\n padding: '12px',\n fontSize: '0.875rem',\n }}\n placeholder={`[\n {\n \"name\": \"brand-dark\",\n \"label\": \"Brand Dark\",\n \"borderRadius\": \"medium\",\n \"lightMode\": { \"background\": \"#ffffff\", \"foreground\": \"#111\" },\n \"darkMode\": { \"background\": \"#0a0a0a\", \"foreground\": \"#fff\" }\n }\n]`}\n />\n\n <div style={{ display: 'flex', gap: '0.5rem', justifyContent: 'flex-end' }}>\n <Button buttonStyle=\"secondary\" onClick={handleImportClick}>\n Upload JSON File\n </Button>\n <Button buttonStyle=\"primary\" onClick={handleApplyClick} disabled={!textValue.trim()}>\n Apply\n </Button>\n </div>\n\n <input\n type=\"file\"\n accept=\"application/json\"\n ref={fileInputRef}\n onChange={handleFileChange}\n style={{ display: 'none' }}\n />\n </div>\n </Drawer>\n </>\n )\n}\n\nexport default ThemePresetImportField\n"],"names":["Button","Drawer","useField","useModal","toast","useCallback","useMemo","useRef","useState","parseThemePresetInput","baseClass","drawerSlug","ThemePresetImportField","field","path","value","setValue","textValue","setTextValue","closeModal","openModal","fileInputRef","presetSummary","length","names","map","preset","name","join","applyFromText","raw","trim","success","presets","error","handleApplyClick","handleFileChange","event","file","target","files","content","text","message","handleImportClick","current","click","handleClear","label","en","description","admin","div","className","style","display","gap","flexDirection","fontWeight","p","margin","color","fontSize","alignItems","flexWrap","buttonStyle","onClick","size","span","slug","title","padding","textarea","onChange","rows","spellCheck","width","fontFamily","borderRadius","border","placeholder","justifyContent","disabled","input","type","accept","ref"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemePreviewField.d.ts","sourceRoot":"","sources":["../../src/fields/ThemePreviewField.tsx"],"names":[],"mappings":"AAEA,OAAO,yBAAyB,CAAA;AAEhC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"ThemePreviewField.d.ts","sourceRoot":"","sources":["../../src/fields/ThemePreviewField.tsx"],"names":[],"mappings":"AAEA,OAAO,yBAAyB,CAAA;AAEhC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AA2PrD,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,+BAmZtE"}
|
|
@@ -7,23 +7,7 @@ import { resolveTypographyPreview } from '../components/typographyPreviewUtils.j
|
|
|
7
7
|
import { allThemePresets } from '../index.js';
|
|
8
8
|
import { borderRadiusPresets } from '../providers/Theme/themeConfig.js';
|
|
9
9
|
import { darkModeDefaults, lightModeDefaults } from './colorModeFields.js';
|
|
10
|
-
|
|
11
|
-
const themePresets = allThemePresets.reduce((acc, preset)=>{
|
|
12
|
-
acc[preset.name] = {
|
|
13
|
-
label: preset.label,
|
|
14
|
-
lightMode: {
|
|
15
|
-
...lightModeDefaults,
|
|
16
|
-
...preset.lightMode ?? {}
|
|
17
|
-
},
|
|
18
|
-
darkMode: {
|
|
19
|
-
...darkModeDefaults,
|
|
20
|
-
...preset.darkMode ?? {}
|
|
21
|
-
},
|
|
22
|
-
typography: preset.typography,
|
|
23
|
-
borderRadius: preset.borderRadius
|
|
24
|
-
};
|
|
25
|
-
return acc;
|
|
26
|
-
}, {});
|
|
10
|
+
// FALLBACK_THEME and runtime theme presets are computed inside the component
|
|
27
11
|
const colorKeys = Object.keys(lightModeDefaults);
|
|
28
12
|
const highlightSwatches = [
|
|
29
13
|
{
|
|
@@ -255,6 +239,29 @@ function ModePreview({ icon, title, colors, typography, radius }) {
|
|
|
255
239
|
}
|
|
256
240
|
export default function ThemePreviewField(props) {
|
|
257
241
|
const { field, path } = props;
|
|
242
|
+
// If the plugin provided theme presets via admin config, prefer those; otherwise fallback to allThemePresets
|
|
243
|
+
const baseThemePresets = field?.admin?.themePresets ?? allThemePresets;
|
|
244
|
+
const runtimeThemePresets = useMemo(()=>{
|
|
245
|
+
return baseThemePresets.reduce((acc, preset)=>{
|
|
246
|
+
acc[preset.name] = {
|
|
247
|
+
label: preset.label,
|
|
248
|
+
lightMode: {
|
|
249
|
+
...lightModeDefaults,
|
|
250
|
+
...preset.lightMode ?? {}
|
|
251
|
+
},
|
|
252
|
+
darkMode: {
|
|
253
|
+
...darkModeDefaults,
|
|
254
|
+
...preset.darkMode ?? {}
|
|
255
|
+
},
|
|
256
|
+
typography: preset.typography,
|
|
257
|
+
borderRadius: preset.borderRadius
|
|
258
|
+
};
|
|
259
|
+
return acc;
|
|
260
|
+
}, {});
|
|
261
|
+
}, [
|
|
262
|
+
baseThemePresets
|
|
263
|
+
]);
|
|
264
|
+
const fallbackTheme = baseThemePresets[0]?.name ?? 'cool';
|
|
258
265
|
const { value: selectedTheme, setValue } = useField({
|
|
259
266
|
path
|
|
260
267
|
});
|
|
@@ -262,7 +269,7 @@ export default function ThemePreviewField(props) {
|
|
|
262
269
|
const formFields = useFormFields(([formState])=>formState);
|
|
263
270
|
const hasAppliedInitialPresetRef = useRef(false);
|
|
264
271
|
const applyPreset = useCallback((presetName)=>{
|
|
265
|
-
const preset =
|
|
272
|
+
const preset = runtimeThemePresets[presetName];
|
|
266
273
|
if (!preset) {
|
|
267
274
|
return;
|
|
268
275
|
}
|
|
@@ -302,11 +309,11 @@ export default function ThemePreviewField(props) {
|
|
|
302
309
|
if (!selectedTheme) {
|
|
303
310
|
return;
|
|
304
311
|
}
|
|
305
|
-
if (
|
|
312
|
+
if (runtimeThemePresets[selectedTheme]) {
|
|
306
313
|
return;
|
|
307
314
|
}
|
|
308
315
|
hasAppliedInitialPresetRef.current = false;
|
|
309
|
-
setValue(
|
|
316
|
+
setValue(fallbackTheme);
|
|
310
317
|
}, [
|
|
311
318
|
selectedTheme,
|
|
312
319
|
setValue
|
|
@@ -321,7 +328,7 @@ export default function ThemePreviewField(props) {
|
|
|
321
328
|
if (!formFields) {
|
|
322
329
|
return;
|
|
323
330
|
}
|
|
324
|
-
const preset =
|
|
331
|
+
const preset = runtimeThemePresets[selectedTheme];
|
|
325
332
|
if (!preset) {
|
|
326
333
|
hasAppliedInitialPresetRef.current = true;
|
|
327
334
|
return;
|
|
@@ -345,7 +352,7 @@ export default function ThemePreviewField(props) {
|
|
|
345
352
|
applyPreset
|
|
346
353
|
]);
|
|
347
354
|
const lightModeColors = useMemo(()=>{
|
|
348
|
-
const presetLightMode = selectedTheme ?
|
|
355
|
+
const presetLightMode = selectedTheme ? runtimeThemePresets[selectedTheme]?.lightMode : undefined;
|
|
349
356
|
return colorKeys.reduce((acc, key)=>{
|
|
350
357
|
const fieldState = formFields?.[`themeConfiguration.lightMode.${key}`];
|
|
351
358
|
const rawValue = typeof fieldState?.value === 'string' && fieldState.value.trim().length > 0 ? fieldState.value : undefined;
|
|
@@ -357,7 +364,7 @@ export default function ThemePreviewField(props) {
|
|
|
357
364
|
selectedTheme
|
|
358
365
|
]);
|
|
359
366
|
const darkModeColors = useMemo(()=>{
|
|
360
|
-
const presetDarkMode = selectedTheme ?
|
|
367
|
+
const presetDarkMode = selectedTheme ? runtimeThemePresets[selectedTheme]?.darkMode : undefined;
|
|
361
368
|
return colorKeys.reduce((acc, key)=>{
|
|
362
369
|
const fieldState = formFields?.[`themeConfiguration.darkMode.${key}`];
|
|
363
370
|
const rawValue = typeof fieldState?.value === 'string' && fieldState.value.trim().length > 0 ? fieldState.value : undefined;
|
|
@@ -368,7 +375,7 @@ export default function ThemePreviewField(props) {
|
|
|
368
375
|
formFields,
|
|
369
376
|
selectedTheme
|
|
370
377
|
]);
|
|
371
|
-
const activePreset = selectedTheme ?
|
|
378
|
+
const activePreset = selectedTheme ? runtimeThemePresets[selectedTheme] : undefined;
|
|
372
379
|
const formTypographySelection = useMemo(()=>{
|
|
373
380
|
if (!formFields) {
|
|
374
381
|
return null;
|
|
@@ -438,7 +445,7 @@ export default function ThemePreviewField(props) {
|
|
|
438
445
|
children: [
|
|
439
446
|
/*#__PURE__*/ _jsx("div", {
|
|
440
447
|
className: "theme-preset-list",
|
|
441
|
-
children: Object.entries(
|
|
448
|
+
children: Object.entries(runtimeThemePresets).map(([key, preset])=>{
|
|
442
449
|
const isSelected = key === selectedTheme;
|
|
443
450
|
const swatches = highlightSwatches.map(({ key: colorKey })=>preset.lightMode[colorKey]).filter((color)=>Boolean(color));
|
|
444
451
|
return /*#__PURE__*/ _jsxs("button", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/fields/ThemePreviewField.tsx"],"sourcesContent":["'use client'\n\nimport './ThemePreviewField.css'\nimport { useField, useForm, useFormFields } from '@payloadcms/ui'\nimport type { SelectFieldClientProps } from 'payload'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\nimport type {\n ResolvedTypographyPreview,\n TypographySelection,\n} from '../components/typographyPreviewUtils.js'\nimport { resolveTypographyPreview } from '../components/typographyPreviewUtils.js'\nimport { allThemePresets } from '../index.js'\nimport type { ThemeTypographyPreset } from '../presets.js'\nimport { borderRadiusPresets } from '../providers/Theme/themeConfig.js'\nimport { darkModeDefaults, lightModeDefaults } from './colorModeFields.js'\n\ninterface ColorModeColors {\n background?: string\n foreground?: string\n card?: string\n cardForeground?: string\n popover?: string\n popoverForeground?: string\n primary?: string\n primaryForeground?: string\n secondary?: string\n secondaryForeground?: string\n muted?: string\n mutedForeground?: string\n accent?: string\n accentForeground?: string\n destructive?: string\n destructiveForeground?: string\n border?: string\n input?: string\n ring?: string\n}\n\ninterface ThemePresetDefinition {\n label: string\n lightMode: ColorModeColors\n darkMode: ColorModeColors\n typography?: ThemeTypographyPreset\n borderRadius: 'none' | 'small' | 'medium' | 'large' | 'xl'\n}\n\nconst FALLBACK_THEME = allThemePresets[0]?.name ?? 'cool'\n\nconst themePresets = allThemePresets.reduce<Record<string, ThemePresetDefinition>>(\n (acc, preset) => {\n acc[preset.name] = {\n label: preset.label,\n lightMode: { ...lightModeDefaults, ...(preset.lightMode ?? {}) },\n darkMode: { ...darkModeDefaults, ...(preset.darkMode ?? {}) },\n typography: preset.typography,\n borderRadius: preset.borderRadius,\n }\n return acc\n },\n {},\n)\n\nconst colorKeys = Object.keys(lightModeDefaults) as (keyof ColorModeColors)[]\n\nconst highlightSwatches: Array<{ key: keyof ColorModeColors; label: string }> = [\n { key: 'primary', label: 'Primary' },\n { key: 'secondary', label: 'Secondary' },\n { key: 'accent', label: 'Accent' },\n { key: 'background', label: 'Background' },\n { key: 'foreground', label: 'Foreground' },\n]\n\ninterface ModePreviewProps {\n icon: string\n title: string\n colors: ColorModeColors\n typography: ResolvedTypographyPreview\n radius: {\n card: string\n chip: string\n pill: string\n }\n}\n\nfunction ModePreview({ icon, title, colors, typography, radius }: Readonly<ModePreviewProps>) {\n const background = colors.background ?? '#ffffff'\n const foreground = colors.foreground ?? '#0a0a0a'\n const borderColor = colors.border ?? 'rgba(148, 163, 184, 0.35)'\n const cardBackground = colors.card ?? background\n const cardForeground = colors.cardForeground ?? foreground\n const primary = colors.primary ?? foreground\n const primaryForeground = colors.primaryForeground ?? background\n const secondary = colors.secondary ?? '#f4f4f5'\n const secondaryForeground = colors.secondaryForeground ?? foreground\n const muted = colors.muted ?? secondary\n const mutedForeground = colors.mutedForeground ?? secondaryForeground\n const accent = colors.accent ?? secondary\n const accentForeground = colors.accentForeground ?? secondaryForeground\n const headingFont = typography.headingFont\n const bodyFont = typography.bodyFont\n const baseFontSize = typography.baseFontSize\n const lineHeight = typography.lineHeight\n\n return (\n <div\n style={{\n display: 'grid',\n gap: '16px',\n padding: '18px',\n borderRadius: '12px',\n backgroundColor: background,\n color: foreground,\n border: `1px solid ${borderColor}`,\n boxShadow: '0 8px 20px rgba(15, 23, 42, 0.08)',\n fontFamily: bodyFont,\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n fontSize: '12px',\n fontWeight: 600,\n letterSpacing: '0.08em',\n textTransform: 'uppercase',\n opacity: 0.7,\n }}\n >\n <span>\n {icon} {title}\n </span>\n <span style={{ fontFamily: 'monospace', fontSize: '11px' }}>\n {background.toUpperCase()}\n </span>\n </div>\n\n <div\n style={{\n backgroundColor: cardBackground,\n color: cardForeground,\n borderRadius: radius.card,\n border: `1px solid ${borderColor}`,\n padding: '16px',\n display: 'flex',\n gap: '12px',\n flexWrap: 'wrap',\n }}\n >\n <button\n type=\"button\"\n style={{\n padding: '10px 18px',\n borderRadius: radius.pill,\n border: 'none',\n cursor: 'pointer',\n fontSize: '12px',\n fontWeight: 600,\n letterSpacing: '0.02em',\n backgroundColor: primary,\n color: primaryForeground,\n boxShadow: '0 6px 14px rgba(15, 23, 42, 0.15)',\n }}\n >\n Primary Action\n </button>\n <button\n type=\"button\"\n style={{\n padding: '10px 18px',\n borderRadius: radius.chip,\n border: '1px solid transparent',\n cursor: 'pointer',\n fontSize: '12px',\n fontWeight: 600,\n letterSpacing: '0.02em',\n backgroundColor: secondary,\n color: secondaryForeground,\n boxShadow: 'inset 0 0 0 1px rgba(15, 23, 42, 0.08)',\n }}\n >\n Secondary\n </button>\n </div>\n\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))',\n gap: '10px',\n }}\n >\n <div\n style={{\n borderRadius: radius.chip,\n padding: '10px 12px',\n fontSize: '12px',\n fontWeight: 500,\n backgroundColor: muted,\n color: mutedForeground,\n }}\n >\n Muted Card\n </div>\n <div\n style={{\n borderRadius: radius.chip,\n padding: '10px 12px',\n fontSize: '12px',\n fontWeight: 500,\n backgroundColor: accent,\n color: accentForeground,\n }}\n >\n Accent Tag\n </div>\n </div>\n\n <div\n style={{\n display: 'grid',\n gap: '8px',\n borderRadius: '10px',\n border: `1px dashed ${borderColor}`,\n padding: '14px',\n backgroundColor: 'rgba(148, 163, 184, 0.08)',\n }}\n >\n <div\n style={{\n fontFamily: headingFont,\n fontSize: 'clamp(24px, 3vw, 32px)',\n fontWeight: 600,\n lineHeight: 1.2,\n }}\n >\n Nadpis / Heading preview\n </div>\n <p\n style={{\n margin: 0,\n fontFamily: bodyFont,\n fontSize: baseFontSize,\n lineHeight,\n color: foreground,\n opacity: 0.85,\n }}\n >\n Sphinx of black quartz, judge my vow. Příliš žluťoučký kůň úpěl ďábelské ódy.\n </p>\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '10px',\n fontSize: '11px',\n color: 'rgba(15, 23, 42, 0.7)',\n }}\n >\n <span>Body: {typography.bodyLabel}</span>\n <span>Heading: {typography.headingLabel}</span>\n <span>Base size: {baseFontSize}</span>\n <span>Line height: {lineHeight}</span>\n </div>\n </div>\n </div>\n )\n}\n\nexport default function ThemePreviewField(props: SelectFieldClientProps) {\n const { field, path } = props\n const { value: selectedTheme, setValue } = useField<string>({ path })\n const { dispatchFields } = useForm()\n const formFields = useFormFields(([formState]) => formState)\n const hasAppliedInitialPresetRef = useRef(false)\n\n const applyPreset = useCallback(\n (presetName: string) => {\n const preset = themePresets[presetName]\n if (!preset) {\n return\n }\n\n colorKeys.forEach((key) => {\n const lightValue = preset.lightMode[key]\n const darkValue = preset.darkMode[key]\n\n if (lightValue) {\n dispatchFields({\n type: 'UPDATE',\n path: `themeConfiguration.lightMode.${key}`,\n value: lightValue,\n })\n }\n\n if (darkValue) {\n dispatchFields({\n type: 'UPDATE',\n path: `themeConfiguration.darkMode.${key}`,\n value: darkValue,\n })\n }\n })\n },\n [dispatchFields],\n )\n\n const handleThemeSelect = useCallback(\n (value: string) => {\n setValue(value)\n\n if (!value) {\n return\n }\n\n hasAppliedInitialPresetRef.current = true\n applyPreset(value)\n },\n [applyPreset, setValue],\n )\n\n useEffect(() => {\n if (!selectedTheme) {\n return\n }\n\n if (themePresets[selectedTheme]) {\n return\n }\n\n hasAppliedInitialPresetRef.current = false\n setValue(FALLBACK_THEME)\n }, [selectedTheme, setValue])\n\n useEffect(() => {\n if (hasAppliedInitialPresetRef.current) {\n return\n }\n\n if (!selectedTheme) {\n return\n }\n\n if (!formFields) {\n return\n }\n\n const preset = themePresets[selectedTheme]\n if (!preset) {\n hasAppliedInitialPresetRef.current = true\n return\n }\n\n const hasExistingValues = colorKeys.some((key) => {\n const lightField = formFields[`themeConfiguration.lightMode.${key}`]\n const darkField = formFields[`themeConfiguration.darkMode.${key}`]\n\n const lightValue = typeof lightField?.value === 'string' && lightField.value.trim().length > 0\n const darkValue = typeof darkField?.value === 'string' && darkField.value.trim().length > 0\n\n return lightValue || darkValue\n })\n\n if (hasExistingValues) {\n hasAppliedInitialPresetRef.current = true\n return\n }\n\n applyPreset(selectedTheme)\n hasAppliedInitialPresetRef.current = true\n }, [selectedTheme, formFields, applyPreset])\n\n const lightModeColors = useMemo(() => {\n const presetLightMode = selectedTheme ? themePresets[selectedTheme]?.lightMode : undefined\n\n return colorKeys.reduce<ColorModeColors>((acc, key) => {\n const fieldState = formFields?.[`themeConfiguration.lightMode.${key}`]\n const rawValue =\n typeof fieldState?.value === 'string' && fieldState.value.trim().length > 0\n ? fieldState.value\n : undefined\n\n acc[key] = rawValue ?? presetLightMode?.[key] ?? lightModeDefaults[key]\n return acc\n }, {} as ColorModeColors)\n }, [formFields, selectedTheme])\n\n const darkModeColors = useMemo(() => {\n const presetDarkMode = selectedTheme ? themePresets[selectedTheme]?.darkMode : undefined\n\n return colorKeys.reduce<ColorModeColors>((acc, key) => {\n const fieldState = formFields?.[`themeConfiguration.darkMode.${key}`]\n const rawValue =\n typeof fieldState?.value === 'string' && fieldState.value.trim().length > 0\n ? fieldState.value\n : undefined\n\n acc[key] = rawValue ?? presetDarkMode?.[key] ?? darkModeDefaults[key]\n return acc\n }, {} as ColorModeColors)\n }, [formFields, selectedTheme])\n\n const activePreset = selectedTheme ? themePresets[selectedTheme] : undefined\n const formTypographySelection = useMemo<TypographySelection | null>(() => {\n if (!formFields) {\n return null\n }\n\n const getValue = (key: string) => formFields[`themeConfiguration.typography.${key}`]?.value\n\n return {\n bodyFont: (getValue('bodyFont') as string | undefined) ?? undefined,\n headingFont: (getValue('headingFont') as string | undefined) ?? undefined,\n bodyFontCustom: (getValue('bodyFontCustom') as string | undefined) ?? undefined,\n headingFontCustom: (getValue('headingFontCustom') as string | undefined) ?? undefined,\n baseFontSize: (getValue('baseFontSize') as string | undefined) ?? undefined,\n lineHeight: (getValue('lineHeight') as string | undefined) ?? undefined,\n }\n }, [formFields])\n\n const previewTypography = useMemo<ResolvedTypographyPreview>(() => {\n return resolveTypographyPreview(formTypographySelection, selectedTheme)\n }, [formTypographySelection, selectedTheme])\n\n const borderRadiusValue = formFields?.['themeConfiguration.borderRadius']?.value\n const presetRadius = activePreset?.borderRadius ?? 'medium'\n const resolvedBorderRadius = (\n typeof borderRadiusValue === 'string' && borderRadiusValue.length\n ? borderRadiusValue\n : presetRadius\n ) as keyof typeof borderRadiusPresets\n const borderRadiusPreset = borderRadiusPresets[resolvedBorderRadius] ?? borderRadiusPresets.medium\n const borderRadiusCSS =\n typeof borderRadiusPreset.css === 'string'\n ? { '--radius-default': borderRadiusPreset.css }\n : (borderRadiusPreset.css as Record<string, string>)\n const previewRadius = {\n card: borderRadiusCSS['--radius-large'] ?? '1rem',\n chip: borderRadiusCSS['--radius-default'] ?? '0.75rem',\n pill: borderRadiusCSS['--radius-xl'] ?? borderRadiusCSS['--radius-large'] ?? '999px',\n }\n\n const fieldLabel =\n typeof field.label === 'string' ? field.label : field.label?.en || field.label?.cs || 'Theme'\n\n return (\n <div style={{ marginBottom: '24px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '10px',\n fontSize: '13px',\n fontWeight: 600,\n color: 'var(--theme-elevation-800)',\n }}\n >\n {fieldLabel}\n </label>\n\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '24px',\n alignItems: 'flex-start',\n }}\n >\n <div\n style={{\n flex: '0 0 320px',\n minWidth: '260px',\n maxWidth: '420px',\n display: 'grid',\n gap: '12px',\n }}\n >\n <div className=\"theme-preset-list\">\n {Object.entries(themePresets).map(([key, preset]) => {\n const isSelected = key === selectedTheme\n const swatches = highlightSwatches\n .map(({ key: colorKey }) => preset.lightMode[colorKey])\n .filter((color): color is string => Boolean(color))\n\n return (\n <button\n key={key}\n type=\"button\"\n onClick={() => handleThemeSelect(key)}\n className={`theme-preset-button ${isSelected ? 'selected' : ''}`}\n >\n <div className=\"theme-info\">\n <span className=\"theme-label\">{preset.label}</span>\n <span className=\"theme-name\">{key}</span>\n </div>\n <div className=\"theme-swatches\" aria-hidden=\"true\">\n {swatches.map((color, index) => (\n <span\n key={`${key}-swatch-${color}-${index}`}\n className=\"color-swatch\"\n style={{ backgroundColor: color }}\n />\n ))}\n </div>\n </button>\n )\n })}\n </div>\n\n {activePreset && (\n <div\n style={{\n padding: '12px',\n borderRadius: '10px',\n border: '1px solid var(--theme-elevation-200)',\n backgroundColor: 'var(--theme-elevation-50)',\n display: 'grid',\n gap: '8px',\n fontSize: '12px',\n color: 'var(--theme-elevation-600)',\n }}\n >\n <div style={{ fontWeight: 600, color: 'var(--theme-elevation-800)' }}>\n {activePreset.label}\n </div>\n <div>Primary color: {activePreset.lightMode.primary}</div>\n <div>Accent color: {activePreset.lightMode.accent}</div>\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '12px',\n marginTop: '4px',\n }}\n >\n {highlightSwatches.map(({ key, label }) => {\n const swatchValue = activePreset.lightMode[key]\n\n if (!swatchValue) {\n return null\n }\n\n return (\n <div\n key={key}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n minWidth: 'fit-content',\n }}\n >\n <span\n style={{\n width: '22px',\n height: '22px',\n borderRadius: '999px',\n border: '1px solid rgba(15, 23, 42, 0.12)',\n backgroundColor: swatchValue,\n boxShadow: 'inset 0 1px 2px rgba(15, 23, 42, 0.08)',\n }}\n aria-hidden=\"true\"\n />\n <span style={{ fontSize: '11px', color: 'var(--theme-elevation-700)' }}>\n {label}\n </span>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {field.admin?.description && (\n <div\n style={{\n fontSize: '12px',\n color: 'var(--theme-elevation-600)',\n lineHeight: 1.5,\n }}\n >\n {typeof field.admin.description === 'string'\n ? field.admin.description\n : field.admin.description?.en || field.admin.description?.cs || ''}\n </div>\n )}\n </div>\n\n <div\n style={{\n flex: '1 1 420px',\n minWidth: '320px',\n position: 'sticky',\n top: '96px',\n alignSelf: 'flex-start',\n }}\n >\n <div\n style={{\n width: '100%',\n borderRadius: '16px',\n border: '1px solid var(--theme-elevation-200)',\n backgroundColor: 'var(--theme-elevation-25)',\n padding: '20px',\n display: 'grid',\n gap: '18px',\n maxHeight: 'calc(100vh - 140px)',\n overflowY: 'auto',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n fontWeight: 600,\n fontSize: '13px',\n color: 'var(--theme-elevation-800)',\n }}\n >\n <span>🎨 Live Preview</span>\n <span style={{ fontSize: '12px', color: 'var(--theme-elevation-600)' }}>\n {activePreset ? activePreset.label : 'Custom palette'}\n </span>\n </div>\n\n <div\n style={{\n display: 'grid',\n gap: '16px',\n }}\n >\n <ModePreview\n icon=\"☀️\"\n title=\"Light Mode\"\n colors={lightModeColors}\n typography={previewTypography}\n radius={previewRadius}\n />\n <ModePreview\n icon=\"🌙\"\n title=\"Dark Mode\"\n colors={darkModeColors}\n typography={previewTypography}\n radius={previewRadius}\n />\n </div>\n </div>\n </div>\n </div>\n </div>\n )\n}\n"],"names":["useField","useForm","useFormFields","useCallback","useEffect","useMemo","useRef","resolveTypographyPreview","allThemePresets","borderRadiusPresets","darkModeDefaults","lightModeDefaults","FALLBACK_THEME","name","themePresets","reduce","acc","preset","label","lightMode","darkMode","typography","borderRadius","colorKeys","Object","keys","highlightSwatches","key","ModePreview","icon","title","colors","radius","background","foreground","borderColor","border","cardBackground","card","cardForeground","primary","primaryForeground","secondary","secondaryForeground","muted","mutedForeground","accent","accentForeground","headingFont","bodyFont","baseFontSize","lineHeight","div","style","display","gap","padding","backgroundColor","color","boxShadow","fontFamily","alignItems","justifyContent","fontSize","fontWeight","letterSpacing","textTransform","opacity","span","toUpperCase","flexWrap","button","type","pill","cursor","chip","gridTemplateColumns","p","margin","bodyLabel","headingLabel","ThemePreviewField","props","field","path","value","selectedTheme","setValue","dispatchFields","formFields","formState","hasAppliedInitialPresetRef","applyPreset","presetName","forEach","lightValue","darkValue","handleThemeSelect","current","hasExistingValues","some","lightField","darkField","trim","length","lightModeColors","presetLightMode","undefined","fieldState","rawValue","darkModeColors","presetDarkMode","activePreset","formTypographySelection","getValue","bodyFontCustom","headingFontCustom","previewTypography","borderRadiusValue","presetRadius","resolvedBorderRadius","borderRadiusPreset","medium","borderRadiusCSS","css","previewRadius","fieldLabel","en","cs","marginBottom","flex","minWidth","maxWidth","className","entries","map","isSelected","swatches","colorKey","filter","Boolean","onClick","aria-hidden","index","marginTop","swatchValue","width","height","admin","description","position","top","alignSelf","maxHeight","overflowY"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/fields/ThemePreviewField.tsx"],"sourcesContent":["'use client'\n\nimport './ThemePreviewField.css'\nimport { useField, useForm, useFormFields } from '@payloadcms/ui'\nimport type { SelectFieldClientProps } from 'payload'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\nimport type {\n ResolvedTypographyPreview,\n TypographySelection,\n} from '../components/typographyPreviewUtils.js'\nimport { resolveTypographyPreview } from '../components/typographyPreviewUtils.js'\nimport { allThemePresets } from '../index.js'\nimport type { ThemePreset, ThemeTypographyPreset } from '../presets.js'\nimport { borderRadiusPresets } from '../providers/Theme/themeConfig.js'\nimport { darkModeDefaults, lightModeDefaults } from './colorModeFields.js'\n\ninterface ColorModeColors {\n background?: string\n foreground?: string\n card?: string\n cardForeground?: string\n popover?: string\n popoverForeground?: string\n primary?: string\n primaryForeground?: string\n secondary?: string\n secondaryForeground?: string\n muted?: string\n mutedForeground?: string\n accent?: string\n accentForeground?: string\n destructive?: string\n destructiveForeground?: string\n border?: string\n input?: string\n ring?: string\n}\n\ninterface ThemePresetDefinition {\n label: string\n lightMode: ColorModeColors\n darkMode: ColorModeColors\n typography?: ThemeTypographyPreset\n borderRadius: 'none' | 'small' | 'medium' | 'large' | 'xl'\n}\n\n// FALLBACK_THEME and runtime theme presets are computed inside the component\n\nconst colorKeys = Object.keys(lightModeDefaults) as (keyof ColorModeColors)[]\n\nconst highlightSwatches: Array<{ key: keyof ColorModeColors; label: string }> = [\n { key: 'primary', label: 'Primary' },\n { key: 'secondary', label: 'Secondary' },\n { key: 'accent', label: 'Accent' },\n { key: 'background', label: 'Background' },\n { key: 'foreground', label: 'Foreground' },\n]\n\ninterface ModePreviewProps {\n icon: string\n title: string\n colors: ColorModeColors\n typography: ResolvedTypographyPreview\n radius: {\n card: string\n chip: string\n pill: string\n }\n}\n\nfunction ModePreview({ icon, title, colors, typography, radius }: Readonly<ModePreviewProps>) {\n const background = colors.background ?? '#ffffff'\n const foreground = colors.foreground ?? '#0a0a0a'\n const borderColor = colors.border ?? 'rgba(148, 163, 184, 0.35)'\n const cardBackground = colors.card ?? background\n const cardForeground = colors.cardForeground ?? foreground\n const primary = colors.primary ?? foreground\n const primaryForeground = colors.primaryForeground ?? background\n const secondary = colors.secondary ?? '#f4f4f5'\n const secondaryForeground = colors.secondaryForeground ?? foreground\n const muted = colors.muted ?? secondary\n const mutedForeground = colors.mutedForeground ?? secondaryForeground\n const accent = colors.accent ?? secondary\n const accentForeground = colors.accentForeground ?? secondaryForeground\n const headingFont = typography.headingFont\n const bodyFont = typography.bodyFont\n const baseFontSize = typography.baseFontSize\n const lineHeight = typography.lineHeight\n\n return (\n <div\n style={{\n display: 'grid',\n gap: '16px',\n padding: '18px',\n borderRadius: '12px',\n backgroundColor: background,\n color: foreground,\n border: `1px solid ${borderColor}`,\n boxShadow: '0 8px 20px rgba(15, 23, 42, 0.08)',\n fontFamily: bodyFont,\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n fontSize: '12px',\n fontWeight: 600,\n letterSpacing: '0.08em',\n textTransform: 'uppercase',\n opacity: 0.7,\n }}\n >\n <span>\n {icon} {title}\n </span>\n <span style={{ fontFamily: 'monospace', fontSize: '11px' }}>\n {background.toUpperCase()}\n </span>\n </div>\n\n <div\n style={{\n backgroundColor: cardBackground,\n color: cardForeground,\n borderRadius: radius.card,\n border: `1px solid ${borderColor}`,\n padding: '16px',\n display: 'flex',\n gap: '12px',\n flexWrap: 'wrap',\n }}\n >\n <button\n type=\"button\"\n style={{\n padding: '10px 18px',\n borderRadius: radius.pill,\n border: 'none',\n cursor: 'pointer',\n fontSize: '12px',\n fontWeight: 600,\n letterSpacing: '0.02em',\n backgroundColor: primary,\n color: primaryForeground,\n boxShadow: '0 6px 14px rgba(15, 23, 42, 0.15)',\n }}\n >\n Primary Action\n </button>\n <button\n type=\"button\"\n style={{\n padding: '10px 18px',\n borderRadius: radius.chip,\n border: '1px solid transparent',\n cursor: 'pointer',\n fontSize: '12px',\n fontWeight: 600,\n letterSpacing: '0.02em',\n backgroundColor: secondary,\n color: secondaryForeground,\n boxShadow: 'inset 0 0 0 1px rgba(15, 23, 42, 0.08)',\n }}\n >\n Secondary\n </button>\n </div>\n\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))',\n gap: '10px',\n }}\n >\n <div\n style={{\n borderRadius: radius.chip,\n padding: '10px 12px',\n fontSize: '12px',\n fontWeight: 500,\n backgroundColor: muted,\n color: mutedForeground,\n }}\n >\n Muted Card\n </div>\n <div\n style={{\n borderRadius: radius.chip,\n padding: '10px 12px',\n fontSize: '12px',\n fontWeight: 500,\n backgroundColor: accent,\n color: accentForeground,\n }}\n >\n Accent Tag\n </div>\n </div>\n\n <div\n style={{\n display: 'grid',\n gap: '8px',\n borderRadius: '10px',\n border: `1px dashed ${borderColor}`,\n padding: '14px',\n backgroundColor: 'rgba(148, 163, 184, 0.08)',\n }}\n >\n <div\n style={{\n fontFamily: headingFont,\n fontSize: 'clamp(24px, 3vw, 32px)',\n fontWeight: 600,\n lineHeight: 1.2,\n }}\n >\n Nadpis / Heading preview\n </div>\n <p\n style={{\n margin: 0,\n fontFamily: bodyFont,\n fontSize: baseFontSize,\n lineHeight,\n color: foreground,\n opacity: 0.85,\n }}\n >\n Sphinx of black quartz, judge my vow. Příliš žluťoučký kůň úpěl ďábelské ódy.\n </p>\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '10px',\n fontSize: '11px',\n color: 'rgba(15, 23, 42, 0.7)',\n }}\n >\n <span>Body: {typography.bodyLabel}</span>\n <span>Heading: {typography.headingLabel}</span>\n <span>Base size: {baseFontSize}</span>\n <span>Line height: {lineHeight}</span>\n </div>\n </div>\n </div>\n )\n}\n\nexport default function ThemePreviewField(props: SelectFieldClientProps) {\n const { field, path } = props\n // If the plugin provided theme presets via admin config, prefer those; otherwise fallback to allThemePresets\n const baseThemePresets =\n (field?.admin as unknown as { themePresets?: ThemePreset[] })?.themePresets ?? allThemePresets\n const runtimeThemePresets = useMemo(() => {\n return (baseThemePresets as ThemePreset[]).reduce<Record<string, ThemePresetDefinition>>(\n (acc, preset) => {\n acc[preset.name] = {\n label: preset.label,\n lightMode: { ...lightModeDefaults, ...(preset.lightMode ?? {}) },\n darkMode: { ...darkModeDefaults, ...(preset.darkMode ?? {}) },\n typography: preset.typography,\n borderRadius: preset.borderRadius,\n }\n return acc\n },\n {},\n )\n }, [baseThemePresets])\n const fallbackTheme = baseThemePresets[0]?.name ?? 'cool'\n const { value: selectedTheme, setValue } = useField<string>({ path })\n const { dispatchFields } = useForm()\n const formFields = useFormFields(([formState]) => formState)\n const hasAppliedInitialPresetRef = useRef(false)\n\n const applyPreset = useCallback(\n (presetName: string) => {\n const preset = runtimeThemePresets[presetName]\n if (!preset) {\n return\n }\n\n colorKeys.forEach((key) => {\n const lightValue = preset.lightMode[key]\n const darkValue = preset.darkMode[key]\n\n if (lightValue) {\n dispatchFields({\n type: 'UPDATE',\n path: `themeConfiguration.lightMode.${key}`,\n value: lightValue,\n })\n }\n\n if (darkValue) {\n dispatchFields({\n type: 'UPDATE',\n path: `themeConfiguration.darkMode.${key}`,\n value: darkValue,\n })\n }\n })\n },\n [dispatchFields],\n )\n\n const handleThemeSelect = useCallback(\n (value: string) => {\n setValue(value)\n\n if (!value) {\n return\n }\n\n hasAppliedInitialPresetRef.current = true\n applyPreset(value)\n },\n [applyPreset, setValue],\n )\n\n useEffect(() => {\n if (!selectedTheme) {\n return\n }\n\n if (runtimeThemePresets[selectedTheme]) {\n return\n }\n\n hasAppliedInitialPresetRef.current = false\n setValue(fallbackTheme)\n }, [selectedTheme, setValue])\n\n useEffect(() => {\n if (hasAppliedInitialPresetRef.current) {\n return\n }\n\n if (!selectedTheme) {\n return\n }\n\n if (!formFields) {\n return\n }\n\n const preset = runtimeThemePresets[selectedTheme]\n if (!preset) {\n hasAppliedInitialPresetRef.current = true\n return\n }\n\n const hasExistingValues = colorKeys.some((key) => {\n const lightField = formFields[`themeConfiguration.lightMode.${key}`]\n const darkField = formFields[`themeConfiguration.darkMode.${key}`]\n\n const lightValue = typeof lightField?.value === 'string' && lightField.value.trim().length > 0\n const darkValue = typeof darkField?.value === 'string' && darkField.value.trim().length > 0\n\n return lightValue || darkValue\n })\n\n if (hasExistingValues) {\n hasAppliedInitialPresetRef.current = true\n return\n }\n\n applyPreset(selectedTheme)\n hasAppliedInitialPresetRef.current = true\n }, [selectedTheme, formFields, applyPreset])\n\n const lightModeColors = useMemo(() => {\n const presetLightMode = selectedTheme\n ? runtimeThemePresets[selectedTheme]?.lightMode\n : undefined\n\n return colorKeys.reduce<ColorModeColors>((acc, key) => {\n const fieldState = formFields?.[`themeConfiguration.lightMode.${key}`]\n const rawValue =\n typeof fieldState?.value === 'string' && fieldState.value.trim().length > 0\n ? fieldState.value\n : undefined\n\n acc[key] = rawValue ?? presetLightMode?.[key] ?? lightModeDefaults[key]\n return acc\n }, {} as ColorModeColors)\n }, [formFields, selectedTheme])\n\n const darkModeColors = useMemo(() => {\n const presetDarkMode = selectedTheme ? runtimeThemePresets[selectedTheme]?.darkMode : undefined\n\n return colorKeys.reduce<ColorModeColors>((acc, key) => {\n const fieldState = formFields?.[`themeConfiguration.darkMode.${key}`]\n const rawValue =\n typeof fieldState?.value === 'string' && fieldState.value.trim().length > 0\n ? fieldState.value\n : undefined\n\n acc[key] = rawValue ?? presetDarkMode?.[key] ?? darkModeDefaults[key]\n return acc\n }, {} as ColorModeColors)\n }, [formFields, selectedTheme])\n\n const activePreset = selectedTheme ? runtimeThemePresets[selectedTheme] : undefined\n const formTypographySelection = useMemo<TypographySelection | null>(() => {\n if (!formFields) {\n return null\n }\n\n const getValue = (key: string) => formFields[`themeConfiguration.typography.${key}`]?.value\n\n return {\n bodyFont: (getValue('bodyFont') as string | undefined) ?? undefined,\n headingFont: (getValue('headingFont') as string | undefined) ?? undefined,\n bodyFontCustom: (getValue('bodyFontCustom') as string | undefined) ?? undefined,\n headingFontCustom: (getValue('headingFontCustom') as string | undefined) ?? undefined,\n baseFontSize: (getValue('baseFontSize') as string | undefined) ?? undefined,\n lineHeight: (getValue('lineHeight') as string | undefined) ?? undefined,\n }\n }, [formFields])\n\n const previewTypography = useMemo<ResolvedTypographyPreview>(() => {\n return resolveTypographyPreview(formTypographySelection, selectedTheme)\n }, [formTypographySelection, selectedTheme])\n\n const borderRadiusValue = formFields?.['themeConfiguration.borderRadius']?.value\n const presetRadius = activePreset?.borderRadius ?? 'medium'\n const resolvedBorderRadius = (\n typeof borderRadiusValue === 'string' && borderRadiusValue.length\n ? borderRadiusValue\n : presetRadius\n ) as keyof typeof borderRadiusPresets\n const borderRadiusPreset = borderRadiusPresets[resolvedBorderRadius] ?? borderRadiusPresets.medium\n const borderRadiusCSS =\n typeof borderRadiusPreset.css === 'string'\n ? { '--radius-default': borderRadiusPreset.css }\n : (borderRadiusPreset.css as Record<string, string>)\n const previewRadius = {\n card: borderRadiusCSS['--radius-large'] ?? '1rem',\n chip: borderRadiusCSS['--radius-default'] ?? '0.75rem',\n pill: borderRadiusCSS['--radius-xl'] ?? borderRadiusCSS['--radius-large'] ?? '999px',\n }\n\n const fieldLabel =\n typeof field.label === 'string' ? field.label : field.label?.en || field.label?.cs || 'Theme'\n\n return (\n <div style={{ marginBottom: '24px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '10px',\n fontSize: '13px',\n fontWeight: 600,\n color: 'var(--theme-elevation-800)',\n }}\n >\n {fieldLabel}\n </label>\n\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '24px',\n alignItems: 'flex-start',\n }}\n >\n <div\n style={{\n flex: '0 0 320px',\n minWidth: '260px',\n maxWidth: '420px',\n display: 'grid',\n gap: '12px',\n }}\n >\n <div className=\"theme-preset-list\">\n {Object.entries(runtimeThemePresets).map(([key, preset]) => {\n const isSelected = key === selectedTheme\n const swatches = highlightSwatches\n .map(({ key: colorKey }) => preset.lightMode[colorKey])\n .filter((color): color is string => Boolean(color))\n\n return (\n <button\n key={key}\n type=\"button\"\n onClick={() => handleThemeSelect(key)}\n className={`theme-preset-button ${isSelected ? 'selected' : ''}`}\n >\n <div className=\"theme-info\">\n <span className=\"theme-label\">{preset.label}</span>\n <span className=\"theme-name\">{key}</span>\n </div>\n <div className=\"theme-swatches\" aria-hidden=\"true\">\n {swatches.map((color, index) => (\n <span\n key={`${key}-swatch-${color}-${index}`}\n className=\"color-swatch\"\n style={{ backgroundColor: color }}\n />\n ))}\n </div>\n </button>\n )\n })}\n </div>\n\n {activePreset && (\n <div\n style={{\n padding: '12px',\n borderRadius: '10px',\n border: '1px solid var(--theme-elevation-200)',\n backgroundColor: 'var(--theme-elevation-50)',\n display: 'grid',\n gap: '8px',\n fontSize: '12px',\n color: 'var(--theme-elevation-600)',\n }}\n >\n <div style={{ fontWeight: 600, color: 'var(--theme-elevation-800)' }}>\n {activePreset.label}\n </div>\n <div>Primary color: {activePreset.lightMode.primary}</div>\n <div>Accent color: {activePreset.lightMode.accent}</div>\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '12px',\n marginTop: '4px',\n }}\n >\n {highlightSwatches.map(({ key, label }) => {\n const swatchValue = activePreset.lightMode[key]\n\n if (!swatchValue) {\n return null\n }\n\n return (\n <div\n key={key}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n minWidth: 'fit-content',\n }}\n >\n <span\n style={{\n width: '22px',\n height: '22px',\n borderRadius: '999px',\n border: '1px solid rgba(15, 23, 42, 0.12)',\n backgroundColor: swatchValue,\n boxShadow: 'inset 0 1px 2px rgba(15, 23, 42, 0.08)',\n }}\n aria-hidden=\"true\"\n />\n <span style={{ fontSize: '11px', color: 'var(--theme-elevation-700)' }}>\n {label}\n </span>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {field.admin?.description && (\n <div\n style={{\n fontSize: '12px',\n color: 'var(--theme-elevation-600)',\n lineHeight: 1.5,\n }}\n >\n {typeof field.admin.description === 'string'\n ? field.admin.description\n : field.admin.description?.en || field.admin.description?.cs || ''}\n </div>\n )}\n </div>\n\n <div\n style={{\n flex: '1 1 420px',\n minWidth: '320px',\n position: 'sticky',\n top: '96px',\n alignSelf: 'flex-start',\n }}\n >\n <div\n style={{\n width: '100%',\n borderRadius: '16px',\n border: '1px solid var(--theme-elevation-200)',\n backgroundColor: 'var(--theme-elevation-25)',\n padding: '20px',\n display: 'grid',\n gap: '18px',\n maxHeight: 'calc(100vh - 140px)',\n overflowY: 'auto',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n fontWeight: 600,\n fontSize: '13px',\n color: 'var(--theme-elevation-800)',\n }}\n >\n <span>🎨 Live Preview</span>\n <span style={{ fontSize: '12px', color: 'var(--theme-elevation-600)' }}>\n {activePreset ? activePreset.label : 'Custom palette'}\n </span>\n </div>\n\n <div\n style={{\n display: 'grid',\n gap: '16px',\n }}\n >\n <ModePreview\n icon=\"☀️\"\n title=\"Light Mode\"\n colors={lightModeColors}\n typography={previewTypography}\n radius={previewRadius}\n />\n <ModePreview\n icon=\"🌙\"\n title=\"Dark Mode\"\n colors={darkModeColors}\n typography={previewTypography}\n radius={previewRadius}\n />\n </div>\n </div>\n </div>\n </div>\n </div>\n )\n}\n"],"names":["useField","useForm","useFormFields","useCallback","useEffect","useMemo","useRef","resolveTypographyPreview","allThemePresets","borderRadiusPresets","darkModeDefaults","lightModeDefaults","colorKeys","Object","keys","highlightSwatches","key","label","ModePreview","icon","title","colors","typography","radius","background","foreground","borderColor","border","cardBackground","card","cardForeground","primary","primaryForeground","secondary","secondaryForeground","muted","mutedForeground","accent","accentForeground","headingFont","bodyFont","baseFontSize","lineHeight","div","style","display","gap","padding","borderRadius","backgroundColor","color","boxShadow","fontFamily","alignItems","justifyContent","fontSize","fontWeight","letterSpacing","textTransform","opacity","span","toUpperCase","flexWrap","button","type","pill","cursor","chip","gridTemplateColumns","p","margin","bodyLabel","headingLabel","ThemePreviewField","props","field","path","baseThemePresets","admin","themePresets","runtimeThemePresets","reduce","acc","preset","name","lightMode","darkMode","fallbackTheme","value","selectedTheme","setValue","dispatchFields","formFields","formState","hasAppliedInitialPresetRef","applyPreset","presetName","forEach","lightValue","darkValue","handleThemeSelect","current","hasExistingValues","some","lightField","darkField","trim","length","lightModeColors","presetLightMode","undefined","fieldState","rawValue","darkModeColors","presetDarkMode","activePreset","formTypographySelection","getValue","bodyFontCustom","headingFontCustom","previewTypography","borderRadiusValue","presetRadius","resolvedBorderRadius","borderRadiusPreset","medium","borderRadiusCSS","css","previewRadius","fieldLabel","en","cs","marginBottom","flex","minWidth","maxWidth","className","entries","map","isSelected","swatches","colorKey","filter","Boolean","onClick","aria-hidden","index","marginTop","swatchValue","width","height","description","position","top","alignSelf","maxHeight","overflowY"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"themeConfigurationField.d.ts","sourceRoot":"","sources":["../../src/fields/themeConfigurationField.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,SAAS,EAAE,MAAM,SAAS,CAAA;AAO/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAG9C,UAAU,8BAA8B;IACtC,YAAY,EAAE,WAAW,EAAE,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,sBAAsB,EAAE,OAAO,CAAA;IAC/B,gBAAgB,EAAE,OAAO,CAAA;IACzB,oBAAoB,EAAE,OAAO,CAAA;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAA;CACjC;AAGD,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7D;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,8BAA8B,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"themeConfigurationField.d.ts","sourceRoot":"","sources":["../../src/fields/themeConfigurationField.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,SAAS,EAAE,MAAM,SAAS,CAAA;AAO/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAG9C,UAAU,8BAA8B;IACtC,YAAY,EAAE,WAAW,EAAE,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,sBAAsB,EAAE,OAAO,CAAA;IAC/B,gBAAgB,EAAE,OAAO,CAAA;IACzB,oBAAoB,EAAE,OAAO,CAAA;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAA;CACjC;AAGD,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7D;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,8BAA8B,GAAG,QAAQ,CAsa/F"}
|
|
@@ -5,25 +5,22 @@ import { darkModeField, lightModeField } from './colorModeFields.js';
|
|
|
5
5
|
* Theme Selection populates lightMode/darkMode colors automatically
|
|
6
6
|
*/ export function createThemeConfigurationField(options) {
|
|
7
7
|
const { themePresets, defaultTheme, includeColorModeToggle, includeCustomCSS, enableAdvancedFeatures = true } = options;
|
|
8
|
-
const themeOptions = themePresets.map((preset)=>{
|
|
9
|
-
const previewColor = preset.preview?.colors.primary;
|
|
10
|
-
const label = previewColor ? `${preset.label} • ${previewColor}` : preset.label;
|
|
11
|
-
return {
|
|
12
|
-
label,
|
|
13
|
-
value: preset.name
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
8
|
const fields = [
|
|
17
9
|
{
|
|
18
10
|
name: 'theme',
|
|
19
|
-
type: '
|
|
11
|
+
type: 'text',
|
|
20
12
|
required: true,
|
|
21
13
|
defaultValue: defaultTheme,
|
|
22
|
-
options: themeOptions,
|
|
23
14
|
label: {
|
|
24
15
|
en: '🎨 Theme Selection',
|
|
25
16
|
cs: '🎨 Výběr tématu'
|
|
26
17
|
},
|
|
18
|
+
validate: (value)=>{
|
|
19
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return 'Select a theme';
|
|
23
|
+
},
|
|
27
24
|
admin: {
|
|
28
25
|
description: {
|
|
29
26
|
en: 'Select a theme to auto-populate color values. You can customize colors after selection.',
|
|
@@ -31,6 +28,9 @@ import { darkModeField, lightModeField } from './colorModeFields.js';
|
|
|
31
28
|
},
|
|
32
29
|
components: {
|
|
33
30
|
Field: '@kilivi/payloadcms-theme-management/fields/ThemePreviewField'
|
|
31
|
+
},
|
|
32
|
+
custom: {
|
|
33
|
+
themePresets
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -352,6 +352,40 @@ import { darkModeField, lightModeField } from './colorModeFields.js';
|
|
|
352
352
|
}
|
|
353
353
|
]
|
|
354
354
|
});
|
|
355
|
+
// Custom Theme Presets importer
|
|
356
|
+
fields.push({
|
|
357
|
+
type: 'collapsible',
|
|
358
|
+
label: {
|
|
359
|
+
en: '🧩 Custom Theme Presets',
|
|
360
|
+
cs: '🧩 Vlastní motivy'
|
|
361
|
+
},
|
|
362
|
+
admin: {
|
|
363
|
+
initCollapsed: true,
|
|
364
|
+
description: {
|
|
365
|
+
en: 'Import additional theme presets via JSON to extend or override the defaults. These presets become available instantly in the Theme Selection above.',
|
|
366
|
+
cs: 'Importujte další motivy pomocí JSONu a rozšiřte nebo přepište výchozí nabídku. Motivy budou ihned k dispozici ve výběru výše.'
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
fields: [
|
|
370
|
+
{
|
|
371
|
+
name: 'customThemePresets',
|
|
372
|
+
type: 'json',
|
|
373
|
+
label: {
|
|
374
|
+
en: 'Custom Preset JSON',
|
|
375
|
+
cs: 'JSON s vlastními motivy'
|
|
376
|
+
},
|
|
377
|
+
admin: {
|
|
378
|
+
description: {
|
|
379
|
+
en: 'Paste or import an array (or object map) of theme presets. Each entry should include at least a unique "name" and "label" with optional lightMode/darkMode colors.',
|
|
380
|
+
cs: 'Vložte nebo importujte pole (či objekt) motivů. Každý motiv musí mít unikátní „name“ a „label“ a volitelně barvy pro světly/tmavý režim.'
|
|
381
|
+
},
|
|
382
|
+
components: {
|
|
383
|
+
Field: '@kilivi/payloadcms-theme-management/fields/ThemePresetImportField'
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
]
|
|
388
|
+
});
|
|
355
389
|
// Advanced Settings (when using extended theme configuration)
|
|
356
390
|
if (enableAdvancedFeatures) {
|
|
357
391
|
const advancedFields = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/fields/themeConfigurationField.ts"],"sourcesContent":["import type { Field, TabsField } from 'payload'\nimport {\n BASE_FONT_SIZE_OPTIONS,\n BODY_FONT_OPTIONS,\n HEADING_FONT_OPTIONS,\n LINE_HEIGHT_OPTIONS,\n} from '../constants/themeFonts.js'\nimport type { ThemePreset } from '../index.js'\nimport { darkModeField, lightModeField } from './colorModeFields.js'\n\ninterface ThemeConfigurationFieldOptions {\n themePresets: ThemePreset[]\n defaultTheme: string\n includeColorModeToggle: boolean\n includeCustomCSS: boolean\n includeBrandIdentity: boolean\n enableAdvancedFeatures?: boolean\n}\n\n// Type for a single tab (not the full TabsField)\nexport type ThemeTab = NonNullable<TabsField['tabs']>[number]\n\n/**\n * Theme Configuration Field with Theme Selection as default value setter\n * Theme Selection populates lightMode/darkMode colors automatically\n */\nexport function createThemeConfigurationField(options: ThemeConfigurationFieldOptions): ThemeTab {\n const {\n themePresets,\n defaultTheme,\n includeColorModeToggle,\n includeCustomCSS,\n enableAdvancedFeatures = true,\n } = options\n\n const themeOptions = themePresets.map((preset) => {\n const previewColor = preset.preview?.colors.primary\n const label = previewColor ? `${preset.label} • ${previewColor}` : preset.label\n return {\n label,\n value: preset.name,\n }\n })\n\n const fields: Field[] = [\n {\n name: 'theme',\n type: 'select',\n required: true,\n defaultValue: defaultTheme,\n options: themeOptions,\n label: {\n en: '🎨 Theme Selection',\n cs: '🎨 Výběr tématu',\n },\n admin: {\n description: {\n en: 'Select a theme to auto-populate color values. You can customize colors after selection.',\n cs: 'Vyberte téma pro automatické předvyplnění barev. Po výběru můžete barvy upravit.',\n },\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/ThemePreviewField',\n },\n },\n },\n ]\n\n // Border Radius Configuration - Always visible\n fields.push({\n type: 'row',\n fields: [\n {\n name: 'borderRadius',\n type: 'select',\n label: {\n en: '📐 Border Radius',\n cs: '📐 Zaoblení rohů',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'None (0px)', cs: 'Žádné (0px)' }, value: 'none' },\n { label: { en: 'Small (0.25rem)', cs: 'Malé (0.25rem)' }, value: 'small' },\n { label: { en: 'Medium (0.5rem)', cs: 'Střední (0.5rem)' }, value: 'medium' },\n { label: { en: 'Large (0.75rem)', cs: 'Velké (0.75rem)' }, value: 'large' },\n { label: { en: 'XL (1rem)', cs: 'XL (1rem)' }, value: 'xl' },\n ],\n admin: {\n description: {\n en: 'Default corner rounding for UI elements',\n cs: 'Výchozí zaoblení rohů pro UI prvky',\n },\n width: '50%',\n },\n },\n {\n name: 'spacing',\n type: 'select',\n label: {\n en: '📏 Spacing Scale',\n cs: '📏 Škála rozestupů',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'Compact', cs: 'Kompaktní' }, value: 'compact' },\n { label: { en: 'Medium', cs: 'Střední' }, value: 'medium' },\n { label: { en: 'Comfortable', cs: 'Pohodlné' }, value: 'comfortable' },\n ],\n admin: {\n description: {\n en: 'Global spacing between elements',\n cs: 'Globální rozestupy mezi prvky',\n },\n width: '50%',\n },\n },\n ],\n })\n\n // Color Mode Settings\n if (includeColorModeToggle) {\n fields.push({\n type: 'collapsible',\n label: {\n en: '🌗 Color Mode Settings',\n cs: '🌗 Nastavení barevného režimu',\n },\n admin: {\n initCollapsed: false,\n description: {\n en: 'Configure light and dark mode colors. Changes here override theme selection.',\n cs: 'Nakonfigurujte barvy světlého a tmavého režimu. Změny zde přepíší výběr tématu.',\n },\n },\n fields: [\n {\n type: 'row',\n fields: [\n {\n name: 'colorMode',\n type: 'select',\n label: {\n en: 'Default Color Mode',\n cs: 'Výchozí barevný režim',\n },\n defaultValue: 'auto',\n options: [\n {\n label: {\n en: 'Auto (System)',\n cs: 'Automaticky (Systém)',\n },\n value: 'auto',\n },\n {\n label: { en: 'Light', cs: 'Světlý' },\n value: 'light',\n },\n {\n label: { en: 'Dark', cs: 'Tmavý' },\n value: 'dark',\n },\n ],\n admin: {\n description: {\n en: 'Default color mode for visitors',\n cs: 'Výchozí barevný režim pro návštěvníky',\n },\n width: '50%',\n },\n },\n {\n name: 'allowColorModeToggle',\n type: 'checkbox',\n label: {\n en: 'Enable Mode Toggle',\n cs: 'Povolit přepínač režimu',\n },\n defaultValue: true,\n admin: {\n description: {\n en: 'Let visitors switch modes',\n cs: 'Umožnit návštěvníkům přepínat režimy',\n },\n width: '50%',\n },\n },\n ],\n },\n lightModeField,\n darkModeField,\n ],\n })\n }\n\n // Typography - Always visible\n fields.push({\n type: 'collapsible',\n label: {\n en: '🅰️ Typography',\n cs: '🅰️ Typografie',\n },\n admin: {\n initCollapsed: true,\n description: {\n en: 'Choose font families and base typography settings. Leave fields on \"Use preset\" to inherit the theme defaults.',\n cs: 'Zvolte rodiny písem a základní typografické nastavení. Hodnota \"Použít výchozí\" zachová nastavení motivu.',\n },\n },\n fields: [\n {\n name: 'typography',\n type: 'group',\n fields: [\n {\n type: 'row',\n fields: [\n {\n name: 'bodyFont',\n type: 'select',\n label: {\n en: 'Body font',\n cs: 'Písmo pro text',\n },\n defaultValue: 'preset',\n // @ts-expect-error - Pass full objects including fontFamily for custom rendering\n options: BODY_FONT_OPTIONS,\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n },\n {\n name: 'headingFont',\n type: 'select',\n label: {\n en: 'Heading font',\n cs: 'Písmo pro nadpisy',\n },\n defaultValue: 'preset',\n // @ts-expect-error - Pass full objects including fontFamily for custom rendering\n options: HEADING_FONT_OPTIONS,\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n },\n ],\n },\n {\n type: 'row',\n fields: [\n {\n name: 'bodyFontCustom',\n type: 'text',\n label: {\n en: 'Custom body font stack',\n cs: 'Vlastní písmo pro text',\n },\n admin: {\n width: '50%',\n placeholder: '\"IBM Plex Sans\", sans-serif',\n description: {\n en: 'Provide full CSS font-family stack when using a custom font.',\n cs: 'Při volbě vlastního písma zadejte celý CSS zápis font-family.',\n },\n condition: (_, siblingData) => siblingData?.bodyFont === 'custom',\n },\n },\n {\n name: 'headingFontCustom',\n type: 'text',\n label: {\n en: 'Custom heading font stack',\n cs: 'Vlastní písmo pro nadpisy',\n },\n admin: {\n width: '50%',\n placeholder: '\"Cormorant Garamond\", serif',\n description: {\n en: 'Provide full CSS font-family stack when using a custom heading font.',\n cs: 'Při volbě vlastního nadpisového písma zadejte celý CSS zápis font-family.',\n },\n condition: (_, siblingData) => siblingData?.headingFont === 'custom',\n },\n },\n ],\n },\n {\n type: 'row',\n fields: [\n {\n name: 'baseFontSize',\n type: 'select',\n label: {\n en: 'Base font size',\n cs: 'Základní velikost písma',\n },\n defaultValue: 'preset',\n options: BASE_FONT_SIZE_OPTIONS.map((option) => ({\n label: option.label,\n value: option.value,\n })),\n admin: {\n width: '50%',\n },\n },\n {\n name: 'lineHeight',\n type: 'select',\n label: {\n en: 'Line height',\n cs: 'Řádkování',\n },\n defaultValue: 'preset',\n options: LINE_HEIGHT_OPTIONS.map((option) => ({\n label: option.label,\n value: option.value,\n })),\n admin: {\n width: '50%',\n },\n },\n ],\n },\n ],\n },\n ],\n })\n\n // Advanced Settings (when using extended theme configuration)\n if (enableAdvancedFeatures) {\n const advancedFields: Field[] = [\n {\n name: 'animationLevel',\n type: 'select',\n label: {\n en: 'Animation Level',\n cs: 'Úroveň animací',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'None', cs: 'Žádné' }, value: 'none' },\n { label: { en: 'Reduced', cs: 'Snížené' }, value: 'reduced' },\n { label: { en: 'Medium', cs: 'Střední' }, value: 'medium' },\n { label: { en: 'High', cs: 'Vysoké' }, value: 'high' },\n ],\n admin: {\n description: {\n en: 'Control website animations and transitions',\n cs: 'Kontrola animací a přechodů na webu',\n },\n width: '50%',\n },\n },\n ]\n\n if (includeCustomCSS) {\n advancedFields.push({\n name: 'customCSS',\n type: 'code',\n label: {\n en: 'Custom CSS',\n cs: 'Vlastní CSS',\n },\n admin: {\n language: 'css',\n description: {\n en: 'Add custom CSS to override default styles',\n cs: 'Přidejte vlastní CSS pro přepsání výchozích stylů',\n },\n // placeholder:\n // ':root {\\n --primary: #your-color;\\n --border-radius: 8px;\\n --font-family: \"Your Font\";\\n}\\n\\n.custom-class {\\n /* Your custom styles */\\n}',\n // rows: 8,\n },\n })\n }\n\n fields.push({\n type: 'collapsible',\n label: {\n en: '⚙️ Advanced Settings',\n cs: '⚙️ Pokročilé nastavení',\n },\n admin: {\n initCollapsed: true,\n },\n fields: advancedFields,\n })\n }\n\n // Return as tab configuration to be injected into tabs structure\n return {\n label: {\n en: '🎨 Appearance Settings',\n cs: '🎨 Nastavení vzhledu',\n },\n fields: [\n {\n name: 'themeConfiguration',\n type: 'group',\n admin: {\n description: {\n en: 'Configure website appearance and styling',\n cs: 'Nakonfigurujte vzhled a stylování webu',\n },\n },\n fields,\n },\n ],\n }\n}\n"],"names":["BASE_FONT_SIZE_OPTIONS","BODY_FONT_OPTIONS","HEADING_FONT_OPTIONS","LINE_HEIGHT_OPTIONS","darkModeField","lightModeField","createThemeConfigurationField","options","themePresets","defaultTheme","includeColorModeToggle","includeCustomCSS","enableAdvancedFeatures","themeOptions","map","preset","previewColor","preview","colors","primary","label","value","name","fields","type","required","defaultValue","en","cs","admin","description","components","Field","push","width","initCollapsed","placeholder","condition","_","siblingData","bodyFont","headingFont","option","advancedFields","language"],"mappings":"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;qBACD;gBACH;gBACA7B;gBACAD;aACD;QACH;IACF;IAEA,8BAA8B;IAC9BmB,OAAOU,IAAI,CAAC;QACVT,MAAM;QACNJ,OAAO;YACLO,IAAI;YACJC,IAAI;QACN;QACAC,OAAO;YACLM,eAAe;YACfL,aAAa;gBACXH,IAAI;gBACJC,IAAI;YACN;QACF;QACAL,QAAQ;YACN;gBACED,MAAM;gBACNE,MAAM;gBACND,QAAQ;oBACN;wBACEC,MAAM;wBACND,QAAQ;4BACN;gCACED,MAAM;gCACNE,MAAM;gCACNJ,OAAO;oCACLO,IAAI;oCACJC,IAAI;gCACN;gCACAF,cAAc;gCACd,iFAAiF;gCACjFnB,SAASN;gCACT4B,OAAO;oCACLK,OAAO;oCACPH,YAAY;wCACVC,OAAO;oCACT;gCACF;4BACF;4BACA;gCACEV,MAAM;gCACNE,MAAM;gCACNJ,OAAO;oCACLO,IAAI;oCACJC,IAAI;gCACN;gCACAF,cAAc;gCACd,iFAAiF;gCACjFnB,SAASL;gCACT2B,OAAO;oCACLK,OAAO;oCACPH,YAAY;wCACVC,OAAO;oCACT;gCACF;4BACF;yBACD;oBACH;oBACA;wBACER,MAAM;wBACND,QAAQ;4BACN;gCACED,MAAM;gCACNE,MAAM;gCACNJ,OAAO;oCACLO,IAAI;oCACJC,IAAI;gCACN;gCACAC,OAAO;oCACLK,OAAO;oCACPE,aAAa;oCACbN,aAAa;wCACXH,IAAI;wCACJC,IAAI;oCACN;oCACAS,WAAW,CAACC,GAAGC,cAAgBA,aAAaC,aAAa;gCAC3D;4BACF;4BACA;gCACElB,MAAM;gCACNE,MAAM;gCACNJ,OAAO;oCACLO,IAAI;oCACJC,IAAI;gCACN;gCACAC,OAAO;oCACLK,OAAO;oCACPE,aAAa;oCACbN,aAAa;wCACXH,IAAI;wCACJC,IAAI;oCACN;oCACAS,WAAW,CAACC,GAAGC,cAAgBA,aAAaE,gBAAgB;gCAC9D;4BACF;yBACD;oBACH;oBACA;wBACEjB,MAAM;wBACND,QAAQ;4BACN;gCACED,MAAM;gCACNE,MAAM;gCACNJ,OAAO;oCACLO,IAAI;oCACJC,IAAI;gCACN;gCACAF,cAAc;gCACdnB,SAASP,uBAAuBc,GAAG,CAAC,CAAC4B,SAAY,CAAA;wCAC/CtB,OAAOsB,OAAOtB,KAAK;wCACnBC,OAAOqB,OAAOrB,KAAK;oCACrB,CAAA;gCACAQ,OAAO;oCACLK,OAAO;gCACT;4BACF;4BACA;gCACEZ,MAAM;gCACNE,MAAM;gCACNJ,OAAO;oCACLO,IAAI;oCACJC,IAAI;gCACN;gCACAF,cAAc;gCACdnB,SAASJ,oBAAoBW,GAAG,CAAC,CAAC4B,SAAY,CAAA;wCAC5CtB,OAAOsB,OAAOtB,KAAK;wCACnBC,OAAOqB,OAAOrB,KAAK;oCACrB,CAAA;gCACAQ,OAAO;oCACLK,OAAO;gCACT;4BACF;yBACD;oBACH;iBACD;YACH;SACD;IACH;IAEA,8DAA8D;IAC9D,IAAItB,wBAAwB;QAC1B,MAAM+B,iBAA0B;YAC9B;gBACErB,MAAM;gBACNE,MAAM;gBACNJ,OAAO;oBACLO,IAAI;oBACJC,IAAI;gBACN;gBACAF,cAAc;gBACdnB,SAAS;oBACP;wBAAEa,OAAO;4BAAEO,IAAI;4BAAQC,IAAI;wBAAQ;wBAAGP,OAAO;oBAAO;oBACpD;wBAAED,OAAO;4BAAEO,IAAI;4BAAWC,IAAI;wBAAU;wBAAGP,OAAO;oBAAU;oBAC5D;wBAAED,OAAO;4BAAEO,IAAI;4BAAUC,IAAI;wBAAU;wBAAGP,OAAO;oBAAS;oBAC1D;wBAAED,OAAO;4BAAEO,IAAI;4BAAQC,IAAI;wBAAS;wBAAGP,OAAO;oBAAO;iBACtD;gBACDQ,OAAO;oBACLC,aAAa;wBACXH,IAAI;wBACJC,IAAI;oBACN;oBACAM,OAAO;gBACT;YACF;SACD;QAED,IAAIvB,kBAAkB;YACpBgC,eAAeV,IAAI,CAAC;gBAClBX,MAAM;gBACNE,MAAM;gBACNJ,OAAO;oBACLO,IAAI;oBACJC,IAAI;gBACN;gBACAC,OAAO;oBACLe,UAAU;oBACVd,aAAa;wBACXH,IAAI;wBACJC,IAAI;oBACN;gBAIF;YACF;QACF;QAEAL,OAAOU,IAAI,CAAC;YACVT,MAAM;YACNJ,OAAO;gBACLO,IAAI;gBACJC,IAAI;YACN;YACAC,OAAO;gBACLM,eAAe;YACjB;YACAZ,QAAQoB;QACV;IACF;IAEA,iEAAiE;IACjE,OAAO;QACLvB,OAAO;YACLO,IAAI;YACJC,IAAI;QACN;QACAL,QAAQ;YACN;gBACED,MAAM;gBACNE,MAAM;gBACNK,OAAO;oBACLC,aAAa;wBACXH,IAAI;wBACJC,IAAI;oBACN;gBACF;gBACAL;YACF;SACD;IACH;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../src/fields/themeConfigurationField.ts"],"sourcesContent":["import type { Field, TabsField } from 'payload'\nimport {\n BASE_FONT_SIZE_OPTIONS,\n BODY_FONT_OPTIONS,\n HEADING_FONT_OPTIONS,\n LINE_HEIGHT_OPTIONS,\n} from '../constants/themeFonts.js'\nimport type { ThemePreset } from '../index.js'\nimport { darkModeField, lightModeField } from './colorModeFields.js'\n\ninterface ThemeConfigurationFieldOptions {\n themePresets: ThemePreset[]\n defaultTheme: string\n includeColorModeToggle: boolean\n includeCustomCSS: boolean\n includeBrandIdentity: boolean\n enableAdvancedFeatures?: boolean\n}\n\n// Type for a single tab (not the full TabsField)\nexport type ThemeTab = NonNullable<TabsField['tabs']>[number]\n\n/**\n * Theme Configuration Field with Theme Selection as default value setter\n * Theme Selection populates lightMode/darkMode colors automatically\n */\nexport function createThemeConfigurationField(options: ThemeConfigurationFieldOptions): ThemeTab {\n const {\n themePresets,\n defaultTheme,\n includeColorModeToggle,\n includeCustomCSS,\n enableAdvancedFeatures = true,\n } = options\n\n const fields: Field[] = [\n {\n name: 'theme',\n type: 'text',\n required: true,\n defaultValue: defaultTheme,\n label: {\n en: '🎨 Theme Selection',\n cs: '🎨 Výběr tématu',\n },\n validate: (value: unknown) => {\n if (typeof value === 'string' && value.trim().length > 0) {\n return true\n }\n return 'Select a theme'\n },\n admin: {\n description: {\n en: 'Select a theme to auto-populate color values. You can customize colors after selection.',\n cs: 'Vyberte téma pro automatické předvyplnění barev. Po výběru můžete barvy upravit.',\n },\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/ThemePreviewField',\n },\n custom: {\n themePresets,\n },\n },\n },\n ]\n\n // Border Radius Configuration - Always visible\n fields.push({\n type: 'row',\n fields: [\n {\n name: 'borderRadius',\n type: 'select',\n label: {\n en: '📐 Border Radius',\n cs: '📐 Zaoblení rohů',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'None (0px)', cs: 'Žádné (0px)' }, value: 'none' },\n { label: { en: 'Small (0.25rem)', cs: 'Malé (0.25rem)' }, value: 'small' },\n { label: { en: 'Medium (0.5rem)', cs: 'Střední (0.5rem)' }, value: 'medium' },\n { label: { en: 'Large (0.75rem)', cs: 'Velké (0.75rem)' }, value: 'large' },\n { label: { en: 'XL (1rem)', cs: 'XL (1rem)' }, value: 'xl' },\n ],\n admin: {\n description: {\n en: 'Default corner rounding for UI elements',\n cs: 'Výchozí zaoblení rohů pro UI prvky',\n },\n width: '50%',\n },\n },\n {\n name: 'spacing',\n type: 'select',\n label: {\n en: '📏 Spacing Scale',\n cs: '📏 Škála rozestupů',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'Compact', cs: 'Kompaktní' }, value: 'compact' },\n { label: { en: 'Medium', cs: 'Střední' }, value: 'medium' },\n { label: { en: 'Comfortable', cs: 'Pohodlné' }, value: 'comfortable' },\n ],\n admin: {\n description: {\n en: 'Global spacing between elements',\n cs: 'Globální rozestupy mezi prvky',\n },\n width: '50%',\n },\n },\n ],\n })\n\n // Color Mode Settings\n if (includeColorModeToggle) {\n fields.push({\n type: 'collapsible',\n label: {\n en: '🌗 Color Mode Settings',\n cs: '🌗 Nastavení barevného režimu',\n },\n admin: {\n initCollapsed: false,\n description: {\n en: 'Configure light and dark mode colors. Changes here override theme selection.',\n cs: 'Nakonfigurujte barvy světlého a tmavého režimu. Změny zde přepíší výběr tématu.',\n },\n },\n fields: [\n {\n type: 'row',\n fields: [\n {\n name: 'colorMode',\n type: 'select',\n label: {\n en: 'Default Color Mode',\n cs: 'Výchozí barevný režim',\n },\n defaultValue: 'auto',\n options: [\n {\n label: {\n en: 'Auto (System)',\n cs: 'Automaticky (Systém)',\n },\n value: 'auto',\n },\n {\n label: { en: 'Light', cs: 'Světlý' },\n value: 'light',\n },\n {\n label: { en: 'Dark', cs: 'Tmavý' },\n value: 'dark',\n },\n ],\n admin: {\n description: {\n en: 'Default color mode for visitors',\n cs: 'Výchozí barevný režim pro návštěvníky',\n },\n width: '50%',\n },\n },\n {\n name: 'allowColorModeToggle',\n type: 'checkbox',\n label: {\n en: 'Enable Mode Toggle',\n cs: 'Povolit přepínač režimu',\n },\n defaultValue: true,\n admin: {\n description: {\n en: 'Let visitors switch modes',\n cs: 'Umožnit návštěvníkům přepínat režimy',\n },\n width: '50%',\n },\n },\n ],\n },\n lightModeField,\n darkModeField,\n ],\n })\n }\n\n // Typography - Always visible\n fields.push({\n type: 'collapsible',\n label: {\n en: '🅰️ Typography',\n cs: '🅰️ Typografie',\n },\n admin: {\n initCollapsed: true,\n description: {\n en: 'Choose font families and base typography settings. Leave fields on \"Use preset\" to inherit the theme defaults.',\n cs: 'Zvolte rodiny písem a základní typografické nastavení. Hodnota \"Použít výchozí\" zachová nastavení motivu.',\n },\n },\n fields: [\n {\n name: 'typography',\n type: 'group',\n fields: [\n {\n type: 'row',\n fields: [\n {\n name: 'bodyFont',\n type: 'select',\n label: {\n en: 'Body font',\n cs: 'Písmo pro text',\n },\n defaultValue: 'preset',\n // @ts-expect-error - Pass full objects including fontFamily for custom rendering\n options: BODY_FONT_OPTIONS,\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n },\n {\n name: 'headingFont',\n type: 'select',\n label: {\n en: 'Heading font',\n cs: 'Písmo pro nadpisy',\n },\n defaultValue: 'preset',\n // @ts-expect-error - Pass full objects including fontFamily for custom rendering\n options: HEADING_FONT_OPTIONS,\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n },\n ],\n },\n {\n type: 'row',\n fields: [\n {\n name: 'bodyFontCustom',\n type: 'text',\n label: {\n en: 'Custom body font stack',\n cs: 'Vlastní písmo pro text',\n },\n admin: {\n width: '50%',\n placeholder: '\"IBM Plex Sans\", sans-serif',\n description: {\n en: 'Provide full CSS font-family stack when using a custom font.',\n cs: 'Při volbě vlastního písma zadejte celý CSS zápis font-family.',\n },\n condition: (_, siblingData) => siblingData?.bodyFont === 'custom',\n },\n },\n {\n name: 'headingFontCustom',\n type: 'text',\n label: {\n en: 'Custom heading font stack',\n cs: 'Vlastní písmo pro nadpisy',\n },\n admin: {\n width: '50%',\n placeholder: '\"Cormorant Garamond\", serif',\n description: {\n en: 'Provide full CSS font-family stack when using a custom heading font.',\n cs: 'Při volbě vlastního nadpisového písma zadejte celý CSS zápis font-family.',\n },\n condition: (_, siblingData) => siblingData?.headingFont === 'custom',\n },\n },\n ],\n },\n {\n type: 'row',\n fields: [\n {\n name: 'baseFontSize',\n type: 'select',\n label: {\n en: 'Base font size',\n cs: 'Základní velikost písma',\n },\n defaultValue: 'preset',\n options: BASE_FONT_SIZE_OPTIONS.map((option) => ({\n label: option.label,\n value: option.value,\n })),\n admin: {\n width: '50%',\n },\n },\n {\n name: 'lineHeight',\n type: 'select',\n label: {\n en: 'Line height',\n cs: 'Řádkování',\n },\n defaultValue: 'preset',\n options: LINE_HEIGHT_OPTIONS.map((option) => ({\n label: option.label,\n value: option.value,\n })),\n admin: {\n width: '50%',\n },\n },\n ],\n },\n ],\n },\n ],\n })\n\n // Custom Theme Presets importer\n fields.push({\n type: 'collapsible',\n label: {\n en: '🧩 Custom Theme Presets',\n cs: '🧩 Vlastní motivy',\n },\n admin: {\n initCollapsed: true,\n description: {\n en: 'Import additional theme presets via JSON to extend or override the defaults. These presets become available instantly in the Theme Selection above.',\n cs: 'Importujte další motivy pomocí JSONu a rozšiřte nebo přepište výchozí nabídku. Motivy budou ihned k dispozici ve výběru výše.',\n },\n },\n fields: [\n {\n name: 'customThemePresets',\n type: 'json',\n label: {\n en: 'Custom Preset JSON',\n cs: 'JSON s vlastními motivy',\n },\n admin: {\n description: {\n en: 'Paste or import an array (or object map) of theme presets. Each entry should include at least a unique \"name\" and \"label\" with optional lightMode/darkMode colors.',\n cs: 'Vložte nebo importujte pole (či objekt) motivů. Každý motiv musí mít unikátní „name“ a „label“ a volitelně barvy pro světly/tmavý režim.',\n },\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/ThemePresetImportField',\n },\n },\n },\n ],\n })\n\n // Advanced Settings (when using extended theme configuration)\n if (enableAdvancedFeatures) {\n const advancedFields: Field[] = [\n {\n name: 'animationLevel',\n type: 'select',\n label: {\n en: 'Animation Level',\n cs: 'Úroveň animací',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'None', cs: 'Žádné' }, value: 'none' },\n { label: { en: 'Reduced', cs: 'Snížené' }, value: 'reduced' },\n { label: { en: 'Medium', cs: 'Střední' }, value: 'medium' },\n { label: { en: 'High', cs: 'Vysoké' }, value: 'high' },\n ],\n admin: {\n description: {\n en: 'Control website animations and transitions',\n cs: 'Kontrola animací a přechodů na webu',\n },\n width: '50%',\n },\n },\n ]\n\n if (includeCustomCSS) {\n advancedFields.push({\n name: 'customCSS',\n type: 'code',\n label: {\n en: 'Custom CSS',\n cs: 'Vlastní CSS',\n },\n admin: {\n language: 'css',\n description: {\n en: 'Add custom CSS to override default styles',\n cs: 'Přidejte vlastní CSS pro přepsání výchozích stylů',\n },\n // placeholder:\n // ':root {\\n --primary: #your-color;\\n --border-radius: 8px;\\n --font-family: \"Your Font\";\\n}\\n\\n.custom-class {\\n /* Your custom styles */\\n}',\n // rows: 8,\n },\n })\n }\n\n fields.push({\n type: 'collapsible',\n label: {\n en: '⚙️ Advanced Settings',\n cs: '⚙️ Pokročilé nastavení',\n },\n admin: {\n initCollapsed: true,\n },\n fields: advancedFields,\n })\n }\n\n // Return as tab configuration to be injected into tabs structure\n return {\n label: {\n en: '🎨 Appearance Settings',\n cs: '🎨 Nastavení vzhledu',\n },\n fields: [\n {\n name: 'themeConfiguration',\n type: 'group',\n admin: {\n description: {\n en: 'Configure website appearance and styling',\n cs: 'Nakonfigurujte vzhled a stylování webu',\n },\n },\n fields,\n },\n ],\n }\n}\n"],"names":["BASE_FONT_SIZE_OPTIONS","BODY_FONT_OPTIONS","HEADING_FONT_OPTIONS","LINE_HEIGHT_OPTIONS","darkModeField","lightModeField","createThemeConfigurationField","options","themePresets","defaultTheme","includeColorModeToggle","includeCustomCSS","enableAdvancedFeatures","fields","name","type","required","defaultValue","label","en","cs","validate","value","trim","length","admin","description","components","Field","custom","push","width","initCollapsed","placeholder","condition","_","siblingData","bodyFont","headingFont","map","option","advancedFields","language"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ThemePreset } from '../presets.js';
|
|
2
|
+
export declare function sanitizeThemePreset(candidate: unknown, fallbackName?: string): ThemePreset | null;
|
|
3
|
+
export declare function parseThemePresetInput(value: unknown): ThemePreset[];
|
|
4
|
+
export declare function mergeThemePresets(base: ThemePreset[], extras: ThemePreset[]): ThemePreset[];
|
|
5
|
+
//# sourceMappingURL=customThemePresets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customThemePresets.d.ts","sourceRoot":"","sources":["../../src/utils/customThemePresets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAmBhD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CA8BjG;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,EAAE,CAsCnE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAc3F"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
function isRecord(value) {
|
|
2
|
+
return typeof value === 'object' && value !== null;
|
|
3
|
+
}
|
|
4
|
+
function normalizeBorderRadius(value) {
|
|
5
|
+
if (value === 'none' || value === 'small' || value === 'medium' || value === 'large' || value === 'xl') {
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
return 'medium';
|
|
9
|
+
}
|
|
10
|
+
export function sanitizeThemePreset(candidate, fallbackName) {
|
|
11
|
+
if (!isRecord(candidate)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const nameCandidate = typeof candidate.name === 'string' ? candidate.name.trim() : fallbackName;
|
|
15
|
+
if (!nameCandidate || nameCandidate.length === 0) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const labelCandidate = typeof candidate.label === 'string' && candidate.label.trim().length > 0 ? candidate.label.trim() : nameCandidate;
|
|
19
|
+
const lightMode = isRecord(candidate.lightMode) ? candidate.lightMode : {};
|
|
20
|
+
const darkMode = isRecord(candidate.darkMode) ? candidate.darkMode : {};
|
|
21
|
+
return {
|
|
22
|
+
...candidate,
|
|
23
|
+
name: nameCandidate,
|
|
24
|
+
label: labelCandidate,
|
|
25
|
+
borderRadius: normalizeBorderRadius(candidate.borderRadius),
|
|
26
|
+
lightMode,
|
|
27
|
+
darkMode
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function parseThemePresetInput(value) {
|
|
31
|
+
if (!value) return [];
|
|
32
|
+
const presets = [];
|
|
33
|
+
const pushPreset = (candidate, fallbackName)=>{
|
|
34
|
+
const preset = sanitizeThemePreset(candidate, fallbackName);
|
|
35
|
+
if (preset) {
|
|
36
|
+
presets.push(preset);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
if (typeof value === 'string') {
|
|
40
|
+
try {
|
|
41
|
+
const parsed = JSON.parse(value);
|
|
42
|
+
return parseThemePresetInput(parsed);
|
|
43
|
+
} catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
value.forEach((candidate, index)=>pushPreset(candidate, `custom-${index + 1}`));
|
|
49
|
+
return presets;
|
|
50
|
+
}
|
|
51
|
+
if (isRecord(value)) {
|
|
52
|
+
Object.entries(value).forEach(([key, candidate])=>{
|
|
53
|
+
if (isRecord(candidate)) {
|
|
54
|
+
pushPreset({
|
|
55
|
+
...candidate,
|
|
56
|
+
name: candidate.name ?? key
|
|
57
|
+
}, key);
|
|
58
|
+
} else {
|
|
59
|
+
pushPreset(candidate, key);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return presets;
|
|
63
|
+
}
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
export function mergeThemePresets(base, extras) {
|
|
67
|
+
if (!extras.length) return base;
|
|
68
|
+
const map = new Map();
|
|
69
|
+
base.forEach((preset)=>{
|
|
70
|
+
if (preset?.name) {
|
|
71
|
+
map.set(preset.name, preset);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
extras.forEach((preset)=>{
|
|
75
|
+
if (preset?.name) {
|
|
76
|
+
map.set(preset.name, preset);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return Array.from(map.values());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=customThemePresets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/customThemePresets.ts"],"sourcesContent":["import type { ThemePreset } from '../presets.js'\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\nfunction normalizeBorderRadius(value: unknown): ThemePreset['borderRadius'] {\n if (\n value === 'none' ||\n value === 'small' ||\n value === 'medium' ||\n value === 'large' ||\n value === 'xl'\n ) {\n return value\n }\n return 'medium'\n}\n\nexport function sanitizeThemePreset(candidate: unknown, fallbackName?: string): ThemePreset | null {\n if (!isRecord(candidate)) {\n return null\n }\n\n const nameCandidate = typeof candidate.name === 'string' ? candidate.name.trim() : fallbackName\n if (!nameCandidate || nameCandidate.length === 0) {\n return null\n }\n\n const labelCandidate =\n typeof candidate.label === 'string' && candidate.label.trim().length > 0\n ? candidate.label.trim()\n : nameCandidate\n\n const lightMode = isRecord(candidate.lightMode)\n ? (candidate.lightMode as Record<string, string>)\n : {}\n const darkMode = isRecord(candidate.darkMode)\n ? (candidate.darkMode as Record<string, string>)\n : {}\n\n return {\n ...candidate,\n name: nameCandidate,\n label: labelCandidate,\n borderRadius: normalizeBorderRadius(candidate.borderRadius),\n lightMode,\n darkMode,\n } as ThemePreset\n}\n\nexport function parseThemePresetInput(value: unknown): ThemePreset[] {\n if (!value) return []\n\n const presets: ThemePreset[] = []\n\n const pushPreset = (candidate: unknown, fallbackName?: string) => {\n const preset = sanitizeThemePreset(candidate, fallbackName)\n if (preset) {\n presets.push(preset)\n }\n }\n\n if (typeof value === 'string') {\n try {\n const parsed = JSON.parse(value)\n return parseThemePresetInput(parsed)\n } catch {\n return []\n }\n }\n\n if (Array.isArray(value)) {\n value.forEach((candidate, index) => pushPreset(candidate, `custom-${index + 1}`))\n return presets\n }\n\n if (isRecord(value)) {\n Object.entries(value).forEach(([key, candidate]) => {\n if (isRecord(candidate)) {\n pushPreset({ ...candidate, name: candidate.name ?? key }, key)\n } else {\n pushPreset(candidate, key)\n }\n })\n return presets\n }\n\n return []\n}\n\nexport function mergeThemePresets(base: ThemePreset[], extras: ThemePreset[]): ThemePreset[] {\n if (!extras.length) return base\n const map = new Map<string, ThemePreset>()\n base.forEach((preset) => {\n if (preset?.name) {\n map.set(preset.name, preset)\n }\n })\n extras.forEach((preset) => {\n if (preset?.name) {\n map.set(preset.name, preset)\n }\n })\n return Array.from(map.values())\n}\n"],"names":["isRecord","value","normalizeBorderRadius","sanitizeThemePreset","candidate","fallbackName","nameCandidate","name","trim","length","labelCandidate","label","lightMode","darkMode","borderRadius","parseThemePresetInput","presets","pushPreset","preset","push","parsed","JSON","parse","Array","isArray","forEach","index","Object","entries","key","mergeThemePresets","base","extras","map","Map","set","from","values"],"mappings":"AAEA,SAASA,SAASC,KAAc;IAC9B,OAAO,OAAOA,UAAU,YAAYA,UAAU;AAChD;AAEA,SAASC,sBAAsBD,KAAc;IAC3C,IACEA,UAAU,UACVA,UAAU,WACVA,UAAU,YACVA,UAAU,WACVA,UAAU,MACV;QACA,OAAOA;IACT;IACA,OAAO;AACT;AAEA,OAAO,SAASE,oBAAoBC,SAAkB,EAAEC,YAAqB;IAC3E,IAAI,CAACL,SAASI,YAAY;QACxB,OAAO;IACT;IAEA,MAAME,gBAAgB,OAAOF,UAAUG,IAAI,KAAK,WAAWH,UAAUG,IAAI,CAACC,IAAI,KAAKH;IACnF,IAAI,CAACC,iBAAiBA,cAAcG,MAAM,KAAK,GAAG;QAChD,OAAO;IACT;IAEA,MAAMC,iBACJ,OAAON,UAAUO,KAAK,KAAK,YAAYP,UAAUO,KAAK,CAACH,IAAI,GAAGC,MAAM,GAAG,IACnEL,UAAUO,KAAK,CAACH,IAAI,KACpBF;IAEN,MAAMM,YAAYZ,SAASI,UAAUQ,SAAS,IACzCR,UAAUQ,SAAS,GACpB,CAAC;IACL,MAAMC,WAAWb,SAASI,UAAUS,QAAQ,IACvCT,UAAUS,QAAQ,GACnB,CAAC;IAEL,OAAO;QACL,GAAGT,SAAS;QACZG,MAAMD;QACNK,OAAOD;QACPI,cAAcZ,sBAAsBE,UAAUU,YAAY;QAC1DF;QACAC;IACF;AACF;AAEA,OAAO,SAASE,sBAAsBd,KAAc;IAClD,IAAI,CAACA,OAAO,OAAO,EAAE;IAErB,MAAMe,UAAyB,EAAE;IAEjC,MAAMC,aAAa,CAACb,WAAoBC;QACtC,MAAMa,SAASf,oBAAoBC,WAAWC;QAC9C,IAAIa,QAAQ;YACVF,QAAQG,IAAI,CAACD;QACf;IACF;IAEA,IAAI,OAAOjB,UAAU,UAAU;QAC7B,IAAI;YACF,MAAMmB,SAASC,KAAKC,KAAK,CAACrB;YAC1B,OAAOc,sBAAsBK;QAC/B,EAAE,OAAM;YACN,OAAO,EAAE;QACX;IACF;IAEA,IAAIG,MAAMC,OAAO,CAACvB,QAAQ;QACxBA,MAAMwB,OAAO,CAAC,CAACrB,WAAWsB,QAAUT,WAAWb,WAAW,CAAC,OAAO,EAAEsB,QAAQ,GAAG;QAC/E,OAAOV;IACT;IAEA,IAAIhB,SAASC,QAAQ;QACnB0B,OAAOC,OAAO,CAAC3B,OAAOwB,OAAO,CAAC,CAAC,CAACI,KAAKzB,UAAU;YAC7C,IAAIJ,SAASI,YAAY;gBACvBa,WAAW;oBAAE,GAAGb,SAAS;oBAAEG,MAAMH,UAAUG,IAAI,IAAIsB;gBAAI,GAAGA;YAC5D,OAAO;gBACLZ,WAAWb,WAAWyB;YACxB;QACF;QACA,OAAOb;IACT;IAEA,OAAO,EAAE;AACX;AAEA,OAAO,SAASc,kBAAkBC,IAAmB,EAAEC,MAAqB;IAC1E,IAAI,CAACA,OAAOvB,MAAM,EAAE,OAAOsB;IAC3B,MAAME,MAAM,IAAIC;IAChBH,KAAKN,OAAO,CAAC,CAACP;QACZ,IAAIA,QAAQX,MAAM;YAChB0B,IAAIE,GAAG,CAACjB,OAAOX,IAAI,EAAEW;QACvB;IACF;IACAc,OAAOP,OAAO,CAAC,CAACP;QACd,IAAIA,QAAQX,MAAM;YAChB0B,IAAIE,GAAG,CAACjB,OAAOX,IAAI,EAAEW;QACvB;IACF;IACA,OAAOK,MAAMa,IAAI,CAACH,IAAII,MAAM;AAC9B"}
|
|
@@ -115,5 +115,5 @@ export declare function createUseExtendedTheme(presets: Record<string, ExtendedT
|
|
|
115
115
|
* }
|
|
116
116
|
* ```
|
|
117
117
|
*/
|
|
118
|
-
export declare function getTailwindVarReferences():
|
|
118
|
+
export declare function getTailwindVarReferences(): Record<string, unknown>;
|
|
119
119
|
//# sourceMappingURL=extendedThemeHelpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extendedThemeHelpers.d.ts","sourceRoot":"","sources":["../../src/utils/extendedThemeHelpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"extendedThemeHelpers.d.ts","sourceRoot":"","sources":["../../src/utils/extendedThemeHelpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAEpF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,GAAE,OAAO,GAAG,MAAgB,GAC/B,IAAI,CAaN;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAU3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAqCzC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,iBAAiB,EACtE,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,OAAO,GAAG,MAAM,EACtB,IAAI,EAAE,CAAC,EAAE,GACR,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAW5B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC;;;2BAK/B,MAAM,QAAQ,OAAO,GAAG,MAAM;;EAmBjF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2ClE"}
|
|
@@ -2,15 +2,15 @@ import * as React from 'react';
|
|
|
2
2
|
/**
|
|
3
3
|
* Apply an extended theme client-side by setting CSS variables on document.documentElement
|
|
4
4
|
* Works exactly like silicondeck/shadcn-dashboard-landing-template's useThemeManager
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```tsx
|
|
8
8
|
* import { extendedThemePresets, applyExtendedTheme } from '@kilivi/payloadcms-theme-management'
|
|
9
|
-
*
|
|
9
|
+
*
|
|
10
10
|
* // In a client component or hook
|
|
11
11
|
* function useExtendedTheme() {
|
|
12
12
|
* const isDark = document.documentElement.classList.contains('dark')
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
14
|
* useEffect(() => {
|
|
15
15
|
* applyExtendedTheme(extendedThemePresets['cool-extended'], isDark ? 'dark' : 'light')
|
|
16
16
|
* }, [isDark])
|
|
@@ -31,14 +31,14 @@ import * as React from 'react';
|
|
|
31
31
|
/**
|
|
32
32
|
* Generate CSS content for globals.css
|
|
33
33
|
* Add this to your globals.css file to define base theme variables
|
|
34
|
-
*
|
|
34
|
+
*
|
|
35
35
|
* @example
|
|
36
36
|
* ```tsx
|
|
37
37
|
* import { extendedThemePresets, generateExtendedThemeCSS } from '@kilivi/payloadcms-theme-management'
|
|
38
|
-
*
|
|
38
|
+
*
|
|
39
39
|
* // Generate CSS for cool-extended theme
|
|
40
40
|
* const css = generateExtendedThemeCSS(extendedThemePresets['cool-extended'])
|
|
41
|
-
*
|
|
41
|
+
*
|
|
42
42
|
* // Copy this to your globals.css:
|
|
43
43
|
* // :root {
|
|
44
44
|
* // --background: oklch(...);
|
|
@@ -57,11 +57,11 @@ import * as React from 'react';
|
|
|
57
57
|
/**
|
|
58
58
|
* Reset all extended theme CSS variables
|
|
59
59
|
* Useful when switching between themes or resetting to defaults
|
|
60
|
-
*
|
|
60
|
+
*
|
|
61
61
|
* @example
|
|
62
62
|
* ```tsx
|
|
63
63
|
* import { resetExtendedTheme } from '@kilivi/payloadcms-theme-management'
|
|
64
|
-
*
|
|
64
|
+
*
|
|
65
65
|
* // Remove all extended theme variables
|
|
66
66
|
* resetExtendedTheme()
|
|
67
67
|
* ```
|
|
@@ -104,11 +104,11 @@ import * as React from 'react';
|
|
|
104
104
|
/**
|
|
105
105
|
* Extract specific color tokens from an extended theme
|
|
106
106
|
* Useful when you only need a subset of theme colors
|
|
107
|
-
*
|
|
107
|
+
*
|
|
108
108
|
* @example
|
|
109
109
|
* ```tsx
|
|
110
110
|
* import { extendedThemePresets, getExtendedThemeTokens } from '@kilivi/payloadcms-theme-management'
|
|
111
|
-
*
|
|
111
|
+
*
|
|
112
112
|
* const chartColors = getExtendedThemeTokens(
|
|
113
113
|
* extendedThemePresets['cool-extended'],
|
|
114
114
|
* 'light',
|
|
@@ -128,16 +128,16 @@ import * as React from 'react';
|
|
|
128
128
|
/**
|
|
129
129
|
* Create a React hook for theme management
|
|
130
130
|
* Factory function to create a custom hook tied to your theme system
|
|
131
|
-
*
|
|
131
|
+
*
|
|
132
132
|
* @example
|
|
133
133
|
* ```tsx
|
|
134
134
|
* import { extendedThemePresets, createUseExtendedTheme } from '@kilivi/payloadcms-theme-management'
|
|
135
|
-
*
|
|
135
|
+
*
|
|
136
136
|
* const useExtendedTheme = createUseExtendedTheme(extendedThemePresets)
|
|
137
|
-
*
|
|
137
|
+
*
|
|
138
138
|
* function MyComponent() {
|
|
139
139
|
* const { applyTheme, currentMode } = useExtendedTheme()
|
|
140
|
-
*
|
|
140
|
+
*
|
|
141
141
|
* return (
|
|
142
142
|
* <button onClick={() => applyTheme('neon-extended', 'dark')}>
|
|
143
143
|
* Apply Neon Dark
|
|
@@ -170,12 +170,12 @@ import * as React from 'react';
|
|
|
170
170
|
/**
|
|
171
171
|
* Get Tailwind CSS color references for extended themes
|
|
172
172
|
* Use this in your tailwind.config.ts to enable semantic color classes
|
|
173
|
-
*
|
|
173
|
+
*
|
|
174
174
|
* @example
|
|
175
175
|
* ```ts
|
|
176
176
|
* // tailwind.config.ts
|
|
177
177
|
* import { getTailwindVarReferences } from '@kilivi/payloadcms-theme-management'
|
|
178
|
-
*
|
|
178
|
+
*
|
|
179
179
|
* export default {
|
|
180
180
|
* theme: {
|
|
181
181
|
* extend: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/extendedThemeHelpers.ts"],"sourcesContent":["import type { ExtendedThemePreset, ShadcnColorTokens } from '../extended-presets.js'\nimport * as React from 'react'\n\n/**\n * Apply an extended theme client-side by setting CSS variables on document.documentElement\n * Works exactly like silicondeck/shadcn-dashboard-landing-template's useThemeManager\n * \n * @example\n * ```tsx\n * import { extendedThemePresets, applyExtendedTheme } from '@kilivi/payloadcms-theme-management'\n * \n * // In a client component or hook\n * function useExtendedTheme() {\n * const isDark = document.documentElement.classList.contains('dark')\n * \n * useEffect(() => {\n * applyExtendedTheme(extendedThemePresets['cool-extended'], isDark ? 'dark' : 'light')\n * }, [isDark])\n * }\n * ```\n */\nexport function applyExtendedTheme(\n theme: ExtendedThemePreset,\n mode: 'light' | 'dark' = 'light'\n): void {\n if (typeof document === 'undefined') {\n console.warn('applyExtendedTheme can only be called in browser environment')\n return\n }\n \n const tokens = theme.styles[mode]\n const root = document.documentElement\n \n // Apply CSS variables to document root (same as silicondeck's approach)\n Object.entries(tokens).forEach(([key, value]) => {\n root.style.setProperty(`--${key}`, value)\n })\n}\n\n/**\n * Generate CSS content for globals.css\n * Add this to your globals.css file to define base theme variables\n * \n * @example\n * ```tsx\n * import { extendedThemePresets, generateExtendedThemeCSS } from '@kilivi/payloadcms-theme-management'\n * \n * // Generate CSS for cool-extended theme\n * const css = generateExtendedThemeCSS(extendedThemePresets['cool-extended'])\n * \n * // Copy this to your globals.css:\n * // :root {\n * // --background: oklch(...);\n * // ...\n * // }\n * // .dark {\n * // --background: oklch(...);\n * // ...\n * // }\n * ```\n */\nexport function generateExtendedThemeCSS(theme: ExtendedThemePreset): string {\n const lightVars = Object.entries(theme.styles.light)\n .map(([key, value]) => ` --${key}: ${value};`)\n .join('\\n')\n\n const darkVars = Object.entries(theme.styles.dark)\n .map(([key, value]) => ` --${key}: ${value};`)\n .join('\\n')\n\n return `:root {\\n${lightVars}\\n}\\n\\n.dark {\\n${darkVars}\\n}`\n}\n\n/**\n * Reset all extended theme CSS variables\n * Useful when switching between themes or resetting to defaults\n * \n * @example\n * ```tsx\n * import { resetExtendedTheme } from '@kilivi/payloadcms-theme-management'\n * \n * // Remove all extended theme variables\n * resetExtendedTheme()\n * ```\n */\nexport function resetExtendedTheme(): void {\n if (typeof document === 'undefined') return\n\n const root = document.documentElement\n const allVars = [\n 'background', 'foreground', 'card', 'card-foreground', 'popover', 'popover-foreground',\n 'primary', 'primary-foreground', 'secondary', 'secondary-foreground',\n 'muted', 'muted-foreground', 'accent', 'accent-foreground',\n 'destructive', 'destructive-foreground', 'border', 'input', 'ring',\n 'chart-1', 'chart-2', 'chart-3', 'chart-4', 'chart-5',\n 'radius', 'font-sans', 'font-mono'\n ]\n\n allVars.forEach(varName => {\n root.style.removeProperty(`--${varName}`)\n })\n}\n\n/**\n * Extract specific color tokens from an extended theme\n * Useful when you only need a subset of theme colors\n * \n * @example\n * ```tsx\n * import { extendedThemePresets, getExtendedThemeTokens } from '@kilivi/payloadcms-theme-management'\n * \n * const chartColors = getExtendedThemeTokens(\n * extendedThemePresets['cool-extended'],\n * 'light',\n * ['chart-1', 'chart-2', 'chart-3', 'chart-4', 'chart-5']\n * )\n * ```\n */\nexport function getExtendedThemeTokens<K extends keyof ShadcnColorTokens>(\n theme: ExtendedThemePreset,\n mode: 'light' | 'dark',\n keys: K[]\n): Pick<ShadcnColorTokens, K> {\n const tokens = theme.styles[mode]\n const result = {} as Pick<ShadcnColorTokens, K>\n \n keys.forEach(key => {\n if (tokens[key]) {\n result[key] = tokens[key]\n }\n })\n \n return result\n}\n\n/**\n * Create a React hook for theme management\n * Factory function to create a custom hook tied to your theme system\n * \n * @example\n * ```tsx\n * import { extendedThemePresets, createUseExtendedTheme } from '@kilivi/payloadcms-theme-management'\n * \n * const useExtendedTheme = createUseExtendedTheme(extendedThemePresets)\n * \n * function MyComponent() {\n * const { applyTheme, currentMode } = useExtendedTheme()\n * \n * return (\n * <button onClick={() => applyTheme('neon-extended', 'dark')}>\n * Apply Neon Dark\n * </button>\n * )\n * }\n * ```\n */\nexport function createUseExtendedTheme(presets: Record<string, ExtendedThemePreset>) {\n return function useExtendedTheme() {\n const [currentTheme, setCurrentTheme] = React.useState<string>('default')\n const [currentMode, setCurrentMode] = React.useState<'light' | 'dark'>('light')\n\n const applyTheme = React.useCallback((themeKey: string, mode: 'light' | 'dark') => {\n const theme = presets[themeKey]\n if (!theme) {\n console.warn(`Theme \"${themeKey}\" not found`)\n return\n }\n\n applyExtendedTheme(theme, mode)\n setCurrentTheme(themeKey)\n setCurrentMode(mode)\n }, [])\n\n return {\n currentTheme,\n currentMode,\n applyTheme,\n resetTheme: resetExtendedTheme\n }\n }\n}\n\n/**\n * Get Tailwind CSS color references for extended themes\n * Use this in your tailwind.config.ts to enable semantic color classes\n * \n * @example\n * ```ts\n * // tailwind.config.ts\n * import { getTailwindVarReferences } from '@kilivi/payloadcms-theme-management'\n * \n * export default {\n * theme: {\n * extend: {\n * colors: getTailwindVarReferences()\n * }\n * }\n * }\n * ```\n */\nexport function getTailwindVarReferences(): any {\n return {\n background: 'hsl(var(--background))',\n foreground: 'hsl(var(--foreground))',\n card: {\n DEFAULT: 'hsl(var(--card))',\n foreground: 'hsl(var(--card-foreground))',\n },\n popover: {\n DEFAULT: 'hsl(var(--popover))',\n foreground: 'hsl(var(--popover-foreground))',\n },\n primary: {\n DEFAULT: 'hsl(var(--primary))',\n foreground: 'hsl(var(--primary-foreground))',\n },\n secondary: {\n DEFAULT: 'hsl(var(--secondary))',\n foreground: 'hsl(var(--secondary-foreground))',\n },\n muted: {\n DEFAULT: 'hsl(var(--muted))',\n foreground: 'hsl(var(--muted-foreground))',\n },\n accent: {\n DEFAULT: 'hsl(var(--accent))',\n foreground: 'hsl(var(--accent-foreground))',\n },\n destructive: {\n DEFAULT: 'hsl(var(--destructive))',\n foreground: 'hsl(var(--destructive-foreground))',\n },\n border: 'hsl(var(--border))',\n input: 'hsl(var(--input))',\n ring: 'hsl(var(--ring))',\n chart: {\n '1': 'hsl(var(--chart-1))',\n '2': 'hsl(var(--chart-2))',\n '3': 'hsl(var(--chart-3))',\n '4': 'hsl(var(--chart-4))',\n '5': 'hsl(var(--chart-5))',\n },\n }\n}\n"],"names":["React","applyExtendedTheme","theme","mode","document","console","warn","tokens","styles","root","documentElement","Object","entries","forEach","key","value","style","setProperty","generateExtendedThemeCSS","lightVars","light","map","join","darkVars","dark","resetExtendedTheme","allVars","varName","removeProperty","getExtendedThemeTokens","keys","result","createUseExtendedTheme","presets","useExtendedTheme","currentTheme","setCurrentTheme","useState","currentMode","setCurrentMode","applyTheme","useCallback","themeKey","resetTheme","getTailwindVarReferences","background","foreground","card","DEFAULT","popover","primary","secondary","muted","accent","destructive","border","input","ring","chart"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/extendedThemeHelpers.ts"],"sourcesContent":["import * as React from 'react'\nimport type { ExtendedThemePreset, ShadcnColorTokens } from '../extended-presets.js'\n\n/**\n * Apply an extended theme client-side by setting CSS variables on document.documentElement\n * Works exactly like silicondeck/shadcn-dashboard-landing-template's useThemeManager\n *\n * @example\n * ```tsx\n * import { extendedThemePresets, applyExtendedTheme } from '@kilivi/payloadcms-theme-management'\n *\n * // In a client component or hook\n * function useExtendedTheme() {\n * const isDark = document.documentElement.classList.contains('dark')\n *\n * useEffect(() => {\n * applyExtendedTheme(extendedThemePresets['cool-extended'], isDark ? 'dark' : 'light')\n * }, [isDark])\n * }\n * ```\n */\nexport function applyExtendedTheme(\n theme: ExtendedThemePreset,\n mode: 'light' | 'dark' = 'light',\n): void {\n if (typeof document === 'undefined') {\n console.warn('applyExtendedTheme can only be called in browser environment')\n return\n }\n\n const tokens = theme.styles[mode]\n const root = document.documentElement\n\n // Apply CSS variables to document root (same as silicondeck's approach)\n Object.entries(tokens).forEach(([key, value]) => {\n root.style.setProperty(`--${key}`, value)\n })\n}\n\n/**\n * Generate CSS content for globals.css\n * Add this to your globals.css file to define base theme variables\n *\n * @example\n * ```tsx\n * import { extendedThemePresets, generateExtendedThemeCSS } from '@kilivi/payloadcms-theme-management'\n *\n * // Generate CSS for cool-extended theme\n * const css = generateExtendedThemeCSS(extendedThemePresets['cool-extended'])\n *\n * // Copy this to your globals.css:\n * // :root {\n * // --background: oklch(...);\n * // ...\n * // }\n * // .dark {\n * // --background: oklch(...);\n * // ...\n * // }\n * ```\n */\nexport function generateExtendedThemeCSS(theme: ExtendedThemePreset): string {\n const lightVars = Object.entries(theme.styles.light)\n .map(([key, value]) => ` --${key}: ${value};`)\n .join('\\n')\n\n const darkVars = Object.entries(theme.styles.dark)\n .map(([key, value]) => ` --${key}: ${value};`)\n .join('\\n')\n\n return `:root {\\n${lightVars}\\n}\\n\\n.dark {\\n${darkVars}\\n}`\n}\n\n/**\n * Reset all extended theme CSS variables\n * Useful when switching between themes or resetting to defaults\n *\n * @example\n * ```tsx\n * import { resetExtendedTheme } from '@kilivi/payloadcms-theme-management'\n *\n * // Remove all extended theme variables\n * resetExtendedTheme()\n * ```\n */\nexport function resetExtendedTheme(): void {\n if (typeof document === 'undefined') return\n\n const root = document.documentElement\n const allVars = [\n 'background',\n 'foreground',\n 'card',\n 'card-foreground',\n 'popover',\n 'popover-foreground',\n 'primary',\n 'primary-foreground',\n 'secondary',\n 'secondary-foreground',\n 'muted',\n 'muted-foreground',\n 'accent',\n 'accent-foreground',\n 'destructive',\n 'destructive-foreground',\n 'border',\n 'input',\n 'ring',\n 'chart-1',\n 'chart-2',\n 'chart-3',\n 'chart-4',\n 'chart-5',\n 'radius',\n 'font-sans',\n 'font-mono',\n ]\n\n allVars.forEach((varName) => {\n root.style.removeProperty(`--${varName}`)\n })\n}\n\n/**\n * Extract specific color tokens from an extended theme\n * Useful when you only need a subset of theme colors\n *\n * @example\n * ```tsx\n * import { extendedThemePresets, getExtendedThemeTokens } from '@kilivi/payloadcms-theme-management'\n *\n * const chartColors = getExtendedThemeTokens(\n * extendedThemePresets['cool-extended'],\n * 'light',\n * ['chart-1', 'chart-2', 'chart-3', 'chart-4', 'chart-5']\n * )\n * ```\n */\nexport function getExtendedThemeTokens<K extends keyof ShadcnColorTokens>(\n theme: ExtendedThemePreset,\n mode: 'light' | 'dark',\n keys: K[],\n): Pick<ShadcnColorTokens, K> {\n const tokens = theme.styles[mode]\n const result = {} as Pick<ShadcnColorTokens, K>\n\n keys.forEach((key) => {\n if (tokens[key]) {\n result[key] = tokens[key]\n }\n })\n\n return result\n}\n\n/**\n * Create a React hook for theme management\n * Factory function to create a custom hook tied to your theme system\n *\n * @example\n * ```tsx\n * import { extendedThemePresets, createUseExtendedTheme } from '@kilivi/payloadcms-theme-management'\n *\n * const useExtendedTheme = createUseExtendedTheme(extendedThemePresets)\n *\n * function MyComponent() {\n * const { applyTheme, currentMode } = useExtendedTheme()\n *\n * return (\n * <button onClick={() => applyTheme('neon-extended', 'dark')}>\n * Apply Neon Dark\n * </button>\n * )\n * }\n * ```\n */\nexport function createUseExtendedTheme(presets: Record<string, ExtendedThemePreset>) {\n return function useExtendedTheme() {\n const [currentTheme, setCurrentTheme] = React.useState<string>('default')\n const [currentMode, setCurrentMode] = React.useState<'light' | 'dark'>('light')\n\n const applyTheme = React.useCallback((themeKey: string, mode: 'light' | 'dark') => {\n const theme = presets[themeKey]\n if (!theme) {\n console.warn(`Theme \"${themeKey}\" not found`)\n return\n }\n\n applyExtendedTheme(theme, mode)\n setCurrentTheme(themeKey)\n setCurrentMode(mode)\n }, [])\n\n return {\n currentTheme,\n currentMode,\n applyTheme,\n resetTheme: resetExtendedTheme,\n }\n }\n}\n\n/**\n * Get Tailwind CSS color references for extended themes\n * Use this in your tailwind.config.ts to enable semantic color classes\n *\n * @example\n * ```ts\n * // tailwind.config.ts\n * import { getTailwindVarReferences } from '@kilivi/payloadcms-theme-management'\n *\n * export default {\n * theme: {\n * extend: {\n * colors: getTailwindVarReferences()\n * }\n * }\n * }\n * ```\n */\nexport function getTailwindVarReferences(): Record<string, unknown> {\n return {\n background: 'hsl(var(--background))',\n foreground: 'hsl(var(--foreground))',\n card: {\n DEFAULT: 'hsl(var(--card))',\n foreground: 'hsl(var(--card-foreground))',\n },\n popover: {\n DEFAULT: 'hsl(var(--popover))',\n foreground: 'hsl(var(--popover-foreground))',\n },\n primary: {\n DEFAULT: 'hsl(var(--primary))',\n foreground: 'hsl(var(--primary-foreground))',\n },\n secondary: {\n DEFAULT: 'hsl(var(--secondary))',\n foreground: 'hsl(var(--secondary-foreground))',\n },\n muted: {\n DEFAULT: 'hsl(var(--muted))',\n foreground: 'hsl(var(--muted-foreground))',\n },\n accent: {\n DEFAULT: 'hsl(var(--accent))',\n foreground: 'hsl(var(--accent-foreground))',\n },\n destructive: {\n DEFAULT: 'hsl(var(--destructive))',\n foreground: 'hsl(var(--destructive-foreground))',\n },\n border: 'hsl(var(--border))',\n input: 'hsl(var(--input))',\n ring: 'hsl(var(--ring))',\n chart: {\n '1': 'hsl(var(--chart-1))',\n '2': 'hsl(var(--chart-2))',\n '3': 'hsl(var(--chart-3))',\n '4': 'hsl(var(--chart-4))',\n '5': 'hsl(var(--chart-5))',\n },\n }\n}\n"],"names":["React","applyExtendedTheme","theme","mode","document","console","warn","tokens","styles","root","documentElement","Object","entries","forEach","key","value","style","setProperty","generateExtendedThemeCSS","lightVars","light","map","join","darkVars","dark","resetExtendedTheme","allVars","varName","removeProperty","getExtendedThemeTokens","keys","result","createUseExtendedTheme","presets","useExtendedTheme","currentTheme","setCurrentTheme","useState","currentMode","setCurrentMode","applyTheme","useCallback","themeKey","resetTheme","getTailwindVarReferences","background","foreground","card","DEFAULT","popover","primary","secondary","muted","accent","destructive","border","input","ring","chart"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kilivi/payloadcms-theme-management",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "Theme configuration plugin for Payload CMS v3 with 60+ professional themes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"payload": "^3.0.0",
|
|
57
57
|
"@payloadcms/ui": "^3.0.0",
|
|
58
|
+
"next": "^14.0.0 || ^15.0.0",
|
|
58
59
|
"react": "^18.3.0 || ^19.0.0-rc",
|
|
59
60
|
"react-dom": "^18.3.0 || ^19.0.0-rc"
|
|
60
61
|
},
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
"jest": "^30.2.0",
|
|
77
78
|
"jest-environment-jsdom": "^30.2.0",
|
|
78
79
|
"mongodb-memory-server": "^10.2.3",
|
|
80
|
+
"next": "^15.0.0",
|
|
79
81
|
"payload": "^3.58.0",
|
|
80
82
|
"playwright": "^1.56.0",
|
|
81
83
|
"postcss": "^8.5.0",
|