@kilivi/payloadcms-theme-management 2.0.0 → 2.2.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.
Files changed (235) hide show
  1. package/README.md +346 -845
  2. package/dist/components/AppearanceSessionControls.d.ts +15 -0
  3. package/dist/components/AppearanceSessionControls.d.ts.map +1 -0
  4. package/dist/components/AppearanceSessionControls.js +152 -0
  5. package/dist/components/AppearanceSessionControls.js.map +1 -0
  6. package/dist/components/ColorModeToggle.css +42 -0
  7. package/dist/components/ColorModeToggle.d.ts +40 -0
  8. package/dist/components/ColorModeToggle.d.ts.map +1 -0
  9. package/dist/components/ColorModeToggle.js +111 -0
  10. package/dist/components/ColorModeToggle.js.map +1 -0
  11. package/dist/components/ServerThemeInjector.d.ts +13 -1
  12. package/dist/components/ServerThemeInjector.d.ts.map +1 -1
  13. package/dist/components/ServerThemeInjector.js +65 -15
  14. package/dist/components/ServerThemeInjector.js.map +1 -1
  15. package/dist/components/ThemeLivePreviewSync.d.ts +4 -0
  16. package/dist/components/ThemeLivePreviewSync.d.ts.map +1 -1
  17. package/dist/components/ThemeLivePreviewSync.js +28 -4
  18. package/dist/components/ThemeLivePreviewSync.js.map +1 -1
  19. package/dist/components/ThemeSwitcher.d.ts +36 -0
  20. package/dist/components/ThemeSwitcher.d.ts.map +1 -0
  21. package/dist/components/ThemeSwitcher.js +89 -0
  22. package/dist/components/ThemeSwitcher.js.map +1 -0
  23. package/dist/components/typographyPreviewUtils.d.ts +5 -0
  24. package/dist/components/typographyPreviewUtils.d.ts.map +1 -1
  25. package/dist/components/typographyPreviewUtils.js +24 -11
  26. package/dist/components/typographyPreviewUtils.js.map +1 -1
  27. package/dist/constants/devicePreviews.d.ts +35 -0
  28. package/dist/constants/devicePreviews.d.ts.map +1 -0
  29. package/dist/constants/devicePreviews.js +43 -0
  30. package/dist/constants/devicePreviews.js.map +1 -0
  31. package/dist/constants/themeFonts.d.ts +94 -168
  32. package/dist/constants/themeFonts.d.ts.map +1 -1
  33. package/dist/constants/themeFonts.js +260 -140
  34. package/dist/constants/themeFonts.js.map +1 -1
  35. package/dist/endpoints/themeFonts.d.ts +5 -0
  36. package/dist/endpoints/themeFonts.d.ts.map +1 -0
  37. package/dist/endpoints/themeFonts.js +138 -0
  38. package/dist/endpoints/themeFonts.js.map +1 -0
  39. package/dist/extended-presets.d.ts +0 -4
  40. package/dist/extended-presets.d.ts.map +1 -1
  41. package/dist/extended-presets.js +1 -18
  42. package/dist/extended-presets.js.map +1 -1
  43. package/dist/extended-themes.d.ts.map +1 -1
  44. package/dist/extended-themes.js +428 -0
  45. package/dist/extended-themes.js.map +1 -1
  46. package/dist/fields/AccessibilityAuditField.d.ts +2 -0
  47. package/dist/fields/AccessibilityAuditField.d.ts.map +1 -0
  48. package/dist/fields/AccessibilityAuditField.js +303 -0
  49. package/dist/fields/AccessibilityAuditField.js.map +1 -0
  50. package/dist/fields/AppearancePreviewField.d.ts +2 -0
  51. package/dist/fields/AppearancePreviewField.d.ts.map +1 -0
  52. package/dist/fields/AppearancePreviewField.js +826 -0
  53. package/dist/fields/AppearancePreviewField.js.map +1 -0
  54. package/dist/fields/ColorPickerField.d.ts.map +1 -1
  55. package/dist/fields/ColorPickerField.js +89 -85
  56. package/dist/fields/ColorPickerField.js.map +1 -1
  57. package/dist/fields/FontSelectField.css +155 -0
  58. package/dist/fields/FontSelectField.d.ts +8 -0
  59. package/dist/fields/FontSelectField.d.ts.map +1 -1
  60. package/dist/fields/FontSelectField.js +223 -188
  61. package/dist/fields/FontSelectField.js.map +1 -1
  62. package/dist/fields/PaletteGeneratorField.d.ts +7 -0
  63. package/dist/fields/PaletteGeneratorField.d.ts.map +1 -0
  64. package/dist/fields/PaletteGeneratorField.js +327 -0
  65. package/dist/fields/PaletteGeneratorField.js.map +1 -0
  66. package/dist/fields/StylePresetField.d.ts +3 -0
  67. package/dist/fields/StylePresetField.d.ts.map +1 -0
  68. package/dist/fields/StylePresetField.js +463 -0
  69. package/dist/fields/StylePresetField.js.map +1 -0
  70. package/dist/fields/ThemeColorPickerField.css +115 -35
  71. package/dist/fields/ThemeColorPickerField.d.ts.map +1 -1
  72. package/dist/fields/ThemeColorPickerField.js +204 -70
  73. package/dist/fields/ThemeColorPickerField.js.map +1 -1
  74. package/dist/fields/ThemeExportField.d.ts +6 -0
  75. package/dist/fields/ThemeExportField.d.ts.map +1 -0
  76. package/dist/fields/ThemeExportField.js +202 -0
  77. package/dist/fields/ThemeExportField.js.map +1 -0
  78. package/dist/fields/ThemePresetImportField.d.ts.map +1 -1
  79. package/dist/fields/ThemePresetImportField.js +56 -25
  80. package/dist/fields/ThemePresetImportField.js.map +1 -1
  81. package/dist/fields/ThemePreviewField.css +444 -5
  82. package/dist/fields/ThemePreviewField.d.ts.map +1 -1
  83. package/dist/fields/ThemePreviewField.js +265 -170
  84. package/dist/fields/ThemePreviewField.js.map +1 -1
  85. package/dist/fields/ThemeTokenSelectField.d.ts.map +1 -1
  86. package/dist/fields/ThemeTokenSelectField.js +278 -80
  87. package/dist/fields/ThemeTokenSelectField.js.map +1 -1
  88. package/dist/fields/colorFieldHelpers.d.ts +90 -0
  89. package/dist/fields/colorFieldHelpers.d.ts.map +1 -0
  90. package/dist/fields/colorFieldHelpers.js +133 -0
  91. package/dist/fields/colorFieldHelpers.js.map +1 -0
  92. package/dist/fields/colorModeFields.d.ts +1 -1
  93. package/dist/fields/colorModeFields.d.ts.map +1 -1
  94. package/dist/fields/colorModeFields.js +49 -7
  95. package/dist/fields/colorModeFields.js.map +1 -1
  96. package/dist/fields/themeConfigurationField.d.ts +8 -1
  97. package/dist/fields/themeConfigurationField.d.ts.map +1 -1
  98. package/dist/fields/themeConfigurationField.js +1209 -228
  99. package/dist/fields/themeConfigurationField.js.map +1 -1
  100. package/dist/globals/ThemeSettings/FontPicker/FontPreview.d.ts.map +1 -1
  101. package/dist/globals/ThemeSettings/FontPicker/FontPreview.js +8 -4
  102. package/dist/globals/ThemeSettings/FontPicker/FontPreview.js.map +1 -1
  103. package/dist/globals/ThemeSettings/FontPicker/index.d.ts.map +1 -1
  104. package/dist/globals/ThemeSettings/FontPicker/index.js +81 -26
  105. package/dist/globals/ThemeSettings/FontPicker/index.js.map +1 -1
  106. package/dist/globals/ThemeSettings/FontPicker/styles.css +98 -41
  107. package/dist/globals/ThemeSettings/FontPicker/translations.d.ts +6 -0
  108. package/dist/globals/ThemeSettings/FontPicker/translations.d.ts.map +1 -1
  109. package/dist/globals/ThemeSettings/FontPicker/translations.js +14 -2
  110. package/dist/globals/ThemeSettings/FontPicker/translations.js.map +1 -1
  111. package/dist/globals/ThemeSettings/config.d.ts.map +1 -1
  112. package/dist/globals/ThemeSettings/config.js +76 -79
  113. package/dist/globals/ThemeSettings/config.js.map +1 -1
  114. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.d.ts.map +1 -1
  115. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.js +63 -62
  116. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.js.map +1 -1
  117. package/dist/globals/ThemeSettings/fields/TypographyInfoField.d.ts.map +1 -1
  118. package/dist/globals/ThemeSettings/fields/TypographyInfoField.js +22 -25
  119. package/dist/globals/ThemeSettings/fields/TypographyInfoField.js.map +1 -1
  120. package/dist/hooks/useAppearanceSession.d.ts +50 -0
  121. package/dist/hooks/useAppearanceSession.d.ts.map +1 -0
  122. package/dist/hooks/useAppearanceSession.js +91 -0
  123. package/dist/hooks/useAppearanceSession.js.map +1 -0
  124. package/dist/hooks/useNativeLivePreview.d.ts +15 -0
  125. package/dist/hooks/useNativeLivePreview.d.ts.map +1 -0
  126. package/dist/hooks/useNativeLivePreview.js +18 -0
  127. package/dist/hooks/useNativeLivePreview.js.map +1 -0
  128. package/dist/hooks/useThemeFonts.d.ts +10 -0
  129. package/dist/hooks/useThemeFonts.d.ts.map +1 -0
  130. package/dist/hooks/useThemeFonts.js +36 -0
  131. package/dist/hooks/useThemeFonts.js.map +1 -0
  132. package/dist/hooks/useThemeTranslations.d.ts +13 -0
  133. package/dist/hooks/useThemeTranslations.d.ts.map +1 -0
  134. package/dist/hooks/useThemeTranslations.js +22 -0
  135. package/dist/hooks/useThemeTranslations.js.map +1 -0
  136. package/dist/i18n.d.ts +53 -0
  137. package/dist/i18n.d.ts.map +1 -0
  138. package/dist/i18n.js +53 -0
  139. package/dist/i18n.js.map +1 -0
  140. package/dist/index.d.ts +13 -4
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +59 -29
  143. package/dist/index.js.map +1 -1
  144. package/dist/next-fonts.d.ts +3 -0
  145. package/dist/next-fonts.d.ts.map +1 -0
  146. package/dist/next-fonts.js +360 -0
  147. package/dist/next-fonts.js.map +1 -0
  148. package/dist/payload-types.d.ts +34 -6
  149. package/dist/payload-types.d.ts.map +1 -1
  150. package/dist/payload-types.js.map +1 -1
  151. package/dist/presets.d.ts +14 -0
  152. package/dist/presets.d.ts.map +1 -1
  153. package/dist/presets.js.map +1 -1
  154. package/dist/providers/Theme/index.d.ts.map +1 -1
  155. package/dist/providers/Theme/index.js +75 -11
  156. package/dist/providers/Theme/index.js.map +1 -1
  157. package/dist/providers/Theme/themeConfig.d.ts.map +1 -1
  158. package/dist/providers/Theme/themeConfig.js +104 -0
  159. package/dist/providers/Theme/themeConfig.js.map +1 -1
  160. package/dist/providers/Theme/types.d.ts +1 -1
  161. package/dist/providers/Theme/types.d.ts.map +1 -1
  162. package/dist/providers/Theme/types.js +10 -1
  163. package/dist/providers/Theme/types.js.map +1 -1
  164. package/dist/style-presets.d.ts +75 -0
  165. package/dist/style-presets.d.ts.map +1 -0
  166. package/dist/style-presets.js +738 -0
  167. package/dist/style-presets.js.map +1 -0
  168. package/dist/translations.d.ts +164 -1
  169. package/dist/translations.d.ts.map +1 -1
  170. package/dist/translations.js +320 -9
  171. package/dist/translations.js.map +1 -1
  172. package/dist/tweakcn-special-themes.d.ts.map +1 -1
  173. package/dist/tweakcn-special-themes.js +585 -9
  174. package/dist/tweakcn-special-themes.js.map +1 -1
  175. package/dist/types/css.d.js +3 -0
  176. package/dist/types/css.d.js.map +1 -0
  177. package/dist/types.d.ts +26 -0
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/types.js +1 -1
  180. package/dist/types.js.map +1 -1
  181. package/dist/utilities/getThemeSettings.d.ts.map +1 -1
  182. package/dist/utilities/getThemeSettings.js +2 -14
  183. package/dist/utilities/getThemeSettings.js.map +1 -1
  184. package/dist/utils/contrast.d.ts +63 -0
  185. package/dist/utils/contrast.d.ts.map +1 -0
  186. package/dist/utils/contrast.js +158 -0
  187. package/dist/utils/contrast.js.map +1 -0
  188. package/dist/utils/exportTokens.d.ts +30 -0
  189. package/dist/utils/exportTokens.d.ts.map +1 -0
  190. package/dist/utils/exportTokens.js +183 -0
  191. package/dist/utils/exportTokens.js.map +1 -0
  192. package/dist/utils/generatePalette.d.ts +46 -0
  193. package/dist/utils/generatePalette.d.ts.map +1 -0
  194. package/dist/utils/generatePalette.js +127 -0
  195. package/dist/utils/generatePalette.js.map +1 -0
  196. package/dist/utils/generateThemeCSS.d.ts.map +1 -1
  197. package/dist/utils/generateThemeCSS.js +2 -40
  198. package/dist/utils/generateThemeCSS.js.map +1 -1
  199. package/dist/utils/livePreviewThemeRuntime.d.ts +22 -3
  200. package/dist/utils/livePreviewThemeRuntime.d.ts.map +1 -1
  201. package/dist/utils/livePreviewThemeRuntime.js +48 -15
  202. package/dist/utils/livePreviewThemeRuntime.js.map +1 -1
  203. package/dist/utils/localizedText.d.ts +8 -0
  204. package/dist/utils/localizedText.d.ts.map +1 -0
  205. package/dist/utils/localizedText.js +21 -0
  206. package/dist/utils/localizedText.js.map +1 -0
  207. package/dist/utils/resolveThemeConfiguration.d.ts +60 -1
  208. package/dist/utils/resolveThemeConfiguration.d.ts.map +1 -1
  209. package/dist/utils/resolveThemeConfiguration.js +53 -13
  210. package/dist/utils/resolveThemeConfiguration.js.map +1 -1
  211. package/dist/utils/themeColors.d.ts +3 -0
  212. package/dist/utils/themeColors.d.ts.map +1 -1
  213. package/dist/utils/themeColors.js +30 -1
  214. package/dist/utils/themeColors.js.map +1 -1
  215. package/dist/utils/themeHtmlAttributes.d.ts +32 -12
  216. package/dist/utils/themeHtmlAttributes.d.ts.map +1 -1
  217. package/dist/utils/themeHtmlAttributes.js +38 -5
  218. package/dist/utils/themeHtmlAttributes.js.map +1 -1
  219. package/dist/utils/themeUtils.d.ts +3 -5
  220. package/dist/utils/themeUtils.d.ts.map +1 -1
  221. package/dist/utils/themeUtils.js +474 -6
  222. package/dist/utils/themeUtils.js.map +1 -1
  223. package/dist/utils/tweakcnConverter.d.ts +1 -3
  224. package/dist/utils/tweakcnConverter.d.ts.map +1 -1
  225. package/dist/utils/tweakcnConverter.js +1 -3
  226. package/dist/utils/tweakcnConverter.js.map +1 -1
  227. package/package.json +15 -5
  228. package/dist/components/ThemePreview.d.ts +0 -3
  229. package/dist/components/ThemePreview.d.ts.map +0 -1
  230. package/dist/components/ThemePreview.js +0 -321
  231. package/dist/components/ThemePreview.js.map +0 -1
  232. package/dist/plugin.d.ts +0 -2
  233. package/dist/plugin.d.ts.map +0 -1
  234. package/dist/plugin.js +0 -3
  235. package/dist/plugin.js.map +0 -1
