@kilivi/payloadcms-theme-management 0.5.7 → 0.5.9

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.
@@ -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":"ThemeTokenSelectField.d.ts","sourceRoot":"","sources":["../../src/fields/ThemeTokenSelectField.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAoFrD,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAAC,KAAK,EAAE,sBAAsB,+BA8H1E"}
1
+ {"version":3,"file":"ThemeTokenSelectField.d.ts","sourceRoot":"","sources":["../../src/fields/ThemeTokenSelectField.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAsFrD,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAAC,KAAK,EAAE,sBAAsB,+BAoI1E"}
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useField } from '@payloadcms/ui';
4
- import { useCallback, useEffect, useState } from 'react';
5
- import { defaultThemePresets } from '../presets.js';
4
+ import { useCallback, useEffect, useMemo, useState } from 'react';
5
+ import { allThemePresets } from '../index.js';
6
6
  import { resolveThemeConfiguration } from '../utils/resolveThemeConfiguration.js';
7
7
  const FALLBACK_TOKENS = [
8
8
  {
@@ -49,12 +49,12 @@ async function fetchThemeConfiguration() {
49
49
  return null;
50
50
  }
51
51
  }
52
- function buildOptionsFromConfiguration(configuration) {
52
+ function buildOptionsFromConfiguration(configuration, themePresets) {
53
53
  if (!configuration) {
54
54
  return FALLBACK_TOKENS;
55
55
  }
56
56
  const resolved = resolveThemeConfiguration(configuration);
57
- const lightMode = resolved.lightMode ?? defaultThemePresets[0]?.lightMode ?? {};
57
+ const lightMode = resolved.lightMode ?? themePresets[0]?.lightMode ?? {};
58
58
  const orderedKeys = [
59
59
  'background',
60
60
  'card',
@@ -94,11 +94,18 @@ export default function ThemeTokenSelectField(props) {
94
94
  });
95
95
  const [options, setOptions] = useState(FALLBACK_TOKENS);
96
96
  const selectedValue = value || 'background';
97
+ // Get themePresets from admin.custom or use defaults
98
+ const themePresets = useMemo(()=>{
99
+ const customPresets = field.admin?.custom?.themePresets;
100
+ return customPresets && customPresets.length > 0 ? customPresets : allThemePresets;
101
+ }, [
102
+ field.admin?.custom
103
+ ]);
97
104
  useEffect(()=>{
98
105
  let isMounted = true;
99
106
  fetchThemeConfiguration().then((configuration)=>{
100
107
  if (!isMounted) return;
101
- setOptions(buildOptionsFromConfiguration(configuration));
108
+ setOptions(buildOptionsFromConfiguration(configuration, themePresets));
102
109
  });
103
110
  return ()=>{
104
111
  isMounted = false;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/fields/ThemeTokenSelectField.tsx"],"sourcesContent":["'use client'\n\nimport { useField } from '@payloadcms/ui'\nimport type { SelectFieldClientProps } from 'payload'\nimport { useCallback, useEffect, useState } from 'react'\nimport { defaultThemePresets } from '../presets.js'\nimport { resolveThemeConfiguration } from '../utils/resolveThemeConfiguration.js'\n\ninterface ThemeColorOption {\n value: string\n label: string\n color: string\n description?: string\n}\n\nconst FALLBACK_TOKENS: ThemeColorOption[] = [\n { value: 'background', label: 'Theme background', color: 'var(--background)' },\n { value: 'card', label: 'Card background', color: 'var(--card)' },\n { value: 'muted', label: 'Muted surface', color: 'var(--muted)' },\n { value: 'accent', label: 'Accent surface', color: 'var(--accent)' },\n { value: 'secondary', label: 'Secondary surface', color: 'var(--secondary)' },\n { value: 'primary', label: 'Primary surface', color: 'var(--primary)' },\n]\n\nasync function fetchThemeConfiguration(): Promise<Record<string, unknown> | null> {\n try {\n const response = await fetch('/api/site-settings')\n if (!response.ok) {\n return null\n }\n const data = await response.json()\n return data?.docs?.[0]?.themeConfiguration ?? null\n } catch (error) {\n console.warn('[ThemeTokenSelectField] Failed to fetch theme configuration:', error)\n return null\n }\n}\n\nfunction buildOptionsFromConfiguration(\n configuration: Record<string, unknown> | null,\n): ThemeColorOption[] {\n if (!configuration) {\n return FALLBACK_TOKENS\n }\n\n const resolved = resolveThemeConfiguration(configuration)\n const lightMode = resolved.lightMode ?? defaultThemePresets[0]?.lightMode ?? {}\n const orderedKeys: string[] = [\n 'background',\n 'card',\n 'muted',\n 'accent',\n 'secondary',\n 'primary',\n 'popover',\n ]\n\n const typedLightMode = lightMode as Record<string, string | null | undefined>\n\n const tokens = orderedKeys\n .map((key) => {\n const value = typedLightMode?.[key]\n if (typeof value !== 'string' || value.trim().length === 0) {\n return null\n }\n\n const labelMap: Record<string, string> = {\n background: 'Theme background',\n card: 'Card background',\n muted: 'Muted surface',\n accent: 'Accent surface',\n secondary: 'Secondary surface',\n primary: 'Primary surface',\n popover: 'Popover surface',\n }\n\n return {\n value: key,\n label: labelMap[key] ?? key,\n color: value,\n }\n })\n .filter((token): token is ThemeColorOption => Boolean(token))\n\n return tokens.length > 0 ? tokens : FALLBACK_TOKENS\n}\n\nexport default function ThemeTokenSelectField(props: SelectFieldClientProps) {\n const { path, field } = props\n const { value, setValue } = useField<string>({ path })\n const [options, setOptions] = useState<ThemeColorOption[]>(FALLBACK_TOKENS)\n const selectedValue = value || 'background'\n\n useEffect(() => {\n let isMounted = true\n\n fetchThemeConfiguration().then((configuration) => {\n if (!isMounted) return\n setOptions(buildOptionsFromConfiguration(configuration))\n })\n\n return () => {\n isMounted = false\n }\n }, [])\n\n const handleSelect = useCallback(\n (nextValue: string) => {\n setValue(nextValue)\n },\n [setValue],\n )\n\n const label = typeof field.label === 'string' ? field.label : ''\n let description = ''\n if (typeof field.admin?.description === 'string') {\n description = field.admin.description\n } else if (\n field.admin?.description &&\n typeof field.admin.description === 'object' &&\n typeof field.admin.description.en === 'string'\n ) {\n description = field.admin.description.en\n }\n\n return (\n <div className=\"field-type theme-token-select\">\n {label && (\n <label\n className=\"field-label\"\n htmlFor={path}\n style={{ display: 'block', marginBottom: '8px' }}\n >\n {label}\n {field.required && <span className=\"required\">*</span>}\n </label>\n )}\n\n {description && (\n <div\n className=\"field-description\"\n style={{ marginBottom: '12px', fontSize: '13px', color: 'var(--theme-elevation-500)' }}\n >\n {description}\n </div>\n )}\n\n <div\n className=\"token-grid\"\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(140px, 1fr))',\n gap: '10px',\n }}\n >\n {options.map((option) => {\n const isActive = option.value === selectedValue\n return (\n <button\n type=\"button\"\n key={option.value}\n onClick={() => handleSelect(option.value)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '10px',\n padding: '10px 12px',\n borderRadius: '10px',\n border: isActive\n ? '2px solid var(--theme-success-500)'\n : '1px solid var(--theme-elevation-150)',\n backgroundColor: 'var(--theme-elevation-0)',\n boxShadow: isActive\n ? '0 10px 20px -10px rgba(59, 130, 246, 0.4)'\n : '0 1px 2px rgba(15, 23, 42, 0.08)',\n cursor: 'pointer',\n transition: 'border-color 0.2s ease, box-shadow 0.2s ease',\n }}\n >\n <span\n aria-hidden\n style={{\n width: '32px',\n height: '32px',\n borderRadius: '8px',\n border: '1px solid rgba(15, 23, 42, 0.08)',\n background: option.color,\n }}\n />\n <span\n style={{ fontSize: '13px', fontWeight: 600, color: 'var(--theme-elevation-800)' }}\n >\n {option.label}\n </span>\n </button>\n )\n })}\n </div>\n\n <select\n id={path}\n value={selectedValue}\n onChange={(event) => handleSelect(event.target.value)}\n style={{ display: 'none' }}\n >\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n )\n}\n"],"names":["useField","useCallback","useEffect","useState","defaultThemePresets","resolveThemeConfiguration","FALLBACK_TOKENS","value","label","color","fetchThemeConfiguration","response","fetch","ok","data","json","docs","themeConfiguration","error","console","warn","buildOptionsFromConfiguration","configuration","resolved","lightMode","orderedKeys","typedLightMode","tokens","map","key","trim","length","labelMap","background","card","muted","accent","secondary","primary","popover","filter","token","Boolean","ThemeTokenSelectField","props","path","field","setValue","options","setOptions","selectedValue","isMounted","then","handleSelect","nextValue","description","admin","en","div","className","htmlFor","style","display","marginBottom","required","span","fontSize","gridTemplateColumns","gap","option","isActive","button","type","onClick","alignItems","padding","borderRadius","border","backgroundColor","boxShadow","cursor","transition","aria-hidden","width","height","fontWeight","select","id","onChange","event","target"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/fields/ThemeTokenSelectField.tsx"],"sourcesContent":["'use client'\n\nimport { useField } from '@payloadcms/ui'\nimport type { SelectFieldClientProps } from 'payload'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { allThemePresets } from '../index.js'\nimport type { ThemePreset } from '../presets.js'\nimport { resolveThemeConfiguration } from '../utils/resolveThemeConfiguration.js'\n\ninterface ThemeColorOption {\n value: string\n label: string\n color: string\n description?: string\n}\n\nconst FALLBACK_TOKENS: ThemeColorOption[] = [\n { value: 'background', label: 'Theme background', color: 'var(--background)' },\n { value: 'card', label: 'Card background', color: 'var(--card)' },\n { value: 'muted', label: 'Muted surface', color: 'var(--muted)' },\n { value: 'accent', label: 'Accent surface', color: 'var(--accent)' },\n { value: 'secondary', label: 'Secondary surface', color: 'var(--secondary)' },\n { value: 'primary', label: 'Primary surface', color: 'var(--primary)' },\n]\n\nasync function fetchThemeConfiguration(): Promise<Record<string, unknown> | null> {\n try {\n const response = await fetch('/api/site-settings')\n if (!response.ok) {\n return null\n }\n const data = await response.json()\n return data?.docs?.[0]?.themeConfiguration ?? null\n } catch (error) {\n console.warn('[ThemeTokenSelectField] Failed to fetch theme configuration:', error)\n return null\n }\n}\n\nfunction buildOptionsFromConfiguration(\n configuration: Record<string, unknown> | null,\n themePresets: ThemePreset[],\n): ThemeColorOption[] {\n if (!configuration) {\n return FALLBACK_TOKENS\n }\n\n const resolved = resolveThemeConfiguration(configuration)\n const lightMode = resolved.lightMode ?? themePresets[0]?.lightMode ?? {}\n const orderedKeys: string[] = [\n 'background',\n 'card',\n 'muted',\n 'accent',\n 'secondary',\n 'primary',\n 'popover',\n ]\n\n const typedLightMode = lightMode as Record<string, string | null | undefined>\n\n const tokens = orderedKeys\n .map((key) => {\n const value = typedLightMode?.[key]\n if (typeof value !== 'string' || value.trim().length === 0) {\n return null\n }\n\n const labelMap: Record<string, string> = {\n background: 'Theme background',\n card: 'Card background',\n muted: 'Muted surface',\n accent: 'Accent surface',\n secondary: 'Secondary surface',\n primary: 'Primary surface',\n popover: 'Popover surface',\n }\n\n return {\n value: key,\n label: labelMap[key] ?? key,\n color: value,\n }\n })\n .filter((token): token is ThemeColorOption => Boolean(token))\n\n return tokens.length > 0 ? tokens : FALLBACK_TOKENS\n}\n\nexport default function ThemeTokenSelectField(props: SelectFieldClientProps) {\n const { path, field } = props\n const { value, setValue } = useField<string>({ path })\n const [options, setOptions] = useState<ThemeColorOption[]>(FALLBACK_TOKENS)\n const selectedValue = value || 'background'\n\n // Get themePresets from admin.custom or use defaults\n const themePresets = useMemo(() => {\n const customPresets = (field.admin?.custom as { themePresets?: ThemePreset[] })?.themePresets\n return customPresets && customPresets.length > 0 ? customPresets : allThemePresets\n }, [field.admin?.custom])\n\n useEffect(() => {\n let isMounted = true\n\n fetchThemeConfiguration().then((configuration) => {\n if (!isMounted) return\n setOptions(buildOptionsFromConfiguration(configuration, themePresets))\n })\n\n return () => {\n isMounted = false\n }\n }, [])\n\n const handleSelect = useCallback(\n (nextValue: string) => {\n setValue(nextValue)\n },\n [setValue],\n )\n\n const label = typeof field.label === 'string' ? field.label : ''\n let description = ''\n if (typeof field.admin?.description === 'string') {\n description = field.admin.description\n } else if (\n field.admin?.description &&\n typeof field.admin.description === 'object' &&\n typeof field.admin.description.en === 'string'\n ) {\n description = field.admin.description.en\n }\n\n return (\n <div className=\"field-type theme-token-select\">\n {label && (\n <label\n className=\"field-label\"\n htmlFor={path}\n style={{ display: 'block', marginBottom: '8px' }}\n >\n {label}\n {field.required && <span className=\"required\">*</span>}\n </label>\n )}\n\n {description && (\n <div\n className=\"field-description\"\n style={{ marginBottom: '12px', fontSize: '13px', color: 'var(--theme-elevation-500)' }}\n >\n {description}\n </div>\n )}\n\n <div\n className=\"token-grid\"\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(140px, 1fr))',\n gap: '10px',\n }}\n >\n {options.map((option) => {\n const isActive = option.value === selectedValue\n return (\n <button\n type=\"button\"\n key={option.value}\n onClick={() => handleSelect(option.value)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '10px',\n padding: '10px 12px',\n borderRadius: '10px',\n border: isActive\n ? '2px solid var(--theme-success-500)'\n : '1px solid var(--theme-elevation-150)',\n backgroundColor: 'var(--theme-elevation-0)',\n boxShadow: isActive\n ? '0 10px 20px -10px rgba(59, 130, 246, 0.4)'\n : '0 1px 2px rgba(15, 23, 42, 0.08)',\n cursor: 'pointer',\n transition: 'border-color 0.2s ease, box-shadow 0.2s ease',\n }}\n >\n <span\n aria-hidden\n style={{\n width: '32px',\n height: '32px',\n borderRadius: '8px',\n border: '1px solid rgba(15, 23, 42, 0.08)',\n background: option.color,\n }}\n />\n <span\n style={{ fontSize: '13px', fontWeight: 600, color: 'var(--theme-elevation-800)' }}\n >\n {option.label}\n </span>\n </button>\n )\n })}\n </div>\n\n <select\n id={path}\n value={selectedValue}\n onChange={(event) => handleSelect(event.target.value)}\n style={{ display: 'none' }}\n >\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n )\n}\n"],"names":["useField","useCallback","useEffect","useMemo","useState","allThemePresets","resolveThemeConfiguration","FALLBACK_TOKENS","value","label","color","fetchThemeConfiguration","response","fetch","ok","data","json","docs","themeConfiguration","error","console","warn","buildOptionsFromConfiguration","configuration","themePresets","resolved","lightMode","orderedKeys","typedLightMode","tokens","map","key","trim","length","labelMap","background","card","muted","accent","secondary","primary","popover","filter","token","Boolean","ThemeTokenSelectField","props","path","field","setValue","options","setOptions","selectedValue","customPresets","admin","custom","isMounted","then","handleSelect","nextValue","description","en","div","className","htmlFor","style","display","marginBottom","required","span","fontSize","gridTemplateColumns","gap","option","isActive","button","type","onClick","alignItems","padding","borderRadius","border","backgroundColor","boxShadow","cursor","transition","aria-hidden","width","height","fontWeight","select","id","onChange","event","target"],"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,CAoY/F"}
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;AAQD,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,CAqa/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: 'select',
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
  }
@@ -244,8 +244,9 @@ import { darkModeField, lightModeField } from './colorModeFields.js';
244
244
  cs: 'Písmo pro text'
245
245
  },
246
246
  defaultValue: 'preset',
247
- // @ts-expect-error - Pass full objects including fontFamily for custom rendering
248
- options: BODY_FONT_OPTIONS,
247
+ options: [
248
+ ...BODY_FONT_OPTIONS
249
+ ],
249
250
  admin: {
250
251
  width: '50%',
251
252
  components: {
@@ -261,8 +262,9 @@ import { darkModeField, lightModeField } from './colorModeFields.js';
261
262
  cs: 'Písmo pro nadpisy'
262
263
  },
263
264
  defaultValue: 'preset',
264
- // @ts-expect-error - Pass full objects including fontFamily for custom rendering
265
- options: HEADING_FONT_OPTIONS,
265
+ options: [
266
+ ...HEADING_FONT_OPTIONS
267
+ ],
266
268
  admin: {
267
269
  width: '50%',
268
270
  components: {
@@ -352,6 +354,40 @@ import { darkModeField, lightModeField } from './colorModeFields.js';
352
354
  }
353
355
  ]
354
356
  });
357
+ const customPresetsField = {
358
+ type: 'collapsible',
359
+ label: {
360
+ en: '🧩 Custom Theme Presets',
361
+ cs: '🧩 Vlastní motivy'
362
+ },
363
+ admin: {
364
+ initCollapsed: true,
365
+ description: {
366
+ en: 'Import additional theme presets via JSON to extend or override the defaults. These presets become available instantly in the Theme Selection above.',
367
+ 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.'
368
+ }
369
+ },
370
+ fields: [
371
+ {
372
+ name: 'customThemePresets',
373
+ type: 'json',
374
+ label: {
375
+ en: 'Custom Preset JSON',
376
+ cs: 'JSON s vlastními motivy'
377
+ },
378
+ admin: {
379
+ description: {
380
+ 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.',
381
+ 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.'
382
+ },
383
+ components: {
384
+ Field: '@kilivi/payloadcms-theme-management/fields/ThemePresetImportField'
385
+ }
386
+ }
387
+ }
388
+ ]
389
+ };
390
+ fields.push(customPresetsField);
355
391
  // Advanced Settings (when using extended theme configuration)
356
392
  if (enableAdvancedFeatures) {
357
393
  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 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 },\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 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 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 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 },\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","customPresetsField","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"}