@kilivi/payloadcms-theme-management 1.1.1 → 2.0.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 +48 -31
- package/dist/components/ThemeLivePreviewSync.d.ts +11 -0
- package/dist/components/ThemeLivePreviewSync.d.ts.map +1 -0
- package/dist/components/ThemeLivePreviewSync.js +40 -0
- package/dist/components/ThemeLivePreviewSync.js.map +1 -0
- package/dist/fields/ThemeColorPickerField.d.ts.map +1 -1
- package/dist/fields/ThemeColorPickerField.js +26 -9
- package/dist/fields/ThemeColorPickerField.js.map +1 -1
- package/dist/fields/ThemePreviewField.d.ts.map +1 -1
- package/dist/fields/ThemePreviewField.js +2 -1
- package/dist/fields/ThemePreviewField.js.map +1 -1
- package/dist/fields/themeConfigurationField.d.ts +1 -0
- package/dist/fields/themeConfigurationField.d.ts.map +1 -1
- package/dist/fields/themeConfigurationField.js +3 -2
- package/dist/fields/themeConfigurationField.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +414 -16
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +30 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +42 -0
- package/dist/server.js.map +1 -1
- package/dist/types/color.d.js +2 -0
- package/dist/types/color.d.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/livePreviewRefresh.d.ts +17 -0
- package/dist/utils/livePreviewRefresh.d.ts.map +1 -0
- package/dist/utils/livePreviewRefresh.js +20 -0
- package/dist/utils/livePreviewRefresh.js.map +1 -0
- package/dist/utils/livePreviewThemeRuntime.d.ts +7 -0
- package/dist/utils/livePreviewThemeRuntime.d.ts.map +1 -0
- package/dist/utils/livePreviewThemeRuntime.js +58 -0
- package/dist/utils/livePreviewThemeRuntime.js.map +1 -0
- package/package.json +1 -1
|
@@ -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 admin.custom property to avoid ts-expect-error\ninterface ThemeAdminCustom {\n themePresets?: ThemePreset[]\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 } as ThemeAdminCustom,\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 dbName: 'border_radius',\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 dbName: 'spacing',\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 dbName: 'color_mode',\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 options: [...BODY_FONT_OPTIONS],\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n dbName: 'body_font',\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 options: [...HEADING_FONT_OPTIONS],\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n dbName: 'heading_font',\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 dbName: 'base_font_size',\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 dbName: 'line_height',\n },\n ],\n },\n ],\n },\n ],\n })\n\n const customPresetsField: Field = {\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 fields.push(customPresetsField)\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 dbName: 'animation_level',\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","dbName","initCollapsed","placeholder","condition","_","siblingData","bodyFont","headingFont","map","option","customPresetsField","advancedFields","language"],"mappings":"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"}
|
|
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 useThemePreviewField?: boolean\n}\n\n// Type for admin.custom property to avoid ts-expect-error\ninterface ThemeAdminCustom {\n themePresets?: ThemePreset[]\n useThemePreviewField?: 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 useThemePreviewField = 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 useThemePreviewField,\n } as ThemeAdminCustom,\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 dbName: 'border_radius',\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 dbName: 'spacing',\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 dbName: 'color_mode',\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 options: [...BODY_FONT_OPTIONS],\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n dbName: 'body_font',\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 options: [...HEADING_FONT_OPTIONS],\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n dbName: 'heading_font',\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 dbName: 'base_font_size',\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 dbName: 'line_height',\n },\n ],\n },\n ],\n },\n ],\n })\n\n const customPresetsField: Field = {\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 fields.push(customPresetsField)\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 dbName: 'animation_level',\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","useThemePreviewField","fields","name","type","required","defaultValue","label","en","cs","validate","value","trim","length","admin","description","components","Field","custom","push","width","dbName","initCollapsed","placeholder","condition","_","siblingData","bodyFont","headingFont","map","option","customPresetsField","advancedFields","language"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type { ThemeTab } from './fields/themeConfigurationField.js';
|
|
|
13
13
|
export { extendedThemePresets, allExtendedThemePresets, extendedThemeToCSSVariables, } from './extended-presets.js';
|
|
14
14
|
export type { ExtendedThemePreset, ShadcnColorTokens } from './extended-presets.js';
|
|
15
15
|
export { applyExtendedTheme, generateExtendedThemeCSS, resetExtendedTheme, getExtendedThemeTokens, createUseExtendedTheme, getTailwindVarReferences, } from './utils/extendedThemeHelpers.js';
|
|
16
|
-
export type { ThemeManagementPluginOptions, FetchThemeConfigurationOptions } from './types.js';
|
|
16
|
+
export type { ThemeManagementPluginOptions, ThemeManagementCacheRevalidationOptions, FetchThemeConfigurationOptions, ThemeManagementLivePreviewOptions, ThemeManagementLivePreviewUrlArgs, } from './types.js';
|
|
17
17
|
export { DEFAULT_THEME_CONFIGURATION, resolveThemeConfiguration, } from './utils/resolveThemeConfiguration.js';
|
|
18
18
|
export type { ResolvedThemeConfiguration } from './utils/resolveThemeConfiguration.js';
|
|
19
19
|
export { generateThemeColorsCss, hexToHsl } from './utils/themeColors.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiD,MAAM,EAAa,MAAM,SAAS,CAAA;AAG/F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG/C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiD,MAAM,EAAa,MAAM,SAAS,CAAA;AAG/F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG/C,OAAO,KAAK,EACV,8BAA8B,EAG9B,4BAA4B,EAC7B,MAAM,YAAY,CAAA;AA0nBnB,eAAO,MAAM,qBAAqB,GAAI,UAAS,4BAAiC,KAAG,MAyOlF,CAAA;AAED,KAAK,iBAAiB,GAAG,MAAM,GAAG,8BAA8B,CAAA;AAEhE,eAAO,MAAM,uBAAuB,GAClC,UAAU,iBAAiB,KAC1B,OAAO,CAAC,sBAAsB,GAAG,IAAI,CA6EvC,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,KAAG,WAAW,GAAG,IAEhE,CAAA;AAED,eAAO,MAAM,wBAAwB,QAAO,WAAW,EAEtD,CAAA;AAED,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnE,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACtE,YAAY,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACnF,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EACV,4BAA4B,EAC5B,uCAAuC,EACvC,8BAA8B,EAC9B,iCAAiC,EACjC,iCAAiC,GAClC,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,sCAAsC,CAAA;AAC7C,YAAY,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAA;AACtF,OAAO,EAAE,sBAAsB,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACzE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAGvE,OAAO,EACL,gBAAgB,IAAI,iBAAiB,EACrC,oBAAoB,GACrB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAEhF,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAG1D,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,GAChB,MAAM,4BAA4B,CAAA;AAMnC,eAAO,MAAM,qBAAqB,aA9Xa,4BAA4B,KAAQ,MA8XzB,CAAA;AAC1D,oDAAoD;AACpD,eAAO,MAAM,wBAAwB,aAhYU,4BAA4B,KAAQ,MAgYtB,CAAA;AAG7D,OAAO,EACL,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,mBAAmB,CAAA;AAE1B,eAAe,qBAAqB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -79,10 +79,380 @@ const THEME_FIELD_NAME = 'themeConfiguration';
|
|
|
79
79
|
return sanitizedFields.map((field)=>field.type === 'tabs' ? updatedTabsField : field);
|
|
80
80
|
};
|
|
81
81
|
const ensureCollectionsArray = (collections)=>Array.isArray(collections) ? collections : [];
|
|
82
|
+
const ensureEndpointsArray = (endpoints)=>Array.isArray(endpoints) ? endpoints : [];
|
|
83
|
+
const normalizeLivePreviewOptions = (livePreview)=>{
|
|
84
|
+
if (livePreview === false) {
|
|
85
|
+
return {
|
|
86
|
+
enabled: false,
|
|
87
|
+
injectRoute: false,
|
|
88
|
+
routePath: '/theme/preview',
|
|
89
|
+
pageCollection: 'pages',
|
|
90
|
+
pageSlug: 'home',
|
|
91
|
+
fallbackToFirstPage: true,
|
|
92
|
+
tenantField: 'tenant',
|
|
93
|
+
tenantQueryParam: 'tenant',
|
|
94
|
+
breakpoints: undefined
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const raw = typeof livePreview === 'object' && livePreview ? livePreview : {};
|
|
98
|
+
return {
|
|
99
|
+
enabled: raw.enabled ?? true,
|
|
100
|
+
injectRoute: raw.injectRoute ?? false,
|
|
101
|
+
routePath: normalizeRoutePath(raw.routePath),
|
|
102
|
+
pageCollection: raw.pageCollection ?? 'pages',
|
|
103
|
+
pageSlug: raw.pageSlug ?? 'home',
|
|
104
|
+
fallbackToFirstPage: raw.fallbackToFirstPage ?? true,
|
|
105
|
+
tenantField: raw.tenantField ?? 'tenant',
|
|
106
|
+
tenantQueryParam: raw.tenantQueryParam ?? 'tenant',
|
|
107
|
+
breakpoints: raw.breakpoints,
|
|
108
|
+
url: raw.url
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
const dedupeStrings = (values)=>[
|
|
112
|
+
...new Set(values.filter((value)=>typeof value === 'string' && value.trim().length > 0))
|
|
113
|
+
];
|
|
114
|
+
const normalizeRoutePath = (path)=>{
|
|
115
|
+
if (!path || !path.trim()) {
|
|
116
|
+
return '/theme/revalidate';
|
|
117
|
+
}
|
|
118
|
+
return path.startsWith('/') ? path : `/${path}`;
|
|
119
|
+
};
|
|
120
|
+
const normalizeCacheRevalidationOptions = (args)=>{
|
|
121
|
+
const { cacheRevalidation, standaloneCollectionSlug, useStandaloneCollection } = args;
|
|
122
|
+
if (cacheRevalidation === false) {
|
|
123
|
+
return {
|
|
124
|
+
enabled: false,
|
|
125
|
+
injectRoute: false,
|
|
126
|
+
routePath: '/theme/revalidate',
|
|
127
|
+
tags: [],
|
|
128
|
+
paths: []
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const raw = typeof cacheRevalidation === 'object' && cacheRevalidation ? cacheRevalidation : {};
|
|
132
|
+
const defaultTag = `global_${standaloneCollectionSlug}`;
|
|
133
|
+
return {
|
|
134
|
+
enabled: raw.enabled ?? useStandaloneCollection,
|
|
135
|
+
injectRoute: raw.injectRoute ?? true,
|
|
136
|
+
routePath: normalizeRoutePath(raw.routePath),
|
|
137
|
+
secret: raw.secret,
|
|
138
|
+
tags: dedupeStrings([
|
|
139
|
+
defaultTag,
|
|
140
|
+
...raw.tags ?? []
|
|
141
|
+
]),
|
|
142
|
+
paths: dedupeStrings(raw.paths ?? [])
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
const asRecord = (value)=>{
|
|
146
|
+
if (value && typeof value === 'object') {
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
return undefined;
|
|
150
|
+
};
|
|
151
|
+
const pickString = (value)=>{
|
|
152
|
+
if (typeof value === 'string' && value.trim()) return value;
|
|
153
|
+
return undefined;
|
|
154
|
+
};
|
|
155
|
+
const extractTenantValue = (value)=>{
|
|
156
|
+
if (!value) return undefined;
|
|
157
|
+
if (typeof value === 'string') return pickString(value);
|
|
158
|
+
const tenantRecord = asRecord(value);
|
|
159
|
+
if (!tenantRecord) return undefined;
|
|
160
|
+
return pickString(tenantRecord.id) ?? pickString(tenantRecord.slug) ?? pickString(tenantRecord.value) ?? pickString(tenantRecord.tenant);
|
|
161
|
+
};
|
|
162
|
+
const resolveTenantSlug = (req, tenantField, data)=>{
|
|
163
|
+
const fromData = extractTenantValue(data?.[tenantField]);
|
|
164
|
+
if (fromData) return fromData;
|
|
165
|
+
const query = req.query;
|
|
166
|
+
if (query && typeof URLSearchParams !== 'undefined' && query instanceof URLSearchParams) {
|
|
167
|
+
return pickString(query.get('tenant')) ?? pickString(query.get('tenantSlug')) ?? pickString(query.get('tenantId'));
|
|
168
|
+
}
|
|
169
|
+
const queryRecord = asRecord(query);
|
|
170
|
+
const fromQuery = extractTenantValue(queryRecord?.tenant) ?? extractTenantValue(queryRecord?.tenantSlug) ?? extractTenantValue(queryRecord?.tenantId);
|
|
171
|
+
if (fromQuery) return fromQuery;
|
|
172
|
+
const userRecord = asRecord(req.user);
|
|
173
|
+
const fromUser = extractTenantValue(userRecord?.[tenantField]) ?? extractTenantValue(userRecord?.tenant) ?? extractTenantValue(userRecord?.tenantSlug);
|
|
174
|
+
if (fromUser) return fromUser;
|
|
175
|
+
const headers = req.headers;
|
|
176
|
+
if (headers && typeof Headers !== 'undefined' && headers instanceof Headers) {
|
|
177
|
+
return pickString(headers.get('x-tenant-id')) ?? pickString(headers.get('x-tenant')) ?? pickString(headers.get('payload-tenant'));
|
|
178
|
+
}
|
|
179
|
+
const headerRecord = asRecord(headers);
|
|
180
|
+
return extractTenantValue(headerRecord?.['x-tenant-id']) ?? extractTenantValue(headerRecord?.['x-tenant']) ?? extractTenantValue(headerRecord?.['payload-tenant']);
|
|
181
|
+
};
|
|
182
|
+
const buildDefaultPreviewPath = (pageSlug, tenantSlug, tenantQueryParam)=>{
|
|
183
|
+
const normalizedSlug = pageSlug === 'home' ? '' : pageSlug;
|
|
184
|
+
const basePath = normalizedSlug ? `/${normalizedSlug}` : '/';
|
|
185
|
+
if (!tenantSlug) {
|
|
186
|
+
return basePath;
|
|
187
|
+
}
|
|
188
|
+
const separator = basePath.includes('?') ? '&' : '?';
|
|
189
|
+
return `${basePath}${separator}${tenantQueryParam}=${encodeURIComponent(tenantSlug)}`;
|
|
190
|
+
};
|
|
191
|
+
const resolveLivePreviewUrl = async (args)=>{
|
|
192
|
+
const { req, data, livePreview, enableLogging } = args;
|
|
193
|
+
const tenantSlug = resolveTenantSlug(req, livePreview.tenantField, data);
|
|
194
|
+
const tenantWhere = tenantSlug ? {
|
|
195
|
+
[livePreview.tenantField]: {
|
|
196
|
+
equals: tenantSlug
|
|
197
|
+
}
|
|
198
|
+
} : undefined;
|
|
199
|
+
const homeWhere = {
|
|
200
|
+
and: [
|
|
201
|
+
{
|
|
202
|
+
slug: {
|
|
203
|
+
equals: livePreview.pageSlug
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
...tenantWhere ? [
|
|
207
|
+
tenantWhere
|
|
208
|
+
] : []
|
|
209
|
+
]
|
|
210
|
+
};
|
|
211
|
+
try {
|
|
212
|
+
const preferredPageResponse = await req.payload.find({
|
|
213
|
+
collection: livePreview.pageCollection,
|
|
214
|
+
where: homeWhere,
|
|
215
|
+
depth: 0,
|
|
216
|
+
draft: true,
|
|
217
|
+
limit: 1,
|
|
218
|
+
overrideAccess: true,
|
|
219
|
+
pagination: false
|
|
220
|
+
});
|
|
221
|
+
let page = preferredPageResponse?.docs?.[0];
|
|
222
|
+
if (!page && livePreview.fallbackToFirstPage) {
|
|
223
|
+
const fallbackResponse = await req.payload.find({
|
|
224
|
+
collection: livePreview.pageCollection,
|
|
225
|
+
where: tenantWhere,
|
|
226
|
+
depth: 0,
|
|
227
|
+
draft: true,
|
|
228
|
+
limit: 1,
|
|
229
|
+
overrideAccess: true,
|
|
230
|
+
pagination: false
|
|
231
|
+
});
|
|
232
|
+
page = fallbackResponse?.docs?.[0];
|
|
233
|
+
}
|
|
234
|
+
const pageRecord = asRecord(page);
|
|
235
|
+
const resolvedPageSlug = pickString(pageRecord?.slug) ?? livePreview.pageSlug;
|
|
236
|
+
if (livePreview.url) {
|
|
237
|
+
return await livePreview.url({
|
|
238
|
+
page: pageRecord,
|
|
239
|
+
pageSlug: resolvedPageSlug,
|
|
240
|
+
tenantSlug,
|
|
241
|
+
req
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return buildDefaultPreviewPath(resolvedPageSlug, tenantSlug, livePreview.tenantQueryParam);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (enableLogging) {
|
|
247
|
+
req.payload.logger?.warn?.('🎨 Theme Management Plugin: live preview URL fallback used', error);
|
|
248
|
+
}
|
|
249
|
+
return buildDefaultPreviewPath(livePreview.pageSlug, tenantSlug, livePreview.tenantQueryParam);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
const createPreviewAdminConfig = (normalizedLivePreview, enableLogging)=>{
|
|
253
|
+
if (!normalizedLivePreview.enabled) {
|
|
254
|
+
return {};
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
livePreview: {
|
|
258
|
+
breakpoints: normalizedLivePreview.breakpoints,
|
|
259
|
+
url: ({ data, req })=>resolveLivePreviewUrl({
|
|
260
|
+
req: req,
|
|
261
|
+
data: asRecord(data),
|
|
262
|
+
livePreview: normalizedLivePreview,
|
|
263
|
+
enableLogging
|
|
264
|
+
})
|
|
265
|
+
},
|
|
266
|
+
preview: (data, options)=>{
|
|
267
|
+
const req = options?.req;
|
|
268
|
+
if (!req) return null;
|
|
269
|
+
return resolveLivePreviewUrl({
|
|
270
|
+
req: req,
|
|
271
|
+
data,
|
|
272
|
+
livePreview: normalizedLivePreview,
|
|
273
|
+
enableLogging
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
const readSecretFromRequest = async (req)=>{
|
|
279
|
+
const requestRecord = asRecord(req);
|
|
280
|
+
const headersValue = requestRecord?.headers;
|
|
281
|
+
if (headersValue && typeof Headers !== 'undefined' && headersValue instanceof Headers) {
|
|
282
|
+
return pickString(headersValue.get('x-theme-revalidate-secret')) ?? pickString(headersValue.get('x-revalidate-secret'));
|
|
283
|
+
}
|
|
284
|
+
const headerRecord = asRecord(headersValue);
|
|
285
|
+
const fromHeaders = pickString(headerRecord?.['x-theme-revalidate-secret']) ?? pickString(headerRecord?.['x-revalidate-secret']);
|
|
286
|
+
if (fromHeaders) {
|
|
287
|
+
return fromHeaders;
|
|
288
|
+
}
|
|
289
|
+
const requestWithJson = req;
|
|
290
|
+
const body = typeof requestWithJson?.json === 'function' ? await requestWithJson.json() : undefined;
|
|
291
|
+
const bodyRecord = asRecord(body);
|
|
292
|
+
const fromBody = pickString(bodyRecord?.secret);
|
|
293
|
+
if (fromBody) {
|
|
294
|
+
return fromBody;
|
|
295
|
+
}
|
|
296
|
+
const queryRecord = asRecord(requestRecord?.query);
|
|
297
|
+
return pickString(queryRecord?.secret);
|
|
298
|
+
};
|
|
299
|
+
const executeThemeRevalidation = async (options)=>{
|
|
300
|
+
const { tags, paths, enableLogging, logger } = options;
|
|
301
|
+
try {
|
|
302
|
+
const { revalidatePath, revalidateTag } = await import('next/cache');
|
|
303
|
+
tags.forEach((tag)=>revalidateTag(tag));
|
|
304
|
+
paths.forEach((path)=>revalidatePath(path));
|
|
305
|
+
if (enableLogging) {
|
|
306
|
+
logger?.info?.(`🎨 Theme Management Plugin: revalidated cache tags=[${tags.join(', ')}] paths=[${paths.join(', ')}]`);
|
|
307
|
+
}
|
|
308
|
+
} catch (error) {
|
|
309
|
+
if (enableLogging) {
|
|
310
|
+
logger?.error?.('🎨 Theme Management Plugin: cache invalidation failed:', error);
|
|
311
|
+
}
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
tags,
|
|
316
|
+
paths
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
const readQueryStringFromRequest = (req, paramName)=>{
|
|
320
|
+
const reqRecord = asRecord(req);
|
|
321
|
+
// Try URLSearchParams from native fetch/Node.js
|
|
322
|
+
const queryValue = reqRecord?.query;
|
|
323
|
+
if (queryValue && typeof URLSearchParams !== 'undefined' && queryValue instanceof URLSearchParams) {
|
|
324
|
+
return pickString(queryValue.get(paramName));
|
|
325
|
+
}
|
|
326
|
+
// Try object form (common in Payload)
|
|
327
|
+
const queryRecord = asRecord(queryValue);
|
|
328
|
+
if (queryRecord) {
|
|
329
|
+
const rawValue = queryRecord[paramName];
|
|
330
|
+
if (Array.isArray(rawValue)) {
|
|
331
|
+
return pickString(rawValue[0]);
|
|
332
|
+
}
|
|
333
|
+
return pickString(rawValue);
|
|
334
|
+
}
|
|
335
|
+
// Try manual URL parsing as fallback
|
|
336
|
+
const urlValue = pickString(reqRecord?.url) ?? pickString(reqRecord?.originalUrl);
|
|
337
|
+
if (urlValue) {
|
|
338
|
+
try {
|
|
339
|
+
const url = new URL(urlValue, 'http://localhost');
|
|
340
|
+
return pickString(url.searchParams.get(paramName));
|
|
341
|
+
} catch {
|
|
342
|
+
// Ignore parse errors
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return undefined;
|
|
346
|
+
};
|
|
347
|
+
const createLivePreviewEndpoint = (options)=>{
|
|
348
|
+
const { livePreview, enableLogging } = options;
|
|
349
|
+
if (!livePreview.enabled || !livePreview.injectRoute) {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
path: livePreview.routePath,
|
|
354
|
+
method: 'get',
|
|
355
|
+
handler: async (req)=>{
|
|
356
|
+
const previewSecret = process.env.PREVIEW_SECRET ?? process.env.PAYLOAD_PREVIEW_SECRET;
|
|
357
|
+
if (previewSecret) {
|
|
358
|
+
const providedSecret = readQueryStringFromRequest(req, 'previewSecret') ?? readQueryStringFromRequest(req, 'preview');
|
|
359
|
+
if (providedSecret !== previewSecret) {
|
|
360
|
+
return jsonResponse({
|
|
361
|
+
error: 'Unauthorized'
|
|
362
|
+
}, 401);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const pageSlug = readQueryStringFromRequest(req, 'pageSlug') ?? livePreview.pageSlug;
|
|
366
|
+
const tenantSlug = readQueryStringFromRequest(req, 'tenant');
|
|
367
|
+
// Redirect to the actual client page (standard Payload live preview behavior)
|
|
368
|
+
const destination = buildDefaultPreviewPath(pageSlug, tenantSlug, livePreview.tenantQueryParam);
|
|
369
|
+
if (enableLogging) {
|
|
370
|
+
console.log('🎨 Theme Management Plugin: live preview redirect', destination);
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
status: 307,
|
|
374
|
+
headers: {
|
|
375
|
+
Location: destination
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
};
|
|
381
|
+
const createCacheRevalidationEndpoint = (options)=>{
|
|
382
|
+
const { cacheRevalidation, enableLogging } = options;
|
|
383
|
+
if (!cacheRevalidation.enabled || !cacheRevalidation.injectRoute) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
path: cacheRevalidation.routePath,
|
|
388
|
+
method: 'post',
|
|
389
|
+
handler: async (req)=>{
|
|
390
|
+
const reqRecord = asRecord(req);
|
|
391
|
+
const payload = asRecord(reqRecord?.payload);
|
|
392
|
+
const logger = payload?.logger;
|
|
393
|
+
if (cacheRevalidation.secret) {
|
|
394
|
+
const providedSecret = await readSecretFromRequest(req);
|
|
395
|
+
if (providedSecret !== cacheRevalidation.secret) {
|
|
396
|
+
return jsonResponse({
|
|
397
|
+
error: 'Unauthorized'
|
|
398
|
+
}, 401);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
const result = await executeThemeRevalidation({
|
|
403
|
+
tags: cacheRevalidation.tags,
|
|
404
|
+
paths: cacheRevalidation.paths,
|
|
405
|
+
enableLogging,
|
|
406
|
+
logger
|
|
407
|
+
});
|
|
408
|
+
return jsonResponse({
|
|
409
|
+
ok: true,
|
|
410
|
+
revalidated: result
|
|
411
|
+
});
|
|
412
|
+
} catch {
|
|
413
|
+
return jsonResponse({
|
|
414
|
+
ok: false,
|
|
415
|
+
error: 'Revalidation failed'
|
|
416
|
+
}, 500);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
};
|
|
421
|
+
const mergeEndpoint = (endpoints, endpoint)=>{
|
|
422
|
+
if (!endpoint) {
|
|
423
|
+
return endpoints;
|
|
424
|
+
}
|
|
425
|
+
const record = endpoint;
|
|
426
|
+
const withoutExisting = endpoints.filter((candidate)=>{
|
|
427
|
+
const candidateRecord = candidate;
|
|
428
|
+
return !(candidateRecord?.path === record?.path && typeof candidateRecord.method === 'string' && typeof record.method === 'string' && candidateRecord.method.toLowerCase() === record.method.toLowerCase());
|
|
429
|
+
});
|
|
430
|
+
return [
|
|
431
|
+
...withoutExisting,
|
|
432
|
+
endpoint
|
|
433
|
+
];
|
|
434
|
+
};
|
|
435
|
+
const jsonResponse = (body, status = 200)=>{
|
|
436
|
+
if (typeof Response !== 'undefined' && typeof Response.json === 'function') {
|
|
437
|
+
return Response.json(body, {
|
|
438
|
+
status
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
status,
|
|
443
|
+
json: async ()=>body
|
|
444
|
+
};
|
|
445
|
+
};
|
|
82
446
|
export const themeManagementPlugin = (options = {})=>{
|
|
83
447
|
return (config)=>{
|
|
84
448
|
const { enabled = true, targetCollection = 'site-settings', useStandaloneCollection = false, standaloneCollectionSlug = 'appearance-settings', // Allow either string or localized object; default comes from plugin translations
|
|
85
|
-
standaloneCollectionLabel = getTranslations().standaloneCollectionLabel, themePresets = allThemePresets, defaultTheme = 'cool', includeColorModeToggle = true, includeCustomCSS = true, includeBrandIdentity = false, enableAdvancedFeatures = true, enableLogging = false } = options;
|
|
449
|
+
standaloneCollectionLabel = getTranslations().standaloneCollectionLabel, themePresets = allThemePresets, defaultTheme = 'cool', includeColorModeToggle = true, includeCustomCSS = true, includeBrandIdentity = false, enableAdvancedFeatures = true, enableLogging = false, livePreview = true, cacheRevalidation } = options;
|
|
450
|
+
const normalizedLivePreview = normalizeLivePreviewOptions(livePreview);
|
|
451
|
+
const normalizedCacheRevalidation = normalizeCacheRevalidationOptions({
|
|
452
|
+
cacheRevalidation,
|
|
453
|
+
standaloneCollectionSlug,
|
|
454
|
+
useStandaloneCollection
|
|
455
|
+
});
|
|
86
456
|
if (!enabled) {
|
|
87
457
|
if (enableLogging) {
|
|
88
458
|
console.log('🎨 Theme Management Plugin: disabled via options, skipping.');
|
|
@@ -95,7 +465,8 @@ export const themeManagementPlugin = (options = {})=>{
|
|
|
95
465
|
includeColorModeToggle,
|
|
96
466
|
includeCustomCSS,
|
|
97
467
|
includeBrandIdentity: includeBrandIdentity ?? false,
|
|
98
|
-
enableAdvancedFeatures
|
|
468
|
+
enableAdvancedFeatures,
|
|
469
|
+
useThemePreviewField: !normalizedLivePreview.enabled
|
|
99
470
|
});
|
|
100
471
|
// If using standalone collection, create a new global
|
|
101
472
|
if (useStandaloneCollection) {
|
|
@@ -139,7 +510,8 @@ export const themeManagementPlugin = (options = {})=>{
|
|
|
139
510
|
slug: standaloneCollectionSlug,
|
|
140
511
|
label: standaloneCollectionLabel,
|
|
141
512
|
admin: {
|
|
142
|
-
group: 'Settings'
|
|
513
|
+
group: 'Settings',
|
|
514
|
+
...createPreviewAdminConfig(normalizedLivePreview, enableLogging)
|
|
143
515
|
},
|
|
144
516
|
// Provide a broad access object but cast to GlobalConfig['access'] to satisfy types
|
|
145
517
|
access: (()=>{
|
|
@@ -159,19 +531,16 @@ export const themeManagementPlugin = (options = {})=>{
|
|
|
159
531
|
afterChange: [
|
|
160
532
|
async ({ doc, req })=>{
|
|
161
533
|
// Invalidate cache after appearance settings change
|
|
162
|
-
if (!req?.context?.disableRevalidate) {
|
|
534
|
+
if (!req?.context?.disableRevalidate && normalizedCacheRevalidation.enabled) {
|
|
163
535
|
try {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (enableLogging) {
|
|
173
|
-
req.payload.logger.error('🎨 Theme Management Plugin: cache invalidation failed:', error);
|
|
174
|
-
}
|
|
536
|
+
await executeThemeRevalidation({
|
|
537
|
+
tags: normalizedCacheRevalidation.tags,
|
|
538
|
+
paths: normalizedCacheRevalidation.paths,
|
|
539
|
+
enableLogging,
|
|
540
|
+
logger: req?.payload?.logger
|
|
541
|
+
});
|
|
542
|
+
} catch {
|
|
543
|
+
// no-op: logging is handled in executeThemeRevalidation
|
|
175
544
|
}
|
|
176
545
|
}
|
|
177
546
|
return doc;
|
|
@@ -188,8 +557,20 @@ export const themeManagementPlugin = (options = {})=>{
|
|
|
188
557
|
}
|
|
189
558
|
return config;
|
|
190
559
|
}
|
|
560
|
+
const currentEndpoints = ensureEndpointsArray(config.endpoints);
|
|
561
|
+
const livePreviewEndpoint = createLivePreviewEndpoint({
|
|
562
|
+
livePreview: normalizedLivePreview,
|
|
563
|
+
enableLogging
|
|
564
|
+
});
|
|
565
|
+
let endpoints = mergeEndpoint(currentEndpoints, livePreviewEndpoint);
|
|
566
|
+
const cacheEndpoint = createCacheRevalidationEndpoint({
|
|
567
|
+
cacheRevalidation: normalizedCacheRevalidation,
|
|
568
|
+
enableLogging
|
|
569
|
+
});
|
|
570
|
+
endpoints = mergeEndpoint(endpoints, cacheEndpoint);
|
|
191
571
|
return {
|
|
192
572
|
...config,
|
|
573
|
+
endpoints,
|
|
193
574
|
globals: [
|
|
194
575
|
...globals,
|
|
195
576
|
standaloneGlobal
|
|
@@ -208,9 +589,14 @@ export const themeManagementPlugin = (options = {})=>{
|
|
|
208
589
|
}
|
|
209
590
|
const existingFields = Array.isArray(collection.fields) ? collection.fields : [];
|
|
210
591
|
const fields = injectThemeTab(existingFields, themeTab, enableLogging);
|
|
592
|
+
const admin = {
|
|
593
|
+
...collection.admin,
|
|
594
|
+
...createPreviewAdminConfig(normalizedLivePreview, enableLogging)
|
|
595
|
+
};
|
|
211
596
|
return {
|
|
212
597
|
...collection,
|
|
213
|
-
fields
|
|
598
|
+
fields,
|
|
599
|
+
admin
|
|
214
600
|
};
|
|
215
601
|
});
|
|
216
602
|
if (!collectionTouched) {
|
|
@@ -219,8 +605,20 @@ export const themeManagementPlugin = (options = {})=>{
|
|
|
219
605
|
}
|
|
220
606
|
return config;
|
|
221
607
|
}
|
|
608
|
+
const currentEndpoints = ensureEndpointsArray(config.endpoints);
|
|
609
|
+
const livePreviewEndpoint = createLivePreviewEndpoint({
|
|
610
|
+
livePreview: normalizedLivePreview,
|
|
611
|
+
enableLogging
|
|
612
|
+
});
|
|
613
|
+
let endpoints = mergeEndpoint(currentEndpoints, livePreviewEndpoint);
|
|
614
|
+
const cacheEndpoint = createCacheRevalidationEndpoint({
|
|
615
|
+
cacheRevalidation: normalizedCacheRevalidation,
|
|
616
|
+
enableLogging
|
|
617
|
+
});
|
|
618
|
+
endpoints = mergeEndpoint(endpoints, cacheEndpoint);
|
|
222
619
|
return {
|
|
223
620
|
...config,
|
|
621
|
+
endpoints,
|
|
224
622
|
collections
|
|
225
623
|
};
|
|
226
624
|
};
|