@@ -1,14 +1,15 @@
1
1
  'use client';
2
2
  import { useLivePreview } from '@payloadcms/live-preview-react';
3
- import { useEffect, useMemo } from 'react';
3
+ import React, { useEffect, useMemo, useRef } from 'react';
4
4
  import { buildLivePreviewThemeRuntime, extractThemeConfigurationFromLiveData } from '../utils/livePreviewThemeRuntime.js';
5
- export const ThemeLivePreviewSync = ({ initialData, serverURL, apiRoute, depth, themeConfigurationKey = 'themeConfiguration', styleElementId = 'theme-live-preview-css' })=>{
5
+ export const ThemeLivePreviewSync = ({ initialData, serverURL, apiRoute, depth, themeConfigurationKey = 'themeConfiguration', styleElementId = 'theme-live-preview-css', fontLoading = 'self-hosted' })=>{
6
6
  const { data } = useLivePreview({
7
7
  apiRoute,
8
8
  depth,
9
9
  initialData,
10
10
  serverURL
11
11
  });
12
+ const appliedAttributes = useRef([]);
12
13
  const themeConfiguration = useMemo(()=>extractThemeConfigurationFromLiveData(data, themeConfigurationKey), [
13
14
  data,
14
15
  themeConfigurationKey
@@ -17,7 +18,7 @@ export const ThemeLivePreviewSync = ({ initialData, serverURL, apiRoute, depth,
17
18
  if (typeof document === 'undefined' || !themeConfiguration) {
18
19
  return;
19
20
  }
20
- const runtime = buildLivePreviewThemeRuntime(themeConfiguration);
21
+ const runtime = buildLivePreviewThemeRuntime(themeConfiguration, apiRoute, fontLoading);
21
22
  let styleElement = document.getElementById(styleElementId);
22
23
  if (!styleElement) {
23
24
  styleElement = document.createElement('style');
@@ -25,6 +26,27 @@ export const ThemeLivePreviewSync = ({ initialData, serverURL, apiRoute, depth,
25
26
  document.head.appendChild(styleElement);
26
27
  }
27
28
  styleElement.innerHTML = runtime.css;
29
+ const fontsLinkId = `${styleElementId}-fonts`;
30
+ let fontsLink = document.getElementById(fontsLinkId);
31
+ const fontsAlreadyLoaded = runtime.fontsHref !== null && [
32
+ ...document.querySelectorAll('link[rel="stylesheet"]')
33
+ ].some((link)=>link !== fontsLink && link.getAttribute('href') === runtime.fontsHref);
34
+ if (runtime.fontsHref && !fontsAlreadyLoaded) {
35
+ if (!fontsLink) {
36
+ fontsLink = document.createElement('link');
37
+ fontsLink.id = fontsLinkId;
38
+ fontsLink.rel = 'stylesheet';
39
+ document.head.appendChild(fontsLink);
40
+ }
41
+ if (fontsLink.getAttribute('href') !== runtime.fontsHref) fontsLink.href = runtime.fontsHref;
42
+ } else {
43
+ fontsLink?.remove();
44
+ }
45
+ const root = document.documentElement;
46
+ // Drop style attributes that are no longer configured, then apply the current set.
47
+ appliedAttributes.current.filter((name)=>!(name in runtime.attributes)).forEach((name)=>root.removeAttribute(name));
48
+ Object.entries(runtime.attributes).forEach(([name, value])=>root.setAttribute(name, value));
49
+ appliedAttributes.current = Object.keys(runtime.attributes);
28
50
  document.documentElement.dataset.theme = runtime.theme;
29
51
  document.documentElement.dataset.colorMode = runtime.mode;
30
52
  document.documentElement.dataset.themeMode = runtime.mode;
@@ -32,7 +54,9 @@ export const ThemeLivePreviewSync = ({ initialData, serverURL, apiRoute, depth,
32
54
  document.documentElement.classList.add(runtime.mode);
33
55
  }, [
34
56
  themeConfiguration,
35
- styleElementId
57
+ styleElementId,
58
+ apiRoute,
59
+ fontLoading
36
60
  ]);
37
61
  return null;
38
62
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/ThemeLivePreviewSync.tsx"],"sourcesContent":["'use client'\n\nimport { useLivePreview } from '@payloadcms/live-preview-react'\nimport { useEffect, useMemo } from 'react'\nimport {\n buildLivePreviewThemeRuntime,\n extractThemeConfigurationFromLiveData,\n} from '../utils/livePreviewThemeRuntime.js'\n\ninterface ThemeLivePreviewSyncProps {\n initialData: Record<string, unknown>\n serverURL: string\n apiRoute?: string\n depth?: number\n themeConfigurationKey?: string\n styleElementId?: string\n}\n\nexport const ThemeLivePreviewSync: React.FC<ThemeLivePreviewSyncProps> = ({\n initialData,\n serverURL,\n apiRoute,\n depth,\n themeConfigurationKey = 'themeConfiguration',\n styleElementId = 'theme-live-preview-css',\n}) => {\n const { data } = useLivePreview<Record<string, unknown>>({\n apiRoute,\n depth,\n initialData,\n serverURL,\n })\n\n const themeConfiguration = useMemo(\n () => extractThemeConfigurationFromLiveData(data, themeConfigurationKey),\n [data, themeConfigurationKey],\n )\n\n useEffect(() => {\n if (typeof document === 'undefined' || !themeConfiguration) {\n return\n }\n\n const runtime = buildLivePreviewThemeRuntime(themeConfiguration)\n\n let styleElement = document.getElementById(styleElementId) as HTMLStyleElement | null\n if (!styleElement) {\n styleElement = document.createElement('style')\n styleElement.id = styleElementId\n document.head.appendChild(styleElement)\n }\n\n styleElement.innerHTML = runtime.css\n\n document.documentElement.dataset.theme = runtime.theme\n document.documentElement.dataset.colorMode = runtime.mode\n document.documentElement.dataset.themeMode = runtime.mode\n document.documentElement.classList.remove('light', 'dark', 'auto')\n document.documentElement.classList.add(runtime.mode)\n }, [themeConfiguration, styleElementId])\n\n return null\n}\n"],"names":["useLivePreview","useEffect","useMemo","buildLivePreviewThemeRuntime","extractThemeConfigurationFromLiveData","ThemeLivePreviewSync","initialData","serverURL","apiRoute","depth","themeConfigurationKey","styleElementId","data","themeConfiguration","document","runtime","styleElement","getElementById","createElement","id","head","appendChild","innerHTML","css","documentElement","dataset","theme","colorMode","mode","themeMode","classList","remove","add"],"mappings":"AAAA;AAEA,SAASA,cAAc,QAAQ,iCAAgC;AAC/D,SAASC,SAAS,EAAEC,OAAO,QAAQ,QAAO;AAC1C,SACEC,4BAA4B,EAC5BC,qCAAqC,QAChC,sCAAqC;AAW5C,OAAO,MAAMC,uBAA4D,CAAC,EACxEC,WAAW,EACXC,SAAS,EACTC,QAAQ,EACRC,KAAK,EACLC,wBAAwB,oBAAoB,EAC5CC,iBAAiB,wBAAwB,EAC1C;IACC,MAAM,EAAEC,IAAI,EAAE,GAAGZ,eAAwC;QACvDQ;QACAC;QACAH;QACAC;IACF;IAEA,MAAMM,qBAAqBX,QACzB,IAAME,sCAAsCQ,MAAMF,wBAClD;QAACE;QAAMF;KAAsB;IAG/BT,UAAU;QACR,IAAI,OAAOa,aAAa,eAAe,CAACD,oBAAoB;YAC1D;QACF;QAEA,MAAME,UAAUZ,6BAA6BU;QAE7C,IAAIG,eAAeF,SAASG,cAAc,CAACN;QAC3C,IAAI,CAACK,cAAc;YACjBA,eAAeF,SAASI,aAAa,CAAC;YACtCF,aAAaG,EAAE,GAAGR;YAClBG,SAASM,IAAI,CAACC,WAAW,CAACL;QAC5B;QAEAA,aAAaM,SAAS,GAAGP,QAAQQ,GAAG;QAEpCT,SAASU,eAAe,CAACC,OAAO,CAACC,KAAK,GAAGX,QAAQW,KAAK;QACtDZ,SAASU,eAAe,CAACC,OAAO,CAACE,SAAS,GAAGZ,QAAQa,IAAI;QACzDd,SAASU,eAAe,CAACC,OAAO,CAACI,SAAS,GAAGd,QAAQa,IAAI;QACzDd,SAASU,eAAe,CAACM,SAAS,CAACC,MAAM,CAAC,SAAS,QAAQ;QAC3DjB,SAASU,eAAe,CAACM,SAAS,CAACE,GAAG,CAACjB,QAAQa,IAAI;IACrD,GAAG;QAACf;QAAoBF;KAAe;IAEvC,OAAO;AACT,EAAC"}
1
+ {"version":3,"sources":["../../src/components/ThemeLivePreviewSync.tsx"],"sourcesContent":["'use client'\n\nimport { useLivePreview } from '@payloadcms/live-preview-react'\nimport React, { useEffect, useMemo, useRef } from 'react'\nimport type { ThemeFontLoading } from '../constants/themeFonts.js'\nimport {\n buildLivePreviewThemeRuntime,\n extractThemeConfigurationFromLiveData,\n} from '../utils/livePreviewThemeRuntime.js'\n\ninterface ThemeLivePreviewSyncProps {\n initialData: Record<string, unknown>\n serverURL: string\n apiRoute?: string\n depth?: number\n themeConfigurationKey?: string\n styleElementId?: string\n /** Same as ServerThemeInjector's `fontLoading`; use 'none' with next/font. */\n fontLoading?: ThemeFontLoading\n}\n\nexport const ThemeLivePreviewSync: React.FC<ThemeLivePreviewSyncProps> = ({\n initialData,\n serverURL,\n apiRoute,\n depth,\n themeConfigurationKey = 'themeConfiguration',\n styleElementId = 'theme-live-preview-css',\n fontLoading = 'self-hosted',\n}) => {\n const { data } = useLivePreview<Record<string, unknown>>({\n apiRoute,\n depth,\n initialData,\n serverURL,\n })\n\n const appliedAttributes = useRef<string[]>([])\n\n const themeConfiguration = useMemo(\n () => extractThemeConfigurationFromLiveData(data, themeConfigurationKey),\n [data, themeConfigurationKey],\n )\n\n useEffect(() => {\n if (typeof document === 'undefined' || !themeConfiguration) {\n return\n }\n\n const runtime = buildLivePreviewThemeRuntime(themeConfiguration, apiRoute, fontLoading)\n\n let styleElement = document.getElementById(styleElementId) as HTMLStyleElement | null\n if (!styleElement) {\n styleElement = document.createElement('style')\n styleElement.id = styleElementId\n document.head.appendChild(styleElement)\n }\n\n styleElement.innerHTML = runtime.css\n\n const fontsLinkId = `${styleElementId}-fonts`\n let fontsLink = document.getElementById(fontsLinkId) as HTMLLinkElement | null\n const fontsAlreadyLoaded =\n runtime.fontsHref !== null &&\n [...document.querySelectorAll<HTMLLinkElement>('link[rel=\"stylesheet\"]')].some(\n (link) => link !== fontsLink && link.getAttribute('href') === runtime.fontsHref,\n )\n if (runtime.fontsHref && !fontsAlreadyLoaded) {\n if (!fontsLink) {\n fontsLink = document.createElement('link')\n fontsLink.id = fontsLinkId\n fontsLink.rel = 'stylesheet'\n document.head.appendChild(fontsLink)\n }\n if (fontsLink.getAttribute('href') !== runtime.fontsHref) fontsLink.href = runtime.fontsHref\n } else {\n fontsLink?.remove()\n }\n\n const root = document.documentElement\n // Drop style attributes that are no longer configured, then apply the current set.\n appliedAttributes.current\n .filter((name) => !(name in runtime.attributes))\n .forEach((name) => root.removeAttribute(name))\n Object.entries(runtime.attributes).forEach(([name, value]) => root.setAttribute(name, value))\n appliedAttributes.current = Object.keys(runtime.attributes)\n\n document.documentElement.dataset.theme = runtime.theme\n document.documentElement.dataset.colorMode = runtime.mode\n document.documentElement.dataset.themeMode = runtime.mode\n document.documentElement.classList.remove('light', 'dark', 'auto')\n document.documentElement.classList.add(runtime.mode)\n }, [themeConfiguration, styleElementId, apiRoute, fontLoading])\n\n return null\n}\n"],"names":["useLivePreview","React","useEffect","useMemo","useRef","buildLivePreviewThemeRuntime","extractThemeConfigurationFromLiveData","ThemeLivePreviewSync","initialData","serverURL","apiRoute","depth","themeConfigurationKey","styleElementId","fontLoading","data","appliedAttributes","themeConfiguration","document","runtime","styleElement","getElementById","createElement","id","head","appendChild","innerHTML","css","fontsLinkId","fontsLink","fontsAlreadyLoaded","fontsHref","querySelectorAll","some","link","getAttribute","rel","href","remove","root","documentElement","current","filter","name","attributes","forEach","removeAttribute","Object","entries","value","setAttribute","keys","dataset","theme","colorMode","mode","themeMode","classList","add"],"mappings":"AAAA;AAEA,SAASA,cAAc,QAAQ,iCAAgC;AAC/D,OAAOC,SAASC,SAAS,EAAEC,OAAO,EAAEC,MAAM,QAAQ,QAAO;AAEzD,SACEC,4BAA4B,EAC5BC,qCAAqC,QAChC,sCAAqC;AAa5C,OAAO,MAAMC,uBAA4D,CAAC,EACxEC,WAAW,EACXC,SAAS,EACTC,QAAQ,EACRC,KAAK,EACLC,wBAAwB,oBAAoB,EAC5CC,iBAAiB,wBAAwB,EACzCC,cAAc,aAAa,EAC5B;IACC,MAAM,EAAEC,IAAI,EAAE,GAAGf,eAAwC;QACvDU;QACAC;QACAH;QACAC;IACF;IAEA,MAAMO,oBAAoBZ,OAAiB,EAAE;IAE7C,MAAMa,qBAAqBd,QACzB,IAAMG,sCAAsCS,MAAMH,wBAClD;QAACG;QAAMH;KAAsB;IAG/BV,UAAU;QACR,IAAI,OAAOgB,aAAa,eAAe,CAACD,oBAAoB;YAC1D;QACF;QAEA,MAAME,UAAUd,6BAA6BY,oBAAoBP,UAAUI;QAE3E,IAAIM,eAAeF,SAASG,cAAc,CAACR;QAC3C,IAAI,CAACO,cAAc;YACjBA,eAAeF,SAASI,aAAa,CAAC;YACtCF,aAAaG,EAAE,GAAGV;YAClBK,SAASM,IAAI,CAACC,WAAW,CAACL;QAC5B;QAEAA,aAAaM,SAAS,GAAGP,QAAQQ,GAAG;QAEpC,MAAMC,cAAc,GAAGf,eAAe,MAAM,CAAC;QAC7C,IAAIgB,YAAYX,SAASG,cAAc,CAACO;QACxC,MAAME,qBACJX,QAAQY,SAAS,KAAK,QACtB;eAAIb,SAASc,gBAAgB,CAAkB;SAA0B,CAACC,IAAI,CAC5E,CAACC,OAASA,SAASL,aAAaK,KAAKC,YAAY,CAAC,YAAYhB,QAAQY,SAAS;QAEnF,IAAIZ,QAAQY,SAAS,IAAI,CAACD,oBAAoB;YAC5C,IAAI,CAACD,WAAW;gBACdA,YAAYX,SAASI,aAAa,CAAC;gBACnCO,UAAUN,EAAE,GAAGK;gBACfC,UAAUO,GAAG,GAAG;gBAChBlB,SAASM,IAAI,CAACC,WAAW,CAACI;YAC5B;YACA,IAAIA,UAAUM,YAAY,CAAC,YAAYhB,QAAQY,SAAS,EAAEF,UAAUQ,IAAI,GAAGlB,QAAQY,SAAS;QAC9F,OAAO;YACLF,WAAWS;QACb;QAEA,MAAMC,OAAOrB,SAASsB,eAAe;QACrC,mFAAmF;QACnFxB,kBAAkByB,OAAO,CACtBC,MAAM,CAAC,CAACC,OAAS,CAAEA,CAAAA,QAAQxB,QAAQyB,UAAU,AAAD,GAC5CC,OAAO,CAAC,CAACF,OAASJ,KAAKO,eAAe,CAACH;QAC1CI,OAAOC,OAAO,CAAC7B,QAAQyB,UAAU,EAAEC,OAAO,CAAC,CAAC,CAACF,MAAMM,MAAM,GAAKV,KAAKW,YAAY,CAACP,MAAMM;QACtFjC,kBAAkByB,OAAO,GAAGM,OAAOI,IAAI,CAAChC,QAAQyB,UAAU;QAE1D1B,SAASsB,eAAe,CAACY,OAAO,CAACC,KAAK,GAAGlC,QAAQkC,KAAK;QACtDnC,SAASsB,eAAe,CAACY,OAAO,CAACE,SAAS,GAAGnC,QAAQoC,IAAI;QACzDrC,SAASsB,eAAe,CAACY,OAAO,CAACI,SAAS,GAAGrC,QAAQoC,IAAI;QACzDrC,SAASsB,eAAe,CAACiB,SAAS,CAACnB,MAAM,CAAC,SAAS,QAAQ;QAC3DpB,SAASsB,eAAe,CAACiB,SAAS,CAACC,GAAG,CAACvC,QAAQoC,IAAI;IACrD,GAAG;QAACtC;QAAoBJ;QAAgBH;QAAUI;KAAY;IAE9D,OAAO;AACT,EAAC"}
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import { type ThemePreset } from '../presets.js';
3
+ export type SwitchablePreset = Pick<ThemePreset, 'name' | 'label' | 'lightMode' | 'darkMode'>;
4
+ export interface ThemeSwitcherProps {
5
+ /**
6
+ * Presets the visitor can switch between. Defaults to all bundled presets.
7
+ * Pass a curated subset for a focused switcher.
8
+ */
9
+ presets?: SwitchablePreset[];
10
+ className?: string;
11
+ style?: React.CSSProperties;
12
+ 'aria-label'?: string;
13
+ /** Persist the choice to localStorage and restore it on load. Default true. */
14
+ persist?: boolean;
15
+ /** Called after a preset is applied. */
16
+ onChange?: (presetName: string) => void;
17
+ }
18
+ /**
19
+ * Front-end theme-preset switcher. Lets visitors swap between bundled (or a
20
+ * curated subset of) theme presets at runtime by injecting the preset's colour
21
+ * CSS and updating `data-theme`. The choice is persisted to localStorage and
22
+ * restored on the next visit.
23
+ *
24
+ * Note: colour-mode (light/dark) is handled by {@link ColorModeToggle} and the
25
+ * SSR injector with no flash; preset switching re-applies on mount, so a brief
26
+ * flash to the SSR default can occur before the stored preset is restored.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * import { ThemeSwitcher } from '@kilivi/payloadcms-theme-management/components/ThemeSwitcher'
31
+ * <ThemeSwitcher presets={[oceanPreset, forestPreset]} />
32
+ * ```
33
+ */
34
+ export declare const ThemeSwitcher: React.FC<ThemeSwitcherProps>;
35
+ export default ThemeSwitcher;
36
+ //# sourceMappingURL=ThemeSwitcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeSwitcher.d.ts","sourceRoot":"","sources":["../../src/components/ThemeSwitcher.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAC/D,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAA;AAMjE,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,UAAU,CAAC,CAAA;AAE7F,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAuBD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA6DtD,CAAA;AAED,eAAe,aAAa,CAAA"}
@@ -0,0 +1,89 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React, { useCallback, useEffect, useState } from 'react';
4
+ import { allThemePresets } from '../presets.js';
5
+ import { themeLocalStorageKey } from '../providers/Theme/shared.js';
6
+ import { generateThemeColorsCss } from '../utils/themeColors.js';
7
+ const STYLE_ID = 'tm-theme-switcher-css';
8
+ const applyPreset = (preset)=>{
9
+ if (typeof document === 'undefined') return;
10
+ // Generate with the preset name so selectors match `:root[data-theme='name']`
11
+ // and override the SSR-injected theme (same specificity, injected later wins).
12
+ const css = generateThemeColorsCss({
13
+ themeName: preset.name,
14
+ lightMode: preset.lightMode,
15
+ darkMode: preset.darkMode
16
+ });
17
+ let styleElement = document.getElementById(STYLE_ID);
18
+ if (!styleElement) {
19
+ styleElement = document.createElement('style');
20
+ styleElement.id = STYLE_ID;
21
+ document.head.appendChild(styleElement);
22
+ }
23
+ styleElement.innerHTML = css;
24
+ document.documentElement.setAttribute('data-theme', preset.name);
25
+ };
26
+ /**
27
+ * Front-end theme-preset switcher. Lets visitors swap between bundled (or a
28
+ * curated subset of) theme presets at runtime by injecting the preset's colour
29
+ * CSS and updating `data-theme`. The choice is persisted to localStorage and
30
+ * restored on the next visit.
31
+ *
32
+ * Note: colour-mode (light/dark) is handled by {@link ColorModeToggle} and the
33
+ * SSR injector with no flash; preset switching re-applies on mount, so a brief
34
+ * flash to the SSR default can occur before the stored preset is restored.
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * import { ThemeSwitcher } from '@kilivi/payloadcms-theme-management/components/ThemeSwitcher'
39
+ * <ThemeSwitcher presets={[oceanPreset, forestPreset]} />
40
+ * ```
41
+ */ export const ThemeSwitcher = ({ presets, className, style, 'aria-label': ariaLabel, persist = true, onChange })=>{
42
+ const list = presets && presets.length > 0 ? presets : allThemePresets;
43
+ const [selected, setSelected] = useState('');
44
+ // Restore the stored preset (or reflect the current data-theme) after mount.
45
+ useEffect(()=>{
46
+ if (typeof document === 'undefined') return;
47
+ const current = document.documentElement.getAttribute('data-theme') ?? '';
48
+ const stored = persist ? window.localStorage.getItem(themeLocalStorageKey) : null;
49
+ const initial = stored && list.some((p)=>p.name === stored) ? stored : current;
50
+ if (initial) {
51
+ setSelected(initial);
52
+ const preset = list.find((p)=>p.name === initial);
53
+ // Only re-inject when the stored preset differs from what SSR rendered.
54
+ if (preset && stored && stored !== current) {
55
+ applyPreset(preset);
56
+ }
57
+ }
58
+ // Intentionally run once on mount to restore the persisted preset.
59
+ }, []);
60
+ const handleChange = useCallback((event)=>{
61
+ const name = event.target.value;
62
+ const preset = list.find((p)=>p.name === name);
63
+ if (!preset) return;
64
+ setSelected(name);
65
+ applyPreset(preset);
66
+ if (persist) {
67
+ window.localStorage.setItem(themeLocalStorageKey, name);
68
+ }
69
+ onChange?.(name);
70
+ }, [
71
+ list,
72
+ persist,
73
+ onChange
74
+ ]);
75
+ return /*#__PURE__*/ _jsx("select", {
76
+ value: selected,
77
+ onChange: handleChange,
78
+ className: className,
79
+ style: style,
80
+ "aria-label": ariaLabel ?? 'Select theme',
81
+ children: list.map((preset)=>/*#__PURE__*/ _jsx("option", {
82
+ value: preset.name,
83
+ children: preset.label
84
+ }, preset.name))
85
+ });
86
+ };
87
+ export default ThemeSwitcher;
88
+
89
+ //# sourceMappingURL=ThemeSwitcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/ThemeSwitcher.tsx"],"sourcesContent":["'use client'\n\nimport React, { useCallback, useEffect, useState } from 'react'\nimport { allThemePresets, type ThemePreset } from '../presets.js'\nimport { themeLocalStorageKey } from '../providers/Theme/shared.js'\nimport { generateThemeColorsCss } from '../utils/themeColors.js'\n\nconst STYLE_ID = 'tm-theme-switcher-css'\n\nexport type SwitchablePreset = Pick<ThemePreset, 'name' | 'label' | 'lightMode' | 'darkMode'>\n\nexport interface ThemeSwitcherProps {\n /**\n * Presets the visitor can switch between. Defaults to all bundled presets.\n * Pass a curated subset for a focused switcher.\n */\n presets?: SwitchablePreset[]\n className?: string\n style?: React.CSSProperties\n 'aria-label'?: string\n /** Persist the choice to localStorage and restore it on load. Default true. */\n persist?: boolean\n /** Called after a preset is applied. */\n onChange?: (presetName: string) => void\n}\n\nconst applyPreset = (preset: SwitchablePreset): void => {\n if (typeof document === 'undefined') return\n\n // Generate with the preset name so selectors match `:root[data-theme='name']`\n // and override the SSR-injected theme (same specificity, injected later wins).\n const css = generateThemeColorsCss({\n themeName: preset.name,\n lightMode: preset.lightMode,\n darkMode: preset.darkMode,\n })\n\n let styleElement = document.getElementById(STYLE_ID) as HTMLStyleElement | null\n if (!styleElement) {\n styleElement = document.createElement('style')\n styleElement.id = STYLE_ID\n document.head.appendChild(styleElement)\n }\n styleElement.innerHTML = css\n document.documentElement.setAttribute('data-theme', preset.name)\n}\n\n/**\n * Front-end theme-preset switcher. Lets visitors swap between bundled (or a\n * curated subset of) theme presets at runtime by injecting the preset's colour\n * CSS and updating `data-theme`. The choice is persisted to localStorage and\n * restored on the next visit.\n *\n * Note: colour-mode (light/dark) is handled by {@link ColorModeToggle} and the\n * SSR injector with no flash; preset switching re-applies on mount, so a brief\n * flash to the SSR default can occur before the stored preset is restored.\n *\n * @example\n * ```tsx\n * import { ThemeSwitcher } from '@kilivi/payloadcms-theme-management/components/ThemeSwitcher'\n * <ThemeSwitcher presets={[oceanPreset, forestPreset]} />\n * ```\n */\nexport const ThemeSwitcher: React.FC<ThemeSwitcherProps> = ({\n presets,\n className,\n style,\n 'aria-label': ariaLabel,\n persist = true,\n onChange,\n}) => {\n const list: SwitchablePreset[] = presets && presets.length > 0 ? presets : allThemePresets\n const [selected, setSelected] = useState<string>('')\n\n // Restore the stored preset (or reflect the current data-theme) after mount.\n useEffect(() => {\n if (typeof document === 'undefined') return\n\n const current = document.documentElement.getAttribute('data-theme') ?? ''\n const stored = persist ? window.localStorage.getItem(themeLocalStorageKey) : null\n const initial = stored && list.some((p) => p.name === stored) ? stored : current\n\n if (initial) {\n setSelected(initial)\n const preset = list.find((p) => p.name === initial)\n // Only re-inject when the stored preset differs from what SSR rendered.\n if (preset && stored && stored !== current) {\n applyPreset(preset)\n }\n }\n // Intentionally run once on mount to restore the persisted preset.\n }, [])\n\n const handleChange = useCallback(\n (event: React.ChangeEvent<HTMLSelectElement>) => {\n const name = event.target.value\n const preset = list.find((p) => p.name === name)\n if (!preset) return\n\n setSelected(name)\n applyPreset(preset)\n if (persist) {\n window.localStorage.setItem(themeLocalStorageKey, name)\n }\n onChange?.(name)\n },\n [list, persist, onChange],\n )\n\n return (\n <select\n value={selected}\n onChange={handleChange}\n className={className}\n style={style}\n aria-label={ariaLabel ?? 'Select theme'}\n >\n {list.map((preset) => (\n <option key={preset.name} value={preset.name}>\n {preset.label}\n </option>\n ))}\n </select>\n )\n}\n\nexport default ThemeSwitcher\n"],"names":["React","useCallback","useEffect","useState","allThemePresets","themeLocalStorageKey","generateThemeColorsCss","STYLE_ID","applyPreset","preset","document","css","themeName","name","lightMode","darkMode","styleElement","getElementById","createElement","id","head","appendChild","innerHTML","documentElement","setAttribute","ThemeSwitcher","presets","className","style","ariaLabel","persist","onChange","list","length","selected","setSelected","current","getAttribute","stored","window","localStorage","getItem","initial","some","p","find","handleChange","event","target","value","setItem","select","aria-label","map","option","label"],"mappings":"AAAA;;AAEA,OAAOA,SAASC,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAC/D,SAASC,eAAe,QAA0B,gBAAe;AACjE,SAASC,oBAAoB,QAAQ,+BAA8B;AACnE,SAASC,sBAAsB,QAAQ,0BAAyB;AAEhE,MAAMC,WAAW;AAmBjB,MAAMC,cAAc,CAACC;IACnB,IAAI,OAAOC,aAAa,aAAa;IAErC,8EAA8E;IAC9E,+EAA+E;IAC/E,MAAMC,MAAML,uBAAuB;QACjCM,WAAWH,OAAOI,IAAI;QACtBC,WAAWL,OAAOK,SAAS;QAC3BC,UAAUN,OAAOM,QAAQ;IAC3B;IAEA,IAAIC,eAAeN,SAASO,cAAc,CAACV;IAC3C,IAAI,CAACS,cAAc;QACjBA,eAAeN,SAASQ,aAAa,CAAC;QACtCF,aAAaG,EAAE,GAAGZ;QAClBG,SAASU,IAAI,CAACC,WAAW,CAACL;IAC5B;IACAA,aAAaM,SAAS,GAAGX;IACzBD,SAASa,eAAe,CAACC,YAAY,CAAC,cAAcf,OAAOI,IAAI;AACjE;AAEA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,MAAMY,gBAA8C,CAAC,EAC1DC,OAAO,EACPC,SAAS,EACTC,KAAK,EACL,cAAcC,SAAS,EACvBC,UAAU,IAAI,EACdC,QAAQ,EACT;IACC,MAAMC,OAA2BN,WAAWA,QAAQO,MAAM,GAAG,IAAIP,UAAUtB;IAC3E,MAAM,CAAC8B,UAAUC,YAAY,GAAGhC,SAAiB;IAEjD,6EAA6E;IAC7ED,UAAU;QACR,IAAI,OAAOQ,aAAa,aAAa;QAErC,MAAM0B,UAAU1B,SAASa,eAAe,CAACc,YAAY,CAAC,iBAAiB;QACvE,MAAMC,SAASR,UAAUS,OAAOC,YAAY,CAACC,OAAO,CAACpC,wBAAwB;QAC7E,MAAMqC,UAAUJ,UAAUN,KAAKW,IAAI,CAAC,CAACC,IAAMA,EAAE/B,IAAI,KAAKyB,UAAUA,SAASF;QAEzE,IAAIM,SAAS;YACXP,YAAYO;YACZ,MAAMjC,SAASuB,KAAKa,IAAI,CAAC,CAACD,IAAMA,EAAE/B,IAAI,KAAK6B;YAC3C,wEAAwE;YACxE,IAAIjC,UAAU6B,UAAUA,WAAWF,SAAS;gBAC1C5B,YAAYC;YACd;QACF;IACA,mEAAmE;IACrE,GAAG,EAAE;IAEL,MAAMqC,eAAe7C,YACnB,CAAC8C;QACC,MAAMlC,OAAOkC,MAAMC,MAAM,CAACC,KAAK;QAC/B,MAAMxC,SAASuB,KAAKa,IAAI,CAAC,CAACD,IAAMA,EAAE/B,IAAI,KAAKA;QAC3C,IAAI,CAACJ,QAAQ;QAEb0B,YAAYtB;QACZL,YAAYC;QACZ,IAAIqB,SAAS;YACXS,OAAOC,YAAY,CAACU,OAAO,CAAC7C,sBAAsBQ;QACpD;QACAkB,WAAWlB;IACb,GACA;QAACmB;QAAMF;QAASC;KAAS;IAG3B,qBACE,KAACoB;QACCF,OAAOf;QACPH,UAAUe;QACVnB,WAAWA;QACXC,OAAOA;QACPwB,cAAYvB,aAAa;kBAExBG,KAAKqB,GAAG,CAAC,CAAC5C,uBACT,KAAC6C;gBAAyBL,OAAOxC,OAAOI,IAAI;0BACzCJ,OAAO8C,KAAK;eADF9C,OAAOI,IAAI;;AAMhC,EAAC;AAED,eAAeY,cAAa"}
@@ -5,12 +5,17 @@ export interface TypographySelection {
5
5
  headingFontCustom?: string | null;
6
6
  baseFontSize?: string | null;
7
7
  lineHeight?: string | null;
8
+ headingWeight?: string | null;
9
+ letterSpacing?: string | null;
8
10
  }
9
11
  export interface ResolvedTypographyPreview {
10
12
  bodyFont: string;
11
13
  headingFont: string;
12
14
  baseFontSize: string;
13
15
  lineHeight: string;
16
+ headingWeight: string;
17
+ headingLetterSpacing: string;
18
+ headingTransform: 'none' | 'uppercase';
14
19
  bodyLabel: string;
15
20
  headingLabel: string;
16
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"typographyPreviewUtils.d.ts","sourceRoot":"","sources":["../../src/components/typographyPreviewUtils.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;CACrB;AAyED,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,mBAAmB,GAAG,IAAI,GAAG,SAAS,EACjD,SAAS,CAAC,EAAE,MAAM,GACjB,yBAAyB,CAmC3B"}
1
+ {"version":3,"file":"typographyPreviewUtils.d.ts","sourceRoot":"","sources":["../../src/components/typographyPreviewUtils.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,gBAAgB,EAAE,MAAM,GAAG,WAAW,CAAA;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;CACrB;AA+ED,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,mBAAmB,GAAG,IAAI,GAAG,SAAS,EACjD,SAAS,CAAC,EAAE,MAAM,GACjB,yBAAyB,CAyC3B"}
@@ -1,24 +1,33 @@
1
- import { BODY_FONT_OPTIONS, FONT_VALUE_TO_CSS, HEADING_FONT_OPTIONS } from '../constants/themeFonts.js';
1
+ import { BODY_FONT_OPTIONS, getFontCatalogEntry, HEADING_FONT_OPTIONS, LETTER_SPACING_TO_CSS, toFontSizeCss } from '../constants/themeFonts.js';
2
2
  import { defaultThemePresets } from '../presets.js';
3
3
  const FALLBACK_THEME = defaultThemePresets[0]?.name ?? 'cool';
4
4
  const fallbackPreset = defaultThemePresets.find((preset)=>preset.name === FALLBACK_THEME) ?? defaultThemePresets[0];
5
5
  const FONT_VAR_FALLBACK = {
6
+ 'var(--font-inter)': '"Inter", system-ui, sans-serif',
6
7
  'var(--font-geist-sans)': '"Geist Sans", system-ui, sans-serif',
7
8
  'var(--font-geist-mono)': '"Geist Mono", SFMono-Regular, ui-monospace, monospace',
8
- 'var(--font-inter)': '"Inter", system-ui, sans-serif',
9
+ 'var(--font-roboto)': '"Roboto", system-ui, sans-serif',
10
+ 'var(--font-open-sans)': '"Open Sans", system-ui, sans-serif',
11
+ 'var(--font-lato)': '"Lato", system-ui, sans-serif',
12
+ 'var(--font-montserrat)': '"Montserrat", system-ui, sans-serif',
13
+ 'var(--font-poppins)': '"Poppins", system-ui, sans-serif',
9
14
  'var(--font-outfit)': '"Outfit", system-ui, sans-serif',
10
15
  'var(--font-playfair)': '"Playfair Display", "Times New Roman", serif',
16
+ 'var(--font-merriweather)': '"Merriweather", "Times New Roman", serif',
17
+ 'var(--font-raleway)': '"Raleway", system-ui, sans-serif',
11
18
  'var(--font-lora)': '"Lora", "Times New Roman", serif',
12
19
  'var(--font-manrope)': '"Manrope", system-ui, sans-serif',
13
20
  'var(--font-urbanist)': '"Urbanist", system-ui, sans-serif'
14
21
  };
15
22
  function withFallback(fontFamily, fallback) {
16
- if (!fontFamily) return fallback;
17
- if (fontFamily.startsWith('var(')) {
18
- const mappedFallback = FONT_VAR_FALLBACK[fontFamily] ?? fallback;
19
- return `${fontFamily}, ${mappedFallback}`;
20
- }
21
- return fontFamily;
23
+ const value = fontFamily?.trim() || fallback;
24
+ // A `var(--font-x)` without a fallback whose variable is not defined (always the
25
+ // case in the admin) makes the whole font-family declaration invalid. Give every
26
+ // such reference a fallback stack so the preview always renders a real face.
27
+ return value.replace(/var\((--[\w-]+)\)/g, (match, name)=>{
28
+ const mapped = FONT_VAR_FALLBACK[match] ?? 'system-ui, sans-serif';
29
+ return `var(${name}, ${mapped})`;
30
+ });
22
31
  }
23
32
  function getPresetTypography(themeName) {
24
33
  const preset = defaultThemePresets.find((item)=>item.name === themeName) ?? fallbackPreset;
@@ -34,9 +43,10 @@ function resolveFontFamily(value, customValue, fallback) {
34
43
  }
35
44
  return withFallback(fallback, fallbackPreset?.typography?.fontFamily ?? 'system-ui, sans-serif');
36
45
  }
37
- const preconfigured = FONT_VALUE_TO_CSS[value];
46
+ // Catalog stacks are complete (variable with fallback + generic family).
47
+ const preconfigured = getFontCatalogEntry(value)?.fontFamily;
38
48
  if (preconfigured) {
39
- return withFallback(preconfigured, fallbackPreset?.typography?.fontFamily ?? 'system-ui, sans-serif');
49
+ return preconfigured;
40
50
  }
41
51
  return withFallback(fallback, fallbackPreset?.typography?.fontFamily ?? 'system-ui, sans-serif');
42
52
  }
@@ -60,13 +70,16 @@ export function resolveTypographyPreview(selection, themeName) {
60
70
  const headingFontCustom = selection?.headingFontCustom ?? undefined;
61
71
  const resolvedBodyFont = resolveFontFamily(bodyFontValue, bodyFontCustom, fallbackBody);
62
72
  const resolvedHeadingFont = resolveFontFamily(headingFontValue, headingFontCustom, fallbackHeading);
63
- const baseFontSize = selection?.baseFontSize && selection.baseFontSize !== 'preset' ? selection.baseFontSize : fallbackSize;
73
+ const baseFontSize = toFontSizeCss(selection?.baseFontSize) ?? fallbackSize;
64
74
  const lineHeight = selection?.lineHeight && selection.lineHeight !== 'preset' ? selection.lineHeight : fallbackLineHeight;
65
75
  return {
66
76
  bodyFont: resolvedBodyFont,
67
77
  headingFont: resolvedHeadingFont,
68
78
  baseFontSize,
69
79
  lineHeight,
80
+ headingWeight: selection?.headingWeight && selection.headingWeight !== 'preset' ? selection.headingWeight : presetTypography?.headingWeight ?? '700',
81
+ headingLetterSpacing: (selection?.letterSpacing && LETTER_SPACING_TO_CSS[selection.letterSpacing]) ?? presetTypography?.headingLetterSpacing ?? 'normal',
82
+ headingTransform: selection?.letterSpacing === 'wider' ? 'uppercase' : 'none',
70
83
  bodyLabel: resolveOptionLabel(BODY_FONT_OPTIONS, bodyFontValue),
71
84
  headingLabel: resolveOptionLabel(HEADING_FONT_OPTIONS, headingFontValue)
72
85
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/typographyPreviewUtils.ts"],"sourcesContent":["import { BODY_FONT_OPTIONS, FONT_VALUE_TO_CSS, HEADING_FONT_OPTIONS } from '../constants/themeFonts.js'\nimport { defaultThemePresets } from '../presets.js'\n\nexport interface TypographySelection {\n bodyFont?: string | null\n headingFont?: string | null\n bodyFontCustom?: string | null\n headingFontCustom?: string | null\n baseFontSize?: string | null\n lineHeight?: string | null\n}\n\nexport interface ResolvedTypographyPreview {\n bodyFont: string\n headingFont: string\n baseFontSize: string\n lineHeight: string\n bodyLabel: string\n headingLabel: string\n}\n\nconst FALLBACK_THEME = defaultThemePresets[0]?.name ?? 'cool'\nconst fallbackPreset =\n defaultThemePresets.find((preset) => preset.name === FALLBACK_THEME) ?? defaultThemePresets[0]\n\nconst FONT_VAR_FALLBACK: Record<string, string> = {\n 'var(--font-geist-sans)': '\"Geist Sans\", system-ui, sans-serif',\n 'var(--font-geist-mono)': '\"Geist Mono\", SFMono-Regular, ui-monospace, monospace',\n 'var(--font-inter)': '\"Inter\", system-ui, sans-serif',\n 'var(--font-outfit)': '\"Outfit\", system-ui, sans-serif',\n 'var(--font-playfair)': '\"Playfair Display\", \"Times New Roman\", serif',\n 'var(--font-lora)': '\"Lora\", \"Times New Roman\", serif',\n 'var(--font-manrope)': '\"Manrope\", system-ui, sans-serif',\n 'var(--font-urbanist)': '\"Urbanist\", system-ui, sans-serif',\n}\n\nfunction withFallback(fontFamily: string, fallback: string): string {\n if (!fontFamily) return fallback\n\n if (fontFamily.startsWith('var(')) {\n const mappedFallback = FONT_VAR_FALLBACK[fontFamily] ?? fallback\n return `${fontFamily}, ${mappedFallback}`\n }\n\n return fontFamily\n}\n\nfunction getPresetTypography(themeName?: string) {\n const preset = defaultThemePresets.find((item) => item.name === themeName) ?? fallbackPreset\n return preset?.typography\n}\n\nfunction resolveFontFamily(\n value: string | null | undefined,\n customValue: string | null | undefined,\n fallback: string,\n): string {\n if (!value || value === 'preset') {\n return withFallback(fallback, fallbackPreset?.typography?.fontFamily ?? 'system-ui, sans-serif')\n }\n\n if (value === 'custom') {\n if (customValue && customValue.trim().length > 0) {\n return customValue.trim()\n }\n return withFallback(fallback, fallbackPreset?.typography?.fontFamily ?? 'system-ui, sans-serif')\n }\n\n const preconfigured = FONT_VALUE_TO_CSS[value]\n if (preconfigured) {\n return withFallback(\n preconfigured,\n fallbackPreset?.typography?.fontFamily ?? 'system-ui, sans-serif',\n )\n }\n\n return withFallback(fallback, fallbackPreset?.typography?.fontFamily ?? 'system-ui, sans-serif')\n}\n\nfunction resolveOptionLabel(\n options: ReadonlyArray<{ label: string | Record<string, string>; value: string }>,\n value?: string | null,\n locale: 'en' | 'cs' = 'en',\n): string {\n const option = options.find((item) => item.value === value)\n if (!option) return value ?? ''\n if (typeof option.label === 'string') {\n return option.label\n }\n return option.label?.[locale] ?? Object.values(option.label)[0] ?? option.value\n}\n\nexport function resolveTypographyPreview(\n selection: TypographySelection | null | undefined,\n themeName?: string,\n): ResolvedTypographyPreview {\n const presetTypography = getPresetTypography(themeName)\n const fallbackBody = presetTypography?.fontFamily ?? 'system-ui, sans-serif'\n const fallbackHeading = presetTypography?.headingFamily ?? fallbackBody\n const fallbackSize = presetTypography?.baseFontSize ?? '16px'\n const fallbackLineHeight = presetTypography?.lineHeight ?? '1.6'\n\n const bodyFontValue = selection?.bodyFont ?? 'preset'\n const headingFontValue = selection?.headingFont ?? 'preset'\n const bodyFontCustom = selection?.bodyFontCustom ?? undefined\n const headingFontCustom = selection?.headingFontCustom ?? undefined\n const resolvedBodyFont = resolveFontFamily(bodyFontValue, bodyFontCustom, fallbackBody)\n const resolvedHeadingFont = resolveFontFamily(\n headingFontValue,\n headingFontCustom,\n fallbackHeading,\n )\n\n const baseFontSize =\n selection?.baseFontSize && selection.baseFontSize !== 'preset'\n ? selection.baseFontSize\n : fallbackSize\n const lineHeight =\n selection?.lineHeight && selection.lineHeight !== 'preset'\n ? selection.lineHeight\n : fallbackLineHeight\n\n return {\n bodyFont: resolvedBodyFont,\n headingFont: resolvedHeadingFont,\n baseFontSize,\n lineHeight,\n bodyLabel: resolveOptionLabel(BODY_FONT_OPTIONS, bodyFontValue),\n headingLabel: resolveOptionLabel(HEADING_FONT_OPTIONS, headingFontValue),\n }\n}\n"],"names":["BODY_FONT_OPTIONS","FONT_VALUE_TO_CSS","HEADING_FONT_OPTIONS","defaultThemePresets","FALLBACK_THEME","name","fallbackPreset","find","preset","FONT_VAR_FALLBACK","withFallback","fontFamily","fallback","startsWith","mappedFallback","getPresetTypography","themeName","item","typography","resolveFontFamily","value","customValue","trim","length","preconfigured","resolveOptionLabel","options","locale","option","label","Object","values","resolveTypographyPreview","selection","presetTypography","fallbackBody","fallbackHeading","headingFamily","fallbackSize","baseFontSize","fallbackLineHeight","lineHeight","bodyFontValue","bodyFont","headingFontValue","headingFont","bodyFontCustom","undefined","headingFontCustom","resolvedBodyFont","resolvedHeadingFont","bodyLabel","headingLabel"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/typographyPreviewUtils.ts"],"sourcesContent":["import {\n BODY_FONT_OPTIONS,\n getFontCatalogEntry,\n HEADING_FONT_OPTIONS,\n LETTER_SPACING_TO_CSS,\n toFontSizeCss,\n} from '../constants/themeFonts.js'\nimport { defaultThemePresets } from '../presets.js'\n\nexport interface TypographySelection {\n bodyFont?: string | null\n headingFont?: string | null\n bodyFontCustom?: string | null\n headingFontCustom?: string | null\n baseFontSize?: string | null\n lineHeight?: string | null\n headingWeight?: string | null\n letterSpacing?: string | null\n}\n\nexport interface ResolvedTypographyPreview {\n bodyFont: string\n headingFont: string\n baseFontSize: string\n lineHeight: string\n headingWeight: string\n headingLetterSpacing: string\n headingTransform: 'none' | 'uppercase'\n bodyLabel: string\n headingLabel: string\n}\n\nconst FALLBACK_THEME = defaultThemePresets[0]?.name ?? 'cool'\nconst fallbackPreset =\n defaultThemePresets.find((preset) => preset.name === FALLBACK_THEME) ?? defaultThemePresets[0]\n\nconst FONT_VAR_FALLBACK: Record<string, string> = {\n 'var(--font-inter)': '\"Inter\", system-ui, sans-serif',\n 'var(--font-geist-sans)': '\"Geist Sans\", system-ui, sans-serif',\n 'var(--font-geist-mono)': '\"Geist Mono\", SFMono-Regular, ui-monospace, monospace',\n 'var(--font-roboto)': '\"Roboto\", system-ui, sans-serif',\n 'var(--font-open-sans)': '\"Open Sans\", system-ui, sans-serif',\n 'var(--font-lato)': '\"Lato\", system-ui, sans-serif',\n 'var(--font-montserrat)': '\"Montserrat\", system-ui, sans-serif',\n 'var(--font-poppins)': '\"Poppins\", system-ui, sans-serif',\n 'var(--font-outfit)': '\"Outfit\", system-ui, sans-serif',\n 'var(--font-playfair)': '\"Playfair Display\", \"Times New Roman\", serif',\n 'var(--font-merriweather)': '\"Merriweather\", \"Times New Roman\", serif',\n 'var(--font-raleway)': '\"Raleway\", system-ui, sans-serif',\n 'var(--font-lora)': '\"Lora\", \"Times New Roman\", serif',\n 'var(--font-manrope)': '\"Manrope\", system-ui, sans-serif',\n 'var(--font-urbanist)': '\"Urbanist\", system-ui, sans-serif',\n}\n\nfunction withFallback(fontFamily: string, fallback: string): string {\n const value = fontFamily?.trim() || fallback\n\n // A `var(--font-x)` without a fallback whose variable is not defined (always the\n // case in the admin) makes the whole font-family declaration invalid. Give every\n // such reference a fallback stack so the preview always renders a real face.\n return value.replace(/var\\((--[\\w-]+)\\)/g, (match, name: string) => {\n const mapped = FONT_VAR_FALLBACK[match] ?? 'system-ui, sans-serif'\n return `var(${name}, ${mapped})`\n })\n}\n\nfunction getPresetTypography(themeName?: string) {\n const preset = defaultThemePresets.find((item) => item.name === themeName) ?? fallbackPreset\n return preset?.typography\n}\n\nfunction resolveFontFamily(\n value: string | null | undefined,\n customValue: string | null | undefined,\n fallback: string,\n): string {\n if (!value || value === 'preset') {\n return withFallback(fallback, fallbackPreset?.typography?.fontFamily ?? 'system-ui, sans-serif')\n }\n\n if (value === 'custom') {\n if (customValue && customValue.trim().length > 0) {\n return customValue.trim()\n }\n return withFallback(fallback, fallbackPreset?.typography?.fontFamily ?? 'system-ui, sans-serif')\n }\n\n // Catalog stacks are complete (variable with fallback + generic family).\n const preconfigured = getFontCatalogEntry(value)?.fontFamily\n if (preconfigured) {\n return preconfigured\n }\n\n return withFallback(fallback, fallbackPreset?.typography?.fontFamily ?? 'system-ui, sans-serif')\n}\n\nfunction resolveOptionLabel(\n options: ReadonlyArray<{ label: string | Record<string, string>; value: string }>,\n value?: string | null,\n locale: 'en' | 'cs' = 'en',\n): string {\n const option = options.find((item) => item.value === value)\n if (!option) return value ?? ''\n if (typeof option.label === 'string') {\n return option.label\n }\n return option.label?.[locale] ?? Object.values(option.label)[0] ?? option.value\n}\n\nexport function resolveTypographyPreview(\n selection: TypographySelection | null | undefined,\n themeName?: string,\n): ResolvedTypographyPreview {\n const presetTypography = getPresetTypography(themeName)\n const fallbackBody = presetTypography?.fontFamily ?? 'system-ui, sans-serif'\n const fallbackHeading = presetTypography?.headingFamily ?? fallbackBody\n const fallbackSize = presetTypography?.baseFontSize ?? '16px'\n const fallbackLineHeight = presetTypography?.lineHeight ?? '1.6'\n\n const bodyFontValue = selection?.bodyFont ?? 'preset'\n const headingFontValue = selection?.headingFont ?? 'preset'\n const bodyFontCustom = selection?.bodyFontCustom ?? undefined\n const headingFontCustom = selection?.headingFontCustom ?? undefined\n const resolvedBodyFont = resolveFontFamily(bodyFontValue, bodyFontCustom, fallbackBody)\n const resolvedHeadingFont = resolveFontFamily(\n headingFontValue,\n headingFontCustom,\n fallbackHeading,\n )\n\n const baseFontSize = toFontSizeCss(selection?.baseFontSize) ?? fallbackSize\n const lineHeight =\n selection?.lineHeight && selection.lineHeight !== 'preset'\n ? selection.lineHeight\n : fallbackLineHeight\n\n return {\n bodyFont: resolvedBodyFont,\n headingFont: resolvedHeadingFont,\n baseFontSize,\n lineHeight,\n headingWeight:\n selection?.headingWeight && selection.headingWeight !== 'preset'\n ? selection.headingWeight\n : (presetTypography?.headingWeight ?? '700'),\n headingLetterSpacing:\n (selection?.letterSpacing && LETTER_SPACING_TO_CSS[selection.letterSpacing]) ??\n presetTypography?.headingLetterSpacing ??\n 'normal',\n headingTransform: selection?.letterSpacing === 'wider' ? 'uppercase' : 'none',\n bodyLabel: resolveOptionLabel(BODY_FONT_OPTIONS, bodyFontValue),\n headingLabel: resolveOptionLabel(HEADING_FONT_OPTIONS, headingFontValue),\n }\n}\n"],"names":["BODY_FONT_OPTIONS","getFontCatalogEntry","HEADING_FONT_OPTIONS","LETTER_SPACING_TO_CSS","toFontSizeCss","defaultThemePresets","FALLBACK_THEME","name","fallbackPreset","find","preset","FONT_VAR_FALLBACK","withFallback","fontFamily","fallback","value","trim","replace","match","mapped","getPresetTypography","themeName","item","typography","resolveFontFamily","customValue","length","preconfigured","resolveOptionLabel","options","locale","option","label","Object","values","resolveTypographyPreview","selection","presetTypography","fallbackBody","fallbackHeading","headingFamily","fallbackSize","baseFontSize","fallbackLineHeight","lineHeight","bodyFontValue","bodyFont","headingFontValue","headingFont","bodyFontCustom","undefined","headingFontCustom","resolvedBodyFont","resolvedHeadingFont","headingWeight","headingLetterSpacing","letterSpacing","headingTransform","bodyLabel","headingLabel"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Shared device-preview definitions used by:
3
+ * - the in-admin live preview panels (AppearancePreviewField / ThemePreviewField device switcher)
4
+ * - the native Payload Live Preview toolbar (admin.livePreview.breakpoints)
5
+ *
6
+ * Keeping a single source of truth means the device chrome shown inside the
7
+ * theme editor matches the iframe sizes editors get in the real Live Preview.
8
+ */
9
+ export type DevicePreviewId = 'mobile' | 'tablet' | 'desktop';
10
+ export type DevicePreview = {
11
+ /** Stable identifier, also used as Payload breakpoint `name`. */
12
+ id: DevicePreviewId;
13
+ /** Human-readable label (bilingual handled at the UI layer). */
14
+ label: string;
15
+ /** Czech label for admin UI. */
16
+ labelCs: string;
17
+ /** Viewport width in px. */
18
+ width: number;
19
+ /** Viewport height in px. */
20
+ height: number;
21
+ };
22
+ export declare const DEVICE_PREVIEWS: readonly DevicePreview[];
23
+ export declare const DEFAULT_DEVICE_PREVIEW_ID: DevicePreviewId;
24
+ export declare const getDevicePreview: (id: DevicePreviewId) => DevicePreview;
25
+ /**
26
+ * Default breakpoints for Payload's native Live Preview toolbar.
27
+ * Derived from DEVICE_PREVIEWS so both stay in sync.
28
+ */
29
+ export declare const DEFAULT_LIVE_PREVIEW_BREAKPOINTS: Array<{
30
+ name: string;
31
+ label: string;
32
+ width: number;
33
+ height: number;
34
+ }>;
35
+ //# sourceMappingURL=devicePreviews.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devicePreviews.d.ts","sourceRoot":"","sources":["../../src/constants/devicePreviews.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;AAE7D,MAAM,MAAM,aAAa,GAAG;IAC1B,iEAAiE;IACjE,EAAE,EAAE,eAAe,CAAA;IACnB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAA;IACb,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,SAAS,aAAa,EAsB1C,CAAA;AAEV,eAAO,MAAM,yBAAyB,EAAE,eAA2B,CAAA;AAEnE,eAAO,MAAM,gBAAgB,GAAI,IAAI,eAAe,KAAG,aAC4C,CAAA;AAEnG;;;GAGG;AACH,eAAO,MAAM,gCAAgC,EAAE,KAAK,CAAC;IACnD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAKE,CAAA"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Shared device-preview definitions used by:
3
+ * - the in-admin live preview panels (AppearancePreviewField / ThemePreviewField device switcher)
4
+ * - the native Payload Live Preview toolbar (admin.livePreview.breakpoints)
5
+ *
6
+ * Keeping a single source of truth means the device chrome shown inside the
7
+ * theme editor matches the iframe sizes editors get in the real Live Preview.
8
+ */ export const DEVICE_PREVIEWS = [
9
+ {
10
+ id: 'mobile',
11
+ label: 'Mobile',
12
+ labelCs: 'Mobil',
13
+ width: 390,
14
+ height: 844
15
+ },
16
+ {
17
+ id: 'tablet',
18
+ label: 'Tablet',
19
+ labelCs: 'Tablet',
20
+ width: 768,
21
+ height: 1024
22
+ },
23
+ {
24
+ id: 'desktop',
25
+ label: 'Desktop',
26
+ labelCs: 'Desktop',
27
+ width: 1440,
28
+ height: 900
29
+ }
30
+ ];
31
+ export const DEFAULT_DEVICE_PREVIEW_ID = 'desktop';
32
+ export const getDevicePreview = (id)=>DEVICE_PREVIEWS.find((device)=>device.id === id) ?? DEVICE_PREVIEWS[DEVICE_PREVIEWS.length - 1];
33
+ /**
34
+ * Default breakpoints for Payload's native Live Preview toolbar.
35
+ * Derived from DEVICE_PREVIEWS so both stay in sync.
36
+ */ export const DEFAULT_LIVE_PREVIEW_BREAKPOINTS = DEVICE_PREVIEWS.map((device)=>({
37
+ name: device.id,
38
+ label: device.label,
39
+ width: device.width,
40
+ height: device.height
41
+ }));
42
+
43
+ //# sourceMappingURL=devicePreviews.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants/devicePreviews.ts"],"sourcesContent":["/**\n * Shared device-preview definitions used by:\n * - the in-admin live preview panels (AppearancePreviewField / ThemePreviewField device switcher)\n * - the native Payload Live Preview toolbar (admin.livePreview.breakpoints)\n *\n * Keeping a single source of truth means the device chrome shown inside the\n * theme editor matches the iframe sizes editors get in the real Live Preview.\n */\n\nexport type DevicePreviewId = 'mobile' | 'tablet' | 'desktop'\n\nexport type DevicePreview = {\n /** Stable identifier, also used as Payload breakpoint `name`. */\n id: DevicePreviewId\n /** Human-readable label (bilingual handled at the UI layer). */\n label: string\n /** Czech label for admin UI. */\n labelCs: string\n /** Viewport width in px. */\n width: number\n /** Viewport height in px. */\n height: number\n}\n\nexport const DEVICE_PREVIEWS: readonly DevicePreview[] = [\n {\n id: 'mobile',\n label: 'Mobile',\n labelCs: 'Mobil',\n width: 390,\n height: 844,\n },\n {\n id: 'tablet',\n label: 'Tablet',\n labelCs: 'Tablet',\n width: 768,\n height: 1024,\n },\n {\n id: 'desktop',\n label: 'Desktop',\n labelCs: 'Desktop',\n width: 1440,\n height: 900,\n },\n] as const\n\nexport const DEFAULT_DEVICE_PREVIEW_ID: DevicePreviewId = 'desktop'\n\nexport const getDevicePreview = (id: DevicePreviewId): DevicePreview =>\n DEVICE_PREVIEWS.find((device) => device.id === id) ?? DEVICE_PREVIEWS[DEVICE_PREVIEWS.length - 1]\n\n/**\n * Default breakpoints for Payload's native Live Preview toolbar.\n * Derived from DEVICE_PREVIEWS so both stay in sync.\n */\nexport const DEFAULT_LIVE_PREVIEW_BREAKPOINTS: Array<{\n name: string\n label: string\n width: number\n height: number\n}> = DEVICE_PREVIEWS.map((device) => ({\n name: device.id,\n label: device.label,\n width: device.width,\n height: device.height,\n}))\n"],"names":["DEVICE_PREVIEWS","id","label","labelCs","width","height","DEFAULT_DEVICE_PREVIEW_ID","getDevicePreview","find","device","length","DEFAULT_LIVE_PREVIEW_BREAKPOINTS","map","name"],"mappings":"AAAA;;;;;;;CAOC,GAiBD,OAAO,MAAMA,kBAA4C;IACvD;QACEC,IAAI;QACJC,OAAO;QACPC,SAAS;QACTC,OAAO;QACPC,QAAQ;IACV;IACA;QACEJ,IAAI;QACJC,OAAO;QACPC,SAAS;QACTC,OAAO;QACPC,QAAQ;IACV;IACA;QACEJ,IAAI;QACJC,OAAO;QACPC,SAAS;QACTC,OAAO;QACPC,QAAQ;IACV;CACD,CAAS;AAEV,OAAO,MAAMC,4BAA6C,UAAS;AAEnE,OAAO,MAAMC,mBAAmB,CAACN,KAC/BD,gBAAgBQ,IAAI,CAAC,CAACC,SAAWA,OAAOR,EAAE,KAAKA,OAAOD,eAAe,CAACA,gBAAgBU,MAAM,GAAG,EAAE,CAAA;AAEnG;;;CAGC,GACD,OAAO,MAAMC,mCAKRX,gBAAgBY,GAAG,CAAC,CAACH,SAAY,CAAA;QACpCI,MAAMJ,OAAOR,EAAE;QACfC,OAAOO,OAAOP,KAAK;QACnBE,OAAOK,OAAOL,KAAK;QACnBC,QAAQI,OAAOJ,MAAM;IACvB,CAAA,GAAG"}