@kilivi/payloadcms-theme-management 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +318 -847
- package/dist/components/AppearanceSessionControls.d.ts +15 -0
- package/dist/components/AppearanceSessionControls.d.ts.map +1 -0
- package/dist/components/AppearanceSessionControls.js +152 -0
- package/dist/components/AppearanceSessionControls.js.map +1 -0
- package/dist/components/ColorModeToggle.css +42 -0
- package/dist/components/ColorModeToggle.d.ts +40 -0
- package/dist/components/ColorModeToggle.d.ts.map +1 -0
- package/dist/components/ColorModeToggle.js +111 -0
- package/dist/components/ColorModeToggle.js.map +1 -0
- package/dist/components/ServerThemeInjector.d.ts.map +1 -1
- package/dist/components/ServerThemeInjector.js +41 -14
- package/dist/components/ServerThemeInjector.js.map +1 -1
- package/dist/components/ThemeLivePreviewSync.d.ts +1 -0
- package/dist/components/ThemeLivePreviewSync.d.ts.map +1 -1
- package/dist/components/ThemeLivePreviewSync.js +1 -1
- package/dist/components/ThemeLivePreviewSync.js.map +1 -1
- package/dist/components/ThemeSwitcher.d.ts +36 -0
- package/dist/components/ThemeSwitcher.d.ts.map +1 -0
- package/dist/components/ThemeSwitcher.js +89 -0
- package/dist/components/ThemeSwitcher.js.map +1 -0
- package/dist/components/typographyPreviewUtils.d.ts.map +1 -1
- package/dist/components/typographyPreviewUtils.js +8 -1
- package/dist/components/typographyPreviewUtils.js.map +1 -1
- package/dist/constants/devicePreviews.d.ts +35 -0
- package/dist/constants/devicePreviews.d.ts.map +1 -0
- package/dist/constants/devicePreviews.js +43 -0
- package/dist/constants/devicePreviews.js.map +1 -0
- package/dist/constants/themeFonts.js +9 -9
- package/dist/constants/themeFonts.js.map +1 -1
- package/dist/extended-presets.d.ts +0 -4
- package/dist/extended-presets.d.ts.map +1 -1
- package/dist/extended-presets.js +1 -18
- package/dist/extended-presets.js.map +1 -1
- package/dist/extended-themes.d.ts.map +1 -1
- package/dist/extended-themes.js +428 -0
- package/dist/extended-themes.js.map +1 -1
- package/dist/fields/AccessibilityAuditField.d.ts +2 -0
- package/dist/fields/AccessibilityAuditField.d.ts.map +1 -0
- package/dist/fields/AccessibilityAuditField.js +303 -0
- package/dist/fields/AccessibilityAuditField.js.map +1 -0
- package/dist/fields/AppearancePreviewField.d.ts +2 -0
- package/dist/fields/AppearancePreviewField.d.ts.map +1 -0
- package/dist/fields/AppearancePreviewField.js +763 -0
- package/dist/fields/AppearancePreviewField.js.map +1 -0
- package/dist/fields/ColorPickerField.d.ts.map +1 -1
- package/dist/fields/ColorPickerField.js +89 -85
- package/dist/fields/ColorPickerField.js.map +1 -1
- package/dist/fields/FontSelectField.d.ts +6 -0
- package/dist/fields/FontSelectField.d.ts.map +1 -1
- package/dist/fields/FontSelectField.js +195 -122
- package/dist/fields/FontSelectField.js.map +1 -1
- package/dist/fields/PaletteGeneratorField.d.ts +7 -0
- package/dist/fields/PaletteGeneratorField.d.ts.map +1 -0
- package/dist/fields/PaletteGeneratorField.js +327 -0
- package/dist/fields/PaletteGeneratorField.js.map +1 -0
- package/dist/fields/StylePresetField.d.ts +3 -0
- package/dist/fields/StylePresetField.d.ts.map +1 -0
- package/dist/fields/StylePresetField.js +462 -0
- package/dist/fields/StylePresetField.js.map +1 -0
- package/dist/fields/ThemeColorPickerField.css +115 -35
- package/dist/fields/ThemeColorPickerField.d.ts.map +1 -1
- package/dist/fields/ThemeColorPickerField.js +204 -70
- package/dist/fields/ThemeColorPickerField.js.map +1 -1
- package/dist/fields/ThemeExportField.d.ts +6 -0
- package/dist/fields/ThemeExportField.d.ts.map +1 -0
- package/dist/fields/ThemeExportField.js +202 -0
- package/dist/fields/ThemeExportField.js.map +1 -0
- package/dist/fields/ThemePresetImportField.d.ts.map +1 -1
- package/dist/fields/ThemePresetImportField.js +56 -25
- package/dist/fields/ThemePresetImportField.js.map +1 -1
- package/dist/fields/ThemePreviewField.css +382 -5
- package/dist/fields/ThemePreviewField.d.ts.map +1 -1
- package/dist/fields/ThemePreviewField.js +268 -170
- package/dist/fields/ThemePreviewField.js.map +1 -1
- package/dist/fields/ThemeTokenSelectField.d.ts.map +1 -1
- package/dist/fields/ThemeTokenSelectField.js +278 -80
- package/dist/fields/ThemeTokenSelectField.js.map +1 -1
- package/dist/fields/colorFieldHelpers.d.ts +90 -0
- package/dist/fields/colorFieldHelpers.d.ts.map +1 -0
- package/dist/fields/colorFieldHelpers.js +133 -0
- package/dist/fields/colorFieldHelpers.js.map +1 -0
- package/dist/fields/colorModeFields.d.ts +1 -1
- package/dist/fields/colorModeFields.d.ts.map +1 -1
- package/dist/fields/colorModeFields.js +49 -7
- package/dist/fields/colorModeFields.js.map +1 -1
- package/dist/fields/themeConfigurationField.d.ts +8 -1
- package/dist/fields/themeConfigurationField.d.ts.map +1 -1
- package/dist/fields/themeConfigurationField.js +961 -29
- package/dist/fields/themeConfigurationField.js.map +1 -1
- package/dist/globals/ThemeSettings/FontPicker/FontPreview.d.ts.map +1 -1
- package/dist/globals/ThemeSettings/FontPicker/FontPreview.js +8 -4
- package/dist/globals/ThemeSettings/FontPicker/FontPreview.js.map +1 -1
- package/dist/globals/ThemeSettings/FontPicker/index.d.ts.map +1 -1
- package/dist/globals/ThemeSettings/FontPicker/index.js +81 -26
- package/dist/globals/ThemeSettings/FontPicker/index.js.map +1 -1
- package/dist/globals/ThemeSettings/FontPicker/styles.css +98 -41
- package/dist/globals/ThemeSettings/FontPicker/translations.d.ts +6 -0
- package/dist/globals/ThemeSettings/FontPicker/translations.d.ts.map +1 -1
- package/dist/globals/ThemeSettings/FontPicker/translations.js +14 -2
- package/dist/globals/ThemeSettings/FontPicker/translations.js.map +1 -1
- package/dist/globals/ThemeSettings/config.d.ts.map +1 -1
- package/dist/globals/ThemeSettings/config.js +76 -79
- package/dist/globals/ThemeSettings/config.js.map +1 -1
- package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.d.ts.map +1 -1
- package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.js +63 -62
- package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.js.map +1 -1
- package/dist/globals/ThemeSettings/fields/TypographyInfoField.d.ts.map +1 -1
- package/dist/globals/ThemeSettings/fields/TypographyInfoField.js +22 -25
- package/dist/globals/ThemeSettings/fields/TypographyInfoField.js.map +1 -1
- package/dist/hooks/useAppearanceSession.d.ts +50 -0
- package/dist/hooks/useAppearanceSession.d.ts.map +1 -0
- package/dist/hooks/useAppearanceSession.js +91 -0
- package/dist/hooks/useAppearanceSession.js.map +1 -0
- package/dist/hooks/useThemeTranslations.d.ts +13 -0
- package/dist/hooks/useThemeTranslations.d.ts.map +1 -0
- package/dist/hooks/useThemeTranslations.js +22 -0
- package/dist/hooks/useThemeTranslations.js.map +1 -0
- package/dist/i18n.d.ts +53 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +53 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -29
- package/dist/index.js.map +1 -1
- package/dist/payload-types.d.ts +24 -0
- package/dist/payload-types.d.ts.map +1 -1
- package/dist/payload-types.js.map +1 -1
- package/dist/presets.d.ts +8 -0
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js.map +1 -1
- package/dist/providers/Theme/index.d.ts.map +1 -1
- package/dist/providers/Theme/index.js +75 -11
- package/dist/providers/Theme/index.js.map +1 -1
- package/dist/providers/Theme/themeConfig.d.ts.map +1 -1
- package/dist/providers/Theme/themeConfig.js +104 -0
- package/dist/providers/Theme/themeConfig.js.map +1 -1
- package/dist/providers/Theme/types.d.ts +1 -1
- package/dist/providers/Theme/types.d.ts.map +1 -1
- package/dist/providers/Theme/types.js +10 -1
- package/dist/providers/Theme/types.js.map +1 -1
- package/dist/style-presets.d.ts +66 -0
- package/dist/style-presets.d.ts.map +1 -0
- package/dist/style-presets.js +556 -0
- package/dist/style-presets.js.map +1 -0
- package/dist/translations.d.ts +159 -1
- package/dist/translations.d.ts.map +1 -1
- package/dist/translations.js +310 -9
- package/dist/translations.js.map +1 -1
- package/dist/tweakcn-special-themes.d.ts.map +1 -1
- package/dist/tweakcn-special-themes.js +585 -9
- package/dist/tweakcn-special-themes.js.map +1 -1
- package/dist/types/css.d.js +3 -0
- package/dist/types/css.d.js.map +1 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utilities/getThemeSettings.d.ts.map +1 -1
- package/dist/utilities/getThemeSettings.js +2 -14
- package/dist/utilities/getThemeSettings.js.map +1 -1
- package/dist/utils/contrast.d.ts +63 -0
- package/dist/utils/contrast.d.ts.map +1 -0
- package/dist/utils/contrast.js +158 -0
- package/dist/utils/contrast.js.map +1 -0
- package/dist/utils/exportTokens.d.ts +30 -0
- package/dist/utils/exportTokens.d.ts.map +1 -0
- package/dist/utils/exportTokens.js +183 -0
- package/dist/utils/exportTokens.js.map +1 -0
- package/dist/utils/generatePalette.d.ts +46 -0
- package/dist/utils/generatePalette.d.ts.map +1 -0
- package/dist/utils/generatePalette.js +127 -0
- package/dist/utils/generatePalette.js.map +1 -0
- package/dist/utils/generateThemeCSS.d.ts.map +1 -1
- package/dist/utils/generateThemeCSS.js +2 -40
- package/dist/utils/generateThemeCSS.js.map +1 -1
- package/dist/utils/livePreviewThemeRuntime.d.ts +16 -2
- package/dist/utils/livePreviewThemeRuntime.d.ts.map +1 -1
- package/dist/utils/livePreviewThemeRuntime.js +37 -14
- package/dist/utils/livePreviewThemeRuntime.js.map +1 -1
- package/dist/utils/localizedText.d.ts +8 -0
- package/dist/utils/localizedText.d.ts.map +1 -0
- package/dist/utils/localizedText.js +21 -0
- package/dist/utils/localizedText.js.map +1 -0
- package/dist/utils/resolveThemeConfiguration.d.ts +45 -1
- package/dist/utils/resolveThemeConfiguration.d.ts.map +1 -1
- package/dist/utils/resolveThemeConfiguration.js +12 -5
- package/dist/utils/resolveThemeConfiguration.js.map +1 -1
- package/dist/utils/themeColors.d.ts +3 -0
- package/dist/utils/themeColors.d.ts.map +1 -1
- package/dist/utils/themeColors.js +30 -1
- package/dist/utils/themeColors.js.map +1 -1
- package/dist/utils/themeHtmlAttributes.d.ts +32 -12
- package/dist/utils/themeHtmlAttributes.d.ts.map +1 -1
- package/dist/utils/themeHtmlAttributes.js +38 -5
- package/dist/utils/themeHtmlAttributes.js.map +1 -1
- package/dist/utils/themeUtils.d.ts +3 -5
- package/dist/utils/themeUtils.d.ts.map +1 -1
- package/dist/utils/themeUtils.js +416 -6
- package/dist/utils/themeUtils.js.map +1 -1
- package/dist/utils/tweakcnConverter.d.ts +1 -3
- package/dist/utils/tweakcnConverter.d.ts.map +1 -1
- package/dist/utils/tweakcnConverter.js +1 -3
- package/dist/utils/tweakcnConverter.js.map +1 -1
- package/package.json +11 -5
- package/dist/components/ThemePreview.d.ts +0 -3
- package/dist/components/ThemePreview.d.ts.map +0 -1
- package/dist/components/ThemePreview.js +0 -321
- package/dist/components/ThemePreview.js.map +0 -1
- package/dist/plugin.d.ts +0 -2
- package/dist/plugin.d.ts.map +0 -1
- package/dist/plugin.js +0 -3
- package/dist/plugin.js.map +0 -1
|
@@ -2,17 +2,26 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import './ThemePreviewField.css';
|
|
4
4
|
import { useField, useForm, useFormFields } from '@payloadcms/ui';
|
|
5
|
-
import
|
|
5
|
+
import * as _PayloadUI from '@payloadcms/ui';
|
|
6
|
+
import { ChevronDown, ChevronRight, Moon, Palette, Sun } from 'lucide-react';
|
|
7
|
+
import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
6
8
|
import { resolveTypographyPreview } from '../components/typographyPreviewUtils.js';
|
|
7
9
|
import { allThemePresets } from '../index.js';
|
|
10
|
+
import { useThemeLanguage, useThemeTranslations } from '../hooks/useThemeTranslations.js';
|
|
11
|
+
import PaletteGeneratorField from './PaletteGeneratorField.js';
|
|
8
12
|
import { borderRadiusPresets } from '../providers/Theme/themeConfig.js';
|
|
9
|
-
import { getTranslations } from '../translations.js';
|
|
10
|
-
import { getAdminLanguage } from '../utils/getAdminLanguage.js';
|
|
11
13
|
import { darkModeDefaults, lightModeDefaults } from './colorModeFields.js';
|
|
14
|
+
import { parseThemePresetInput } from '../utils/customThemePresets.js';
|
|
15
|
+
import { resolveLocalizedText } from '../utils/localizedText.js';
|
|
16
|
+
import { AppearanceLocks, AppearanceUndoBar } from '../components/AppearanceSessionControls.js';
|
|
17
|
+
import { createBulkWriter, getAppearanceSession } from '../hooks/useAppearanceSession.js';
|
|
18
|
+
const _useLivePreviewContext = _PayloadUI.useLivePreviewContext ?? function useLivePreviewContextFallback() {
|
|
19
|
+
return {};
|
|
20
|
+
};
|
|
12
21
|
// FALLBACK_THEME and runtime theme presets are computed inside the component
|
|
13
22
|
const colorKeys = Object.keys(lightModeDefaults);
|
|
14
|
-
function ModePreview({ icon, title, colors, typography, radius }) {
|
|
15
|
-
const t =
|
|
23
|
+
function ModePreview({ icon: Icon, title, colors, typography, radius }) {
|
|
24
|
+
const t = useThemeTranslations();
|
|
16
25
|
const background = colors.background ?? '#ffffff';
|
|
17
26
|
const foreground = colors.foreground ?? '#0a0a0a';
|
|
18
27
|
const borderColor = colors.border ?? 'rgba(148, 163, 184, 0.35)';
|
|
@@ -31,6 +40,8 @@ function ModePreview({ icon, title, colors, typography, radius }) {
|
|
|
31
40
|
const baseFontSize = typography.baseFontSize;
|
|
32
41
|
const lineHeight = typography.lineHeight;
|
|
33
42
|
return /*#__PURE__*/ _jsxs("div", {
|
|
43
|
+
// A picture of the site: its sample buttons must not become dead tab stops.
|
|
44
|
+
ref: (el)=>el?.setAttribute('inert', ''),
|
|
34
45
|
style: {
|
|
35
46
|
display: 'grid',
|
|
36
47
|
gap: '16px',
|
|
@@ -56,9 +67,16 @@ function ModePreview({ icon, title, colors, typography, radius }) {
|
|
|
56
67
|
},
|
|
57
68
|
children: [
|
|
58
69
|
/*#__PURE__*/ _jsxs("span", {
|
|
70
|
+
style: {
|
|
71
|
+
display: 'inline-flex',
|
|
72
|
+
alignItems: 'center',
|
|
73
|
+
gap: '6px'
|
|
74
|
+
},
|
|
59
75
|
children: [
|
|
60
|
-
|
|
61
|
-
|
|
76
|
+
/*#__PURE__*/ _jsx(Icon, {
|
|
77
|
+
size: 13,
|
|
78
|
+
"aria-hidden": true
|
|
79
|
+
}),
|
|
62
80
|
title
|
|
63
81
|
]
|
|
64
82
|
}),
|
|
@@ -133,7 +151,7 @@ function ModePreview({ icon, title, colors, typography, radius }) {
|
|
|
133
151
|
backgroundColor: muted,
|
|
134
152
|
color: mutedForeground
|
|
135
153
|
},
|
|
136
|
-
children:
|
|
154
|
+
children: t.ui.mutedSample
|
|
137
155
|
}),
|
|
138
156
|
/*#__PURE__*/ _jsx("div", {
|
|
139
157
|
style: {
|
|
@@ -144,7 +162,7 @@ function ModePreview({ icon, title, colors, typography, radius }) {
|
|
|
144
162
|
backgroundColor: accent,
|
|
145
163
|
color: accentForeground
|
|
146
164
|
},
|
|
147
|
-
children:
|
|
165
|
+
children: t.ui.accentSample
|
|
148
166
|
})
|
|
149
167
|
]
|
|
150
168
|
}),
|
|
@@ -165,7 +183,7 @@ function ModePreview({ icon, title, colors, typography, radius }) {
|
|
|
165
183
|
fontWeight: 600,
|
|
166
184
|
lineHeight: 1.2
|
|
167
185
|
},
|
|
168
|
-
children:
|
|
186
|
+
children: t.ui.headingSample
|
|
169
187
|
}),
|
|
170
188
|
/*#__PURE__*/ _jsx("p", {
|
|
171
189
|
style: {
|
|
@@ -176,38 +194,43 @@ function ModePreview({ icon, title, colors, typography, radius }) {
|
|
|
176
194
|
color: foreground,
|
|
177
195
|
opacity: 0.85
|
|
178
196
|
},
|
|
179
|
-
children:
|
|
197
|
+
children: t.ui.bodySample
|
|
180
198
|
}),
|
|
181
199
|
/*#__PURE__*/ _jsxs("div", {
|
|
182
200
|
style: {
|
|
183
201
|
display: 'flex',
|
|
184
202
|
flexWrap: 'wrap',
|
|
185
203
|
gap: '10px',
|
|
186
|
-
fontSize: '
|
|
187
|
-
|
|
204
|
+
fontSize: '12px',
|
|
205
|
+
// Follow the previewed palette so the meta line stays readable in dark mode.
|
|
206
|
+
color: mutedForeground
|
|
188
207
|
},
|
|
189
208
|
children: [
|
|
190
209
|
/*#__PURE__*/ _jsxs("span", {
|
|
191
210
|
children: [
|
|
192
|
-
|
|
211
|
+
t.ui.bodyFontLabel,
|
|
212
|
+
": ",
|
|
193
213
|
typography.bodyLabel
|
|
194
214
|
]
|
|
195
215
|
}),
|
|
196
216
|
/*#__PURE__*/ _jsxs("span", {
|
|
197
217
|
children: [
|
|
198
|
-
|
|
218
|
+
t.ui.headingFontLabel,
|
|
219
|
+
": ",
|
|
199
220
|
typography.headingLabel
|
|
200
221
|
]
|
|
201
222
|
}),
|
|
202
223
|
/*#__PURE__*/ _jsxs("span", {
|
|
203
224
|
children: [
|
|
204
|
-
|
|
225
|
+
t.ui.baseSizeLabel,
|
|
226
|
+
": ",
|
|
205
227
|
baseFontSize
|
|
206
228
|
]
|
|
207
229
|
}),
|
|
208
230
|
/*#__PURE__*/ _jsxs("span", {
|
|
209
231
|
children: [
|
|
210
|
-
|
|
232
|
+
t.ui.lineHeightLabel,
|
|
233
|
+
": ",
|
|
211
234
|
lineHeight
|
|
212
235
|
]
|
|
213
236
|
})
|
|
@@ -220,13 +243,43 @@ function ModePreview({ icon, title, colors, typography, radius }) {
|
|
|
220
243
|
}
|
|
221
244
|
export default function ThemePreviewField(props) {
|
|
222
245
|
const { field, path } = props;
|
|
223
|
-
|
|
246
|
+
// useThemePreviewField: explicit opt-out via admin.custom.useThemePreviewField = false
|
|
247
|
+
// Plugin always passes true; advanced users can override to permanently hide the panel.
|
|
248
|
+
const configuredShowPreview = field?.admin?.custom?.useThemePreviewField ?? true;
|
|
249
|
+
// isLivePreviewing: true only when the user actively clicked Preview and the iframe/popup is open.
|
|
250
|
+
// While editing (before clicking Preview), the panel is always shown regardless of livePreview config.
|
|
251
|
+
// Default context value is false, so the panel shows by default even without LivePreview provider.
|
|
252
|
+
const { isLivePreviewing } = _useLivePreviewContext();
|
|
253
|
+
const showPreviewPanel = configuredShowPreview && !isLivePreviewing;
|
|
224
254
|
// If the plugin provided theme presets via admin config, prefer those;
|
|
225
255
|
// support both `field.admin.themePresets` (legacy) and `field.admin.custom.themePresets`
|
|
226
256
|
// otherwise fallback to `allThemePresets`.
|
|
227
257
|
// Prefer presets passed via `admin.custom.themePresets` (set by the field factory / plugin).
|
|
228
258
|
// Fall back to legacy `admin.themePresets` for backwards compatibility, then to `allThemePresets`.
|
|
229
|
-
const
|
|
259
|
+
const configuredThemePresets = field?.admin?.custom?.themePresets ?? field?.admin?.themePresets ?? allThemePresets;
|
|
260
|
+
// Presets imported through the "Custom Theme Presets" JSON field live next to
|
|
261
|
+
// this field. Merge them in (overriding built-ins by name) so they show up in
|
|
262
|
+
// the picker as soon as they are imported.
|
|
263
|
+
const customPresetsPath = [
|
|
264
|
+
...path.split('.').slice(0, -1),
|
|
265
|
+
'customThemePresets'
|
|
266
|
+
].join('.');
|
|
267
|
+
const customPresetsValue = useFormFields(([formState])=>formState?.[customPresetsPath]?.value);
|
|
268
|
+
const baseThemePresets = useMemo(()=>{
|
|
269
|
+
const custom = parseThemePresetInput(customPresetsValue);
|
|
270
|
+
if (custom.length === 0) return configuredThemePresets;
|
|
271
|
+
const byName = new Map(configuredThemePresets.map((preset)=>[
|
|
272
|
+
preset.name,
|
|
273
|
+
preset
|
|
274
|
+
]));
|
|
275
|
+
custom.forEach((preset)=>byName.set(preset.name, preset));
|
|
276
|
+
return [
|
|
277
|
+
...byName.values()
|
|
278
|
+
];
|
|
279
|
+
}, [
|
|
280
|
+
configuredThemePresets,
|
|
281
|
+
customPresetsValue
|
|
282
|
+
]);
|
|
230
283
|
const runtimeThemePresets = useMemo(()=>{
|
|
231
284
|
return baseThemePresets.reduce((acc, preset)=>{
|
|
232
285
|
acc[preset.name] = {
|
|
@@ -240,7 +293,8 @@ export default function ThemePreviewField(props) {
|
|
|
240
293
|
...preset.darkMode ?? {}
|
|
241
294
|
},
|
|
242
295
|
typography: preset.typography,
|
|
243
|
-
borderRadius: preset.borderRadius
|
|
296
|
+
borderRadius: preset.borderRadius,
|
|
297
|
+
visualEffects: preset.visualEffects
|
|
244
298
|
};
|
|
245
299
|
return acc;
|
|
246
300
|
}, {});
|
|
@@ -254,42 +308,84 @@ export default function ThemePreviewField(props) {
|
|
|
254
308
|
const { dispatchFields } = useForm();
|
|
255
309
|
const formFields = useFormFields(([formState])=>formState);
|
|
256
310
|
const hasAppliedInitialPresetRef = useRef(false);
|
|
257
|
-
const
|
|
311
|
+
const [previewOpen, setPreviewOpen] = useState(true);
|
|
312
|
+
// Preset list starts collapsed once a theme is already selected so the
|
|
313
|
+
// preview panel — not the long preset list — dominates the layout.
|
|
314
|
+
const [presetsOpen, setPresetsOpen] = useState(false);
|
|
315
|
+
const { setModified } = useForm();
|
|
316
|
+
/**
|
|
317
|
+
* Write a theme preset into the form, honouring the session locks:
|
|
318
|
+
* colours (Colours lock), visual effects (Style lock). Theme fonts apply
|
|
319
|
+
* implicitly through the "Use theme preset" font option, so with the Fonts lock
|
|
320
|
+
* on we pin the current preset font as a custom value before switching.
|
|
321
|
+
*/ const applyPreset = useCallback((presetName, writer)=>{
|
|
258
322
|
const preset = runtimeThemePresets[presetName];
|
|
259
323
|
if (!preset) {
|
|
260
324
|
return;
|
|
261
325
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
326
|
+
const { locks } = getAppearanceSession();
|
|
327
|
+
if (!locks.colors) {
|
|
328
|
+
colorKeys.forEach((key)=>{
|
|
329
|
+
const lightValue = preset.lightMode[key];
|
|
330
|
+
const darkValue = preset.darkMode[key];
|
|
331
|
+
if (lightValue) writer.write(`themeConfiguration.lightMode.${key}`, lightValue);
|
|
332
|
+
if (darkValue) writer.write(`themeConfiguration.darkMode.${key}`, darkValue);
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
if (locks.fonts && selectedTheme) {
|
|
336
|
+
const typographyValue = (key)=>formFields?.[`themeConfiguration.typography.${key}`]?.value;
|
|
337
|
+
const current = resolveTypographyPreview({
|
|
338
|
+
bodyFont: typographyValue('bodyFont'),
|
|
339
|
+
headingFont: typographyValue('headingFont')
|
|
340
|
+
}, selectedTheme);
|
|
341
|
+
const pin = (key, family)=>{
|
|
342
|
+
const value = typographyValue(key);
|
|
343
|
+
if (!value || value === 'preset') {
|
|
344
|
+
writer.write(`themeConfiguration.typography.${key}`, 'custom');
|
|
345
|
+
writer.write(`themeConfiguration.typography.${key}Custom`, family);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
pin('bodyFont', current.bodyFont);
|
|
349
|
+
pin('headingFont', current.headingFont);
|
|
350
|
+
}
|
|
351
|
+
if (!locks.style && preset.visualEffects) {
|
|
352
|
+
const vfKeys = [
|
|
353
|
+
'effectStyle',
|
|
354
|
+
'shadowIntensity',
|
|
355
|
+
'backdropBlur',
|
|
356
|
+
'borderStyle',
|
|
357
|
+
'borderWidth',
|
|
358
|
+
'glassOpacity'
|
|
359
|
+
];
|
|
360
|
+
vfKeys.forEach((key)=>{
|
|
361
|
+
const value = preset.visualEffects?.[key];
|
|
362
|
+
if (value !== undefined) writer.write(`themeConfiguration.visualEffects.${key}`, value);
|
|
363
|
+
});
|
|
364
|
+
}
|
|
280
365
|
}, [
|
|
281
|
-
|
|
366
|
+
runtimeThemePresets,
|
|
367
|
+
formFields,
|
|
368
|
+
selectedTheme
|
|
282
369
|
]);
|
|
283
370
|
const handleThemeSelect = useCallback((value)=>{
|
|
284
|
-
setValue(value);
|
|
285
371
|
if (!value) {
|
|
372
|
+
setValue(value);
|
|
286
373
|
return;
|
|
287
374
|
}
|
|
288
375
|
hasAppliedInitialPresetRef.current = true;
|
|
289
|
-
|
|
376
|
+
const writer = createBulkWriter(formFields, dispatchFields);
|
|
377
|
+
writer.write(path, value);
|
|
378
|
+
applyPreset(value, writer);
|
|
379
|
+
setModified(true);
|
|
380
|
+
writer.commit('theme', runtimeThemePresets[value]?.label ?? value);
|
|
290
381
|
}, [
|
|
291
382
|
applyPreset,
|
|
292
|
-
setValue
|
|
383
|
+
setValue,
|
|
384
|
+
formFields,
|
|
385
|
+
dispatchFields,
|
|
386
|
+
path,
|
|
387
|
+
setModified,
|
|
388
|
+
runtimeThemePresets
|
|
293
389
|
]);
|
|
294
390
|
useEffect(()=>{
|
|
295
391
|
if (!selectedTheme) {
|
|
@@ -330,7 +426,8 @@ export default function ThemePreviewField(props) {
|
|
|
330
426
|
hasAppliedInitialPresetRef.current = true;
|
|
331
427
|
return;
|
|
332
428
|
}
|
|
333
|
-
|
|
429
|
+
// First-time fill of empty colour fields: not an editor action, so no undo entry.
|
|
430
|
+
applyPreset(selectedTheme, createBulkWriter(formFields, dispatchFields));
|
|
334
431
|
hasAppliedInitialPresetRef.current = true;
|
|
335
432
|
}, [
|
|
336
433
|
selectedTheme,
|
|
@@ -396,8 +493,9 @@ export default function ThemePreviewField(props) {
|
|
|
396
493
|
chip: borderRadiusCSS['--radius-default'] ?? '0.75rem',
|
|
397
494
|
pill: borderRadiusCSS['--radius-xl'] ?? borderRadiusCSS['--radius-large'] ?? '999px'
|
|
398
495
|
};
|
|
399
|
-
// Translate labels at runtime based on admin language
|
|
400
|
-
const t =
|
|
496
|
+
// Translate labels at runtime based on the active Payload admin language
|
|
497
|
+
const t = useThemeTranslations();
|
|
498
|
+
const fieldId = `tm-theme-${useId().replace(/:/g, '')}`;
|
|
401
499
|
// Explicitly type keys as `keyof ColorModeColors` to satisfy TypeScript when indexing `lightMode`/`darkMode`
|
|
402
500
|
const highlightSwatches = [
|
|
403
501
|
{
|
|
@@ -421,61 +519,130 @@ export default function ThemePreviewField(props) {
|
|
|
421
519
|
label: t.colors.foreground
|
|
422
520
|
}
|
|
423
521
|
];
|
|
424
|
-
const
|
|
522
|
+
const presetCount = Object.keys(runtimeThemePresets).length;
|
|
523
|
+
const summarySwatches = highlightSwatches.map(({ key })=>activePreset ? activePreset.lightMode[key] : lightModeColors[key]).filter((v)=>typeof v === 'string' && v.trim().length > 0);
|
|
524
|
+
// Field labels/descriptions arrive as `{ en, cs, … }` objects. Resolve them
|
|
525
|
+
// against the active admin language (Payload i18n) instead of hard-coding `en`,
|
|
526
|
+
// otherwise the theme selection stays English in every locale.
|
|
527
|
+
const adminLanguage = useThemeLanguage();
|
|
528
|
+
const resolveLocalized = (value, fallback = '')=>resolveLocalizedText(value, adminLanguage, fallback);
|
|
529
|
+
const fieldLabel = resolveLocalized(field.label, 'Theme');
|
|
530
|
+
const fieldDescription = resolveLocalized(field.admin?.description);
|
|
425
531
|
return /*#__PURE__*/ _jsxs("div", {
|
|
426
532
|
style: {
|
|
427
533
|
marginBottom: '24px'
|
|
428
534
|
},
|
|
429
535
|
children: [
|
|
430
|
-
/*#__PURE__*/ _jsx("label", {
|
|
431
|
-
style: {
|
|
432
|
-
display: 'block',
|
|
433
|
-
marginBottom: '10px',
|
|
434
|
-
fontSize: '13px',
|
|
435
|
-
fontWeight: 600,
|
|
436
|
-
color: 'var(--theme-elevation-800)'
|
|
437
|
-
},
|
|
438
|
-
children: fieldLabel
|
|
439
|
-
}),
|
|
440
536
|
/*#__PURE__*/ _jsxs("div", {
|
|
441
537
|
style: {
|
|
442
538
|
display: 'flex',
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
539
|
+
alignItems: 'center',
|
|
540
|
+
justifyContent: 'space-between',
|
|
541
|
+
marginBottom: '10px'
|
|
446
542
|
},
|
|
447
543
|
children: [
|
|
448
|
-
/*#__PURE__*/
|
|
544
|
+
/*#__PURE__*/ _jsx("div", {
|
|
545
|
+
id: `${fieldId}-label`,
|
|
449
546
|
style: {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
display: 'grid',
|
|
454
|
-
gap: '12px'
|
|
547
|
+
fontSize: '13px',
|
|
548
|
+
fontWeight: 600,
|
|
549
|
+
color: 'var(--theme-elevation-800)'
|
|
455
550
|
},
|
|
551
|
+
children: fieldLabel
|
|
552
|
+
}),
|
|
553
|
+
showPreviewPanel && /*#__PURE__*/ _jsxs("button", {
|
|
554
|
+
type: "button",
|
|
555
|
+
onClick: ()=>setPreviewOpen((v)=>!v),
|
|
556
|
+
className: "theme-preview-toggle",
|
|
557
|
+
"aria-expanded": previewOpen,
|
|
558
|
+
"aria-controls": `${fieldId}-preview`,
|
|
559
|
+
children: [
|
|
560
|
+
previewOpen ? /*#__PURE__*/ _jsx(ChevronDown, {
|
|
561
|
+
size: 12,
|
|
562
|
+
"aria-hidden": true
|
|
563
|
+
}) : /*#__PURE__*/ _jsx(ChevronRight, {
|
|
564
|
+
size: 12,
|
|
565
|
+
"aria-hidden": true
|
|
566
|
+
}),
|
|
567
|
+
previewOpen ? t.ui.hidePreview : t.ui.showPreview
|
|
568
|
+
]
|
|
569
|
+
})
|
|
570
|
+
]
|
|
571
|
+
}),
|
|
572
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
573
|
+
className: `theme-preview-layout${showPreviewPanel && previewOpen ? '' : ' theme-preview-layout--single'}`,
|
|
574
|
+
children: [
|
|
575
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
576
|
+
className: "theme-preset-column",
|
|
456
577
|
children: [
|
|
457
|
-
/*#__PURE__*/ _jsx(
|
|
578
|
+
/*#__PURE__*/ _jsx(AppearanceLocks, {}),
|
|
579
|
+
/*#__PURE__*/ _jsx(AppearanceUndoBar, {
|
|
580
|
+
sources: [
|
|
581
|
+
'theme',
|
|
582
|
+
'palette'
|
|
583
|
+
]
|
|
584
|
+
}),
|
|
585
|
+
/*#__PURE__*/ _jsx(PaletteGeneratorField, {}),
|
|
586
|
+
/*#__PURE__*/ _jsxs("button", {
|
|
587
|
+
type: "button",
|
|
588
|
+
className: `theme-preset-summary${presetsOpen ? ' is-open' : ''}`,
|
|
589
|
+
onClick: ()=>setPresetsOpen((v)=>!v),
|
|
590
|
+
"aria-expanded": presetsOpen,
|
|
591
|
+
"aria-controls": `${fieldId}-presets`,
|
|
592
|
+
"aria-label": activePreset ? `${t.ui.choosePreset}: ${activePreset.label}` : t.ui.choosePreset,
|
|
593
|
+
children: [
|
|
594
|
+
presetsOpen ? /*#__PURE__*/ _jsx(ChevronDown, {
|
|
595
|
+
size: 14,
|
|
596
|
+
"aria-hidden": true
|
|
597
|
+
}) : /*#__PURE__*/ _jsx(ChevronRight, {
|
|
598
|
+
size: 14,
|
|
599
|
+
"aria-hidden": true
|
|
600
|
+
}),
|
|
601
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
602
|
+
className: "theme-preset-summary__info",
|
|
603
|
+
children: [
|
|
604
|
+
/*#__PURE__*/ _jsx("span", {
|
|
605
|
+
className: "theme-label",
|
|
606
|
+
children: activePreset?.label ?? t.ui.choosePreset
|
|
607
|
+
}),
|
|
608
|
+
/*#__PURE__*/ _jsx("span", {
|
|
609
|
+
className: "theme-name",
|
|
610
|
+
children: `${presetCount} ${t.ui.presetCount}`
|
|
611
|
+
})
|
|
612
|
+
]
|
|
613
|
+
}),
|
|
614
|
+
/*#__PURE__*/ _jsx("span", {
|
|
615
|
+
className: "theme-swatches",
|
|
616
|
+
"aria-hidden": "true",
|
|
617
|
+
children: summarySwatches.map((color, index)=>/*#__PURE__*/ _jsx("span", {
|
|
618
|
+
className: "color-swatch",
|
|
619
|
+
style: {
|
|
620
|
+
backgroundColor: color
|
|
621
|
+
}
|
|
622
|
+
}, `summary-swatch-${color}-${index}`))
|
|
623
|
+
})
|
|
624
|
+
]
|
|
625
|
+
}),
|
|
626
|
+
presetsOpen && /*#__PURE__*/ _jsx("div", {
|
|
458
627
|
className: "theme-preset-list",
|
|
628
|
+
id: `${fieldId}-presets`,
|
|
629
|
+
role: "group",
|
|
630
|
+
"aria-labelledby": `${fieldId}-label`,
|
|
459
631
|
children: Object.entries(runtimeThemePresets).map(([key, preset])=>{
|
|
460
632
|
const isSelected = key === selectedTheme;
|
|
461
633
|
const swatches = highlightSwatches.map(({ key: colorKey })=>preset.lightMode[colorKey]).filter((v)=>typeof v === 'string' && v.trim().length > 0);
|
|
462
634
|
return /*#__PURE__*/ _jsxs("button", {
|
|
463
635
|
type: "button",
|
|
636
|
+
"aria-pressed": isSelected,
|
|
464
637
|
onClick: ()=>handleThemeSelect(key),
|
|
465
638
|
className: `theme-preset-button ${isSelected ? 'selected' : ''}`,
|
|
466
639
|
children: [
|
|
467
|
-
/*#__PURE__*/
|
|
640
|
+
/*#__PURE__*/ _jsx("div", {
|
|
468
641
|
className: "theme-info",
|
|
469
|
-
children:
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}),
|
|
474
|
-
/*#__PURE__*/ _jsx("span", {
|
|
475
|
-
className: "theme-name",
|
|
476
|
-
children: key
|
|
477
|
-
})
|
|
478
|
-
]
|
|
642
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
643
|
+
className: "theme-label",
|
|
644
|
+
children: preset.label
|
|
645
|
+
})
|
|
479
646
|
}),
|
|
480
647
|
/*#__PURE__*/ _jsx("div", {
|
|
481
648
|
className: "theme-swatches",
|
|
@@ -491,99 +658,19 @@ export default function ThemePreviewField(props) {
|
|
|
491
658
|
}, key);
|
|
492
659
|
})
|
|
493
660
|
}),
|
|
494
|
-
|
|
495
|
-
style: {
|
|
496
|
-
padding: '12px',
|
|
497
|
-
borderRadius: '10px',
|
|
498
|
-
border: '1px solid var(--theme-elevation-200)',
|
|
499
|
-
backgroundColor: 'var(--theme-elevation-50)',
|
|
500
|
-
display: 'grid',
|
|
501
|
-
gap: '8px',
|
|
502
|
-
fontSize: '12px',
|
|
503
|
-
color: 'var(--theme-elevation-600)'
|
|
504
|
-
},
|
|
505
|
-
children: [
|
|
506
|
-
/*#__PURE__*/ _jsx("div", {
|
|
507
|
-
style: {
|
|
508
|
-
fontWeight: 600,
|
|
509
|
-
color: 'var(--theme-elevation-800)'
|
|
510
|
-
},
|
|
511
|
-
children: activePreset.label
|
|
512
|
-
}),
|
|
513
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
514
|
-
children: [
|
|
515
|
-
"Primary color: ",
|
|
516
|
-
activePreset.lightMode.primary
|
|
517
|
-
]
|
|
518
|
-
}),
|
|
519
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
520
|
-
children: [
|
|
521
|
-
"Accent color: ",
|
|
522
|
-
activePreset.lightMode.accent
|
|
523
|
-
]
|
|
524
|
-
}),
|
|
525
|
-
/*#__PURE__*/ _jsx("div", {
|
|
526
|
-
style: {
|
|
527
|
-
display: 'flex',
|
|
528
|
-
flexWrap: 'wrap',
|
|
529
|
-
gap: '12px',
|
|
530
|
-
marginTop: '4px'
|
|
531
|
-
},
|
|
532
|
-
children: highlightSwatches.map(({ key, label })=>{
|
|
533
|
-
const swatchValue = activePreset.lightMode[key];
|
|
534
|
-
if (!swatchValue) {
|
|
535
|
-
return null;
|
|
536
|
-
}
|
|
537
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
538
|
-
style: {
|
|
539
|
-
display: 'flex',
|
|
540
|
-
alignItems: 'center',
|
|
541
|
-
gap: '6px',
|
|
542
|
-
minWidth: 'fit-content'
|
|
543
|
-
},
|
|
544
|
-
children: [
|
|
545
|
-
/*#__PURE__*/ _jsx("span", {
|
|
546
|
-
style: {
|
|
547
|
-
width: '22px',
|
|
548
|
-
height: '22px',
|
|
549
|
-
borderRadius: '999px',
|
|
550
|
-
border: '1px solid rgba(15, 23, 42, 0.12)',
|
|
551
|
-
backgroundColor: swatchValue,
|
|
552
|
-
boxShadow: 'inset 0 1px 2px rgba(15, 23, 42, 0.08)'
|
|
553
|
-
},
|
|
554
|
-
"aria-hidden": "true"
|
|
555
|
-
}),
|
|
556
|
-
/*#__PURE__*/ _jsx("span", {
|
|
557
|
-
style: {
|
|
558
|
-
fontSize: '11px',
|
|
559
|
-
color: 'var(--theme-elevation-700)'
|
|
560
|
-
},
|
|
561
|
-
children: label
|
|
562
|
-
})
|
|
563
|
-
]
|
|
564
|
-
}, key);
|
|
565
|
-
})
|
|
566
|
-
})
|
|
567
|
-
]
|
|
568
|
-
}),
|
|
569
|
-
field.admin?.description && /*#__PURE__*/ _jsx("div", {
|
|
661
|
+
fieldDescription && /*#__PURE__*/ _jsx("div", {
|
|
570
662
|
style: {
|
|
571
663
|
fontSize: '12px',
|
|
572
664
|
color: 'var(--theme-elevation-600)',
|
|
573
665
|
lineHeight: 1.5
|
|
574
666
|
},
|
|
575
|
-
children:
|
|
667
|
+
children: fieldDescription
|
|
576
668
|
})
|
|
577
669
|
]
|
|
578
670
|
}),
|
|
579
|
-
showPreviewPanel && /*#__PURE__*/ _jsx("div", {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
minWidth: '320px',
|
|
583
|
-
position: 'sticky',
|
|
584
|
-
top: '96px',
|
|
585
|
-
alignSelf: 'flex-start'
|
|
586
|
-
},
|
|
671
|
+
showPreviewPanel && previewOpen && /*#__PURE__*/ _jsx("div", {
|
|
672
|
+
className: "theme-preview-panel",
|
|
673
|
+
id: `${fieldId}-preview`,
|
|
587
674
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
588
675
|
style: {
|
|
589
676
|
width: '100%',
|
|
@@ -607,15 +694,26 @@ export default function ThemePreviewField(props) {
|
|
|
607
694
|
color: 'var(--theme-elevation-800)'
|
|
608
695
|
},
|
|
609
696
|
children: [
|
|
610
|
-
/*#__PURE__*/
|
|
611
|
-
|
|
697
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
698
|
+
style: {
|
|
699
|
+
display: 'inline-flex',
|
|
700
|
+
alignItems: 'center',
|
|
701
|
+
gap: '6px'
|
|
702
|
+
},
|
|
703
|
+
children: [
|
|
704
|
+
/*#__PURE__*/ _jsx(Palette, {
|
|
705
|
+
size: 14,
|
|
706
|
+
"aria-hidden": true
|
|
707
|
+
}),
|
|
708
|
+
t.ui.colorsAndTypography
|
|
709
|
+
]
|
|
612
710
|
}),
|
|
613
711
|
/*#__PURE__*/ _jsx("span", {
|
|
614
712
|
style: {
|
|
615
713
|
fontSize: '12px',
|
|
616
714
|
color: 'var(--theme-elevation-600)'
|
|
617
715
|
},
|
|
618
|
-
children: activePreset ? activePreset.label :
|
|
716
|
+
children: activePreset ? activePreset.label : t.preview.customPalette
|
|
619
717
|
})
|
|
620
718
|
]
|
|
621
719
|
}),
|
|
@@ -626,15 +724,15 @@ export default function ThemePreviewField(props) {
|
|
|
626
724
|
},
|
|
627
725
|
children: [
|
|
628
726
|
/*#__PURE__*/ _jsx(ModePreview, {
|
|
629
|
-
icon:
|
|
630
|
-
title:
|
|
727
|
+
icon: Sun,
|
|
728
|
+
title: t.ui.lightMode,
|
|
631
729
|
colors: lightModeColors,
|
|
632
730
|
typography: previewTypography,
|
|
633
731
|
radius: previewRadius
|
|
634
732
|
}),
|
|
635
733
|
/*#__PURE__*/ _jsx(ModePreview, {
|
|
636
|
-
icon:
|
|
637
|
-
title:
|
|
734
|
+
icon: Moon,
|
|
735
|
+
title: t.ui.darkMode,
|
|
638
736
|
colors: darkModeColors,
|
|
639
737
|
typography: previewTypography,
|
|
640
738
|
radius: previewRadius
|