@kilivi/payloadcms-theme-management 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/README.md +318 -847
  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.map +1 -1
  12. package/dist/components/ServerThemeInjector.js +41 -14
  13. package/dist/components/ServerThemeInjector.js.map +1 -1
  14. package/dist/components/ThemeLivePreviewSync.d.ts +1 -0
  15. package/dist/components/ThemeLivePreviewSync.d.ts.map +1 -1
  16. package/dist/components/ThemeLivePreviewSync.js +1 -1
  17. package/dist/components/ThemeLivePreviewSync.js.map +1 -1
  18. package/dist/components/ThemeSwitcher.d.ts +36 -0
  19. package/dist/components/ThemeSwitcher.d.ts.map +1 -0
  20. package/dist/components/ThemeSwitcher.js +89 -0
  21. package/dist/components/ThemeSwitcher.js.map +1 -0
  22. package/dist/components/typographyPreviewUtils.d.ts.map +1 -1
  23. package/dist/components/typographyPreviewUtils.js +8 -1
  24. package/dist/components/typographyPreviewUtils.js.map +1 -1
  25. package/dist/constants/devicePreviews.d.ts +35 -0
  26. package/dist/constants/devicePreviews.d.ts.map +1 -0
  27. package/dist/constants/devicePreviews.js +43 -0
  28. package/dist/constants/devicePreviews.js.map +1 -0
  29. package/dist/constants/themeFonts.js +9 -9
  30. package/dist/constants/themeFonts.js.map +1 -1
  31. package/dist/extended-presets.d.ts +0 -4
  32. package/dist/extended-presets.d.ts.map +1 -1
  33. package/dist/extended-presets.js +1 -18
  34. package/dist/extended-presets.js.map +1 -1
  35. package/dist/extended-themes.d.ts.map +1 -1
  36. package/dist/extended-themes.js +428 -0
  37. package/dist/extended-themes.js.map +1 -1
  38. package/dist/fields/AccessibilityAuditField.d.ts +2 -0
  39. package/dist/fields/AccessibilityAuditField.d.ts.map +1 -0
  40. package/dist/fields/AccessibilityAuditField.js +303 -0
  41. package/dist/fields/AccessibilityAuditField.js.map +1 -0
  42. package/dist/fields/AppearancePreviewField.d.ts +2 -0
  43. package/dist/fields/AppearancePreviewField.d.ts.map +1 -0
  44. package/dist/fields/AppearancePreviewField.js +763 -0
  45. package/dist/fields/AppearancePreviewField.js.map +1 -0
  46. package/dist/fields/ColorPickerField.d.ts.map +1 -1
  47. package/dist/fields/ColorPickerField.js +89 -85
  48. package/dist/fields/ColorPickerField.js.map +1 -1
  49. package/dist/fields/FontSelectField.d.ts +6 -0
  50. package/dist/fields/FontSelectField.d.ts.map +1 -1
  51. package/dist/fields/FontSelectField.js +195 -122
  52. package/dist/fields/FontSelectField.js.map +1 -1
  53. package/dist/fields/PaletteGeneratorField.d.ts +7 -0
  54. package/dist/fields/PaletteGeneratorField.d.ts.map +1 -0
  55. package/dist/fields/PaletteGeneratorField.js +327 -0
  56. package/dist/fields/PaletteGeneratorField.js.map +1 -0
  57. package/dist/fields/StylePresetField.d.ts +3 -0
  58. package/dist/fields/StylePresetField.d.ts.map +1 -0
  59. package/dist/fields/StylePresetField.js +462 -0
  60. package/dist/fields/StylePresetField.js.map +1 -0
  61. package/dist/fields/ThemeColorPickerField.css +115 -35
  62. package/dist/fields/ThemeColorPickerField.d.ts.map +1 -1
  63. package/dist/fields/ThemeColorPickerField.js +204 -70
  64. package/dist/fields/ThemeColorPickerField.js.map +1 -1
  65. package/dist/fields/ThemeExportField.d.ts +6 -0
  66. package/dist/fields/ThemeExportField.d.ts.map +1 -0
  67. package/dist/fields/ThemeExportField.js +202 -0
  68. package/dist/fields/ThemeExportField.js.map +1 -0
  69. package/dist/fields/ThemePresetImportField.d.ts.map +1 -1
  70. package/dist/fields/ThemePresetImportField.js +56 -25
  71. package/dist/fields/ThemePresetImportField.js.map +1 -1
  72. package/dist/fields/ThemePreviewField.css +382 -5
  73. package/dist/fields/ThemePreviewField.d.ts.map +1 -1
  74. package/dist/fields/ThemePreviewField.js +268 -170
  75. package/dist/fields/ThemePreviewField.js.map +1 -1
  76. package/dist/fields/ThemeTokenSelectField.d.ts.map +1 -1
  77. package/dist/fields/ThemeTokenSelectField.js +278 -80
  78. package/dist/fields/ThemeTokenSelectField.js.map +1 -1
  79. package/dist/fields/colorFieldHelpers.d.ts +90 -0
  80. package/dist/fields/colorFieldHelpers.d.ts.map +1 -0
  81. package/dist/fields/colorFieldHelpers.js +133 -0
  82. package/dist/fields/colorFieldHelpers.js.map +1 -0
  83. package/dist/fields/colorModeFields.d.ts +1 -1
  84. package/dist/fields/colorModeFields.d.ts.map +1 -1
  85. package/dist/fields/colorModeFields.js +49 -7
  86. package/dist/fields/colorModeFields.js.map +1 -1
  87. package/dist/fields/themeConfigurationField.d.ts +8 -1
  88. package/dist/fields/themeConfigurationField.d.ts.map +1 -1
  89. package/dist/fields/themeConfigurationField.js +961 -29
  90. package/dist/fields/themeConfigurationField.js.map +1 -1
  91. package/dist/globals/ThemeSettings/FontPicker/FontPreview.d.ts.map +1 -1
  92. package/dist/globals/ThemeSettings/FontPicker/FontPreview.js +8 -4
  93. package/dist/globals/ThemeSettings/FontPicker/FontPreview.js.map +1 -1
  94. package/dist/globals/ThemeSettings/FontPicker/index.d.ts.map +1 -1
  95. package/dist/globals/ThemeSettings/FontPicker/index.js +81 -26
  96. package/dist/globals/ThemeSettings/FontPicker/index.js.map +1 -1
  97. package/dist/globals/ThemeSettings/FontPicker/styles.css +98 -41
  98. package/dist/globals/ThemeSettings/FontPicker/translations.d.ts +6 -0
  99. package/dist/globals/ThemeSettings/FontPicker/translations.d.ts.map +1 -1
  100. package/dist/globals/ThemeSettings/FontPicker/translations.js +14 -2
  101. package/dist/globals/ThemeSettings/FontPicker/translations.js.map +1 -1
  102. package/dist/globals/ThemeSettings/config.d.ts.map +1 -1
  103. package/dist/globals/ThemeSettings/config.js +76 -79
  104. package/dist/globals/ThemeSettings/config.js.map +1 -1
  105. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.d.ts.map +1 -1
  106. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.js +63 -62
  107. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.js.map +1 -1
  108. package/dist/globals/ThemeSettings/fields/TypographyInfoField.d.ts.map +1 -1
  109. package/dist/globals/ThemeSettings/fields/TypographyInfoField.js +22 -25
  110. package/dist/globals/ThemeSettings/fields/TypographyInfoField.js.map +1 -1
  111. package/dist/hooks/useAppearanceSession.d.ts +50 -0
  112. package/dist/hooks/useAppearanceSession.d.ts.map +1 -0
  113. package/dist/hooks/useAppearanceSession.js +91 -0
  114. package/dist/hooks/useAppearanceSession.js.map +1 -0
  115. package/dist/hooks/useThemeTranslations.d.ts +13 -0
  116. package/dist/hooks/useThemeTranslations.d.ts.map +1 -0
  117. package/dist/hooks/useThemeTranslations.js +22 -0
  118. package/dist/hooks/useThemeTranslations.js.map +1 -0
  119. package/dist/i18n.d.ts +53 -0
  120. package/dist/i18n.d.ts.map +1 -0
  121. package/dist/i18n.js +53 -0
  122. package/dist/i18n.js.map +1 -0
  123. package/dist/index.d.ts +10 -4
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +50 -29
  126. package/dist/index.js.map +1 -1
  127. package/dist/payload-types.d.ts +24 -0
  128. package/dist/payload-types.d.ts.map +1 -1
  129. package/dist/payload-types.js.map +1 -1
  130. package/dist/presets.d.ts +8 -0
  131. package/dist/presets.d.ts.map +1 -1
  132. package/dist/presets.js.map +1 -1
  133. package/dist/providers/Theme/index.d.ts.map +1 -1
  134. package/dist/providers/Theme/index.js +75 -11
  135. package/dist/providers/Theme/index.js.map +1 -1
  136. package/dist/providers/Theme/themeConfig.d.ts.map +1 -1
  137. package/dist/providers/Theme/themeConfig.js +104 -0
  138. package/dist/providers/Theme/themeConfig.js.map +1 -1
  139. package/dist/providers/Theme/types.d.ts +1 -1
  140. package/dist/providers/Theme/types.d.ts.map +1 -1
  141. package/dist/providers/Theme/types.js +10 -1
  142. package/dist/providers/Theme/types.js.map +1 -1
  143. package/dist/style-presets.d.ts +66 -0
  144. package/dist/style-presets.d.ts.map +1 -0
  145. package/dist/style-presets.js +556 -0
  146. package/dist/style-presets.js.map +1 -0
  147. package/dist/translations.d.ts +159 -1
  148. package/dist/translations.d.ts.map +1 -1
  149. package/dist/translations.js +310 -9
  150. package/dist/translations.js.map +1 -1
  151. package/dist/tweakcn-special-themes.d.ts.map +1 -1
  152. package/dist/tweakcn-special-themes.js +585 -9
  153. package/dist/tweakcn-special-themes.js.map +1 -1
  154. package/dist/types/css.d.js +3 -0
  155. package/dist/types/css.d.js.map +1 -0
  156. package/dist/types.d.ts +26 -0
  157. package/dist/types.d.ts.map +1 -1
  158. package/dist/types.js +1 -1
  159. package/dist/types.js.map +1 -1
  160. package/dist/utilities/getThemeSettings.d.ts.map +1 -1
  161. package/dist/utilities/getThemeSettings.js +2 -14
  162. package/dist/utilities/getThemeSettings.js.map +1 -1
  163. package/dist/utils/contrast.d.ts +63 -0
  164. package/dist/utils/contrast.d.ts.map +1 -0
  165. package/dist/utils/contrast.js +158 -0
  166. package/dist/utils/contrast.js.map +1 -0
  167. package/dist/utils/exportTokens.d.ts +30 -0
  168. package/dist/utils/exportTokens.d.ts.map +1 -0
  169. package/dist/utils/exportTokens.js +183 -0
  170. package/dist/utils/exportTokens.js.map +1 -0
  171. package/dist/utils/generatePalette.d.ts +46 -0
  172. package/dist/utils/generatePalette.d.ts.map +1 -0
  173. package/dist/utils/generatePalette.js +127 -0
  174. package/dist/utils/generatePalette.js.map +1 -0
  175. package/dist/utils/generateThemeCSS.d.ts.map +1 -1
  176. package/dist/utils/generateThemeCSS.js +2 -40
  177. package/dist/utils/generateThemeCSS.js.map +1 -1
  178. package/dist/utils/livePreviewThemeRuntime.d.ts +16 -2
  179. package/dist/utils/livePreviewThemeRuntime.d.ts.map +1 -1
  180. package/dist/utils/livePreviewThemeRuntime.js +37 -14
  181. package/dist/utils/livePreviewThemeRuntime.js.map +1 -1
  182. package/dist/utils/localizedText.d.ts +8 -0
  183. package/dist/utils/localizedText.d.ts.map +1 -0
  184. package/dist/utils/localizedText.js +21 -0
  185. package/dist/utils/localizedText.js.map +1 -0
  186. package/dist/utils/resolveThemeConfiguration.d.ts +45 -1
  187. package/dist/utils/resolveThemeConfiguration.d.ts.map +1 -1
  188. package/dist/utils/resolveThemeConfiguration.js +12 -5
  189. package/dist/utils/resolveThemeConfiguration.js.map +1 -1
  190. package/dist/utils/themeColors.d.ts +3 -0
  191. package/dist/utils/themeColors.d.ts.map +1 -1
  192. package/dist/utils/themeColors.js +30 -1
  193. package/dist/utils/themeColors.js.map +1 -1
  194. package/dist/utils/themeHtmlAttributes.d.ts +32 -12
  195. package/dist/utils/themeHtmlAttributes.d.ts.map +1 -1
  196. package/dist/utils/themeHtmlAttributes.js +38 -5
  197. package/dist/utils/themeHtmlAttributes.js.map +1 -1
  198. package/dist/utils/themeUtils.d.ts +3 -5
  199. package/dist/utils/themeUtils.d.ts.map +1 -1
  200. package/dist/utils/themeUtils.js +416 -6
  201. package/dist/utils/themeUtils.js.map +1 -1
  202. package/dist/utils/tweakcnConverter.d.ts +1 -3
  203. package/dist/utils/tweakcnConverter.d.ts.map +1 -1
  204. package/dist/utils/tweakcnConverter.js +1 -3
  205. package/dist/utils/tweakcnConverter.js.map +1 -1
  206. package/package.json +11 -5
  207. package/dist/components/ThemePreview.d.ts +0 -3
  208. package/dist/components/ThemePreview.d.ts.map +0 -1
  209. package/dist/components/ThemePreview.js +0 -321
  210. package/dist/components/ThemePreview.js.map +0 -1
  211. package/dist/plugin.d.ts +0 -2
  212. package/dist/plugin.d.ts.map +0 -1
  213. package/dist/plugin.js +0 -3
  214. package/dist/plugin.js.map +0 -1
@@ -0,0 +1,327 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useForm, useFormFields } from '@payloadcms/ui';
4
+ import { ImageUp, Sparkles, Wand2 } from 'lucide-react';
5
+ import { useCallback, useEffect, useId, useRef, useState } from 'react';
6
+ import { HexColorInput, HexColorPicker } from 'react-colorful';
7
+ import { createBulkWriter, getAppearanceSession, useAppearanceSession } from '../hooks/useAppearanceSession.js';
8
+ import { useThemeTranslations } from '../hooks/useThemeTranslations.js';
9
+ import { extractDominantColors, generatePaletteFromColor } from '../utils/generatePalette.js';
10
+ /**
11
+ * Admin tool that generates a full light + dark palette from a single brand
12
+ * colour (or a colour extracted from an uploaded logo/image) and writes it
13
+ * straight into the colour-mode fields.
14
+ */ export default function PaletteGeneratorField() {
15
+ const formFields = useFormFields(([state])=>state);
16
+ const { dispatchFields, setModified } = useForm();
17
+ const t = useThemeTranslations().paletteGenerator;
18
+ const fileInputRef = useRef(null);
19
+ const currentPrimary = (()=>{
20
+ const v = formFields?.['themeConfiguration.lightMode.primary']?.value;
21
+ return typeof v === 'string' && v.trim() ? v : '#3b82f6';
22
+ })();
23
+ const [seed, setSeed] = useState(currentPrimary);
24
+ const [swatches, setSwatches] = useState([]);
25
+ const [open, setOpen] = useState(false);
26
+ const [imageError, setImageError] = useState(false);
27
+ const seedTouchedRef = useRef(false);
28
+ // Follow the form's primary colour (e.g. after a preset is applied) until the
29
+ // editor picks a seed of their own.
30
+ useEffect(()=>{
31
+ if (!seedTouchedRef.current) setSeed(currentPrimary);
32
+ }, [
33
+ currentPrimary
34
+ ]);
35
+ const chooseSeed = useCallback((color)=>{
36
+ seedTouchedRef.current = true;
37
+ setSeed(color);
38
+ }, []);
39
+ const { locks } = useAppearanceSession();
40
+ const appearanceT = useThemeTranslations().appearance;
41
+ const colorsLocked = locks.colors;
42
+ const lockNoteId = useId();
43
+ const applyPalette = useCallback(()=>{
44
+ if (getAppearanceSession().locks.colors) return;
45
+ const palette = generatePaletteFromColor(seed);
46
+ const writer = createBulkWriter(formFields, dispatchFields);
47
+ [
48
+ 'lightMode',
49
+ 'darkMode'
50
+ ].forEach((mode)=>{
51
+ const colors = palette[mode];
52
+ Object.keys(colors).forEach((key)=>{
53
+ writer.write(`themeConfiguration.${mode}.${key}`, colors[key]);
54
+ });
55
+ });
56
+ // `dispatchFields` alone does not flip the form's dirty flag, so Payload
57
+ // would keep the Save button disabled after generating a palette.
58
+ setModified(true);
59
+ writer.commit('palette', `${appearanceT.generatedPalette} ${seed}`);
60
+ }, [
61
+ seed,
62
+ formFields,
63
+ dispatchFields,
64
+ setModified,
65
+ appearanceT.generatedPalette
66
+ ]);
67
+ const handleFile = useCallback((event)=>{
68
+ const file = event.target.files?.[0];
69
+ if (!file) return;
70
+ setImageError(false);
71
+ const url = URL.createObjectURL(file);
72
+ const img = new Image();
73
+ img.onload = ()=>{
74
+ const colors = extractDominantColors(img, 6);
75
+ setSwatches(colors);
76
+ if (colors[0]) chooseSeed(colors[0]);
77
+ URL.revokeObjectURL(url);
78
+ };
79
+ img.onerror = ()=>{
80
+ setImageError(true);
81
+ URL.revokeObjectURL(url);
82
+ };
83
+ img.src = url;
84
+ // Allow re-selecting the same file after an error.
85
+ event.target.value = '';
86
+ }, [
87
+ chooseSeed
88
+ ]);
89
+ return /*#__PURE__*/ _jsxs("div", {
90
+ style: {
91
+ padding: '16px',
92
+ borderRadius: '12px',
93
+ border: '1px solid var(--theme-elevation-200)',
94
+ backgroundColor: 'var(--theme-elevation-25)'
95
+ },
96
+ children: [
97
+ /*#__PURE__*/ _jsxs("div", {
98
+ style: {
99
+ display: 'inline-flex',
100
+ alignItems: 'center',
101
+ gap: '6px',
102
+ fontSize: '13px',
103
+ fontWeight: 600,
104
+ color: 'var(--theme-elevation-800)'
105
+ },
106
+ children: [
107
+ /*#__PURE__*/ _jsx(Sparkles, {
108
+ size: 14,
109
+ "aria-hidden": true
110
+ }),
111
+ t.title
112
+ ]
113
+ }),
114
+ /*#__PURE__*/ _jsx("div", {
115
+ style: {
116
+ fontSize: '12px',
117
+ color: 'var(--theme-elevation-600)',
118
+ marginBottom: '12px'
119
+ },
120
+ children: t.subtitle
121
+ }),
122
+ /*#__PURE__*/ _jsxs("div", {
123
+ style: {
124
+ display: 'flex',
125
+ flexWrap: 'wrap',
126
+ alignItems: 'flex-start',
127
+ gap: '16px'
128
+ },
129
+ children: [
130
+ /*#__PURE__*/ _jsxs("div", {
131
+ style: {
132
+ display: 'grid',
133
+ gap: '8px'
134
+ },
135
+ children: [
136
+ /*#__PURE__*/ _jsxs("button", {
137
+ type: "button",
138
+ onClick: ()=>setOpen((o)=>!o),
139
+ "aria-label": `${t.seedLabel}: ${seed}`,
140
+ "aria-expanded": open,
141
+ style: {
142
+ display: 'flex',
143
+ alignItems: 'center',
144
+ gap: '8px',
145
+ padding: '4px 8px 4px 4px',
146
+ borderRadius: '8px',
147
+ border: '1px solid var(--theme-elevation-200)',
148
+ background: 'var(--theme-elevation-50)',
149
+ cursor: 'pointer'
150
+ },
151
+ children: [
152
+ /*#__PURE__*/ _jsx("span", {
153
+ style: {
154
+ width: '28px',
155
+ height: '28px',
156
+ borderRadius: '6px',
157
+ background: seed,
158
+ border: '1px solid rgba(0,0,0,0.1)'
159
+ }
160
+ }),
161
+ /*#__PURE__*/ _jsx("span", {
162
+ style: {
163
+ fontFamily: 'monospace',
164
+ fontSize: '12px',
165
+ color: 'var(--theme-elevation-700)'
166
+ },
167
+ children: seed
168
+ })
169
+ ]
170
+ }),
171
+ open ? /*#__PURE__*/ _jsxs("div", {
172
+ style: {
173
+ display: 'grid',
174
+ gap: '8px'
175
+ },
176
+ children: [
177
+ /*#__PURE__*/ _jsx(HexColorPicker, {
178
+ color: seed,
179
+ onChange: chooseSeed
180
+ }),
181
+ /*#__PURE__*/ _jsx(HexColorInput, {
182
+ color: seed,
183
+ onChange: chooseSeed,
184
+ prefixed: true,
185
+ style: {
186
+ width: '160px',
187
+ padding: '6px 8px',
188
+ borderRadius: '6px',
189
+ border: '1px solid var(--theme-elevation-200)',
190
+ fontFamily: 'monospace',
191
+ fontSize: '12px'
192
+ }
193
+ })
194
+ ]
195
+ }) : null
196
+ ]
197
+ }),
198
+ /*#__PURE__*/ _jsxs("div", {
199
+ style: {
200
+ display: 'grid',
201
+ gap: '8px'
202
+ },
203
+ children: [
204
+ /*#__PURE__*/ _jsxs("button", {
205
+ type: "button",
206
+ onClick: applyPalette,
207
+ disabled: colorsLocked,
208
+ "aria-describedby": colorsLocked ? lockNoteId : undefined,
209
+ style: {
210
+ display: 'inline-flex',
211
+ alignItems: 'center',
212
+ gap: '6px',
213
+ padding: '8px 14px',
214
+ borderRadius: '8px',
215
+ border: 'none',
216
+ cursor: colorsLocked ? 'not-allowed' : 'pointer',
217
+ background: 'var(--theme-elevation-800)',
218
+ color: 'var(--theme-elevation-0)',
219
+ opacity: colorsLocked ? 0.5 : 1,
220
+ fontWeight: 600,
221
+ fontSize: '12px'
222
+ },
223
+ children: [
224
+ /*#__PURE__*/ _jsx(Wand2, {
225
+ size: 14,
226
+ "aria-hidden": true
227
+ }),
228
+ t.generate
229
+ ]
230
+ }),
231
+ /*#__PURE__*/ _jsxs("button", {
232
+ type: "button",
233
+ onClick: ()=>fileInputRef.current?.click(),
234
+ style: {
235
+ display: 'inline-flex',
236
+ alignItems: 'center',
237
+ gap: '6px',
238
+ padding: '8px 14px',
239
+ borderRadius: '8px',
240
+ border: '1px solid var(--theme-elevation-200)',
241
+ cursor: 'pointer',
242
+ background: 'transparent',
243
+ color: 'var(--theme-elevation-700)',
244
+ fontWeight: 600,
245
+ fontSize: '12px'
246
+ },
247
+ children: [
248
+ /*#__PURE__*/ _jsx(ImageUp, {
249
+ size: 14,
250
+ "aria-hidden": true
251
+ }),
252
+ t.fromImage
253
+ ]
254
+ }),
255
+ /*#__PURE__*/ _jsx("input", {
256
+ ref: fileInputRef,
257
+ type: "file",
258
+ accept: "image/*",
259
+ onChange: handleFile,
260
+ style: {
261
+ display: 'none'
262
+ }
263
+ }),
264
+ /*#__PURE__*/ _jsx("div", {
265
+ id: lockNoteId,
266
+ style: {
267
+ fontSize: '12px',
268
+ color: 'var(--theme-elevation-600)',
269
+ maxWidth: '200px'
270
+ },
271
+ children: colorsLocked ? appearanceT.colorsLocked : t.hint
272
+ })
273
+ ]
274
+ })
275
+ ]
276
+ }),
277
+ imageError ? /*#__PURE__*/ _jsx("div", {
278
+ role: "alert",
279
+ style: {
280
+ marginTop: '12px',
281
+ fontSize: '12px',
282
+ color: 'var(--theme-error-600)'
283
+ },
284
+ children: t.imageError
285
+ }) : null,
286
+ swatches.length > 0 ? /*#__PURE__*/ _jsxs("div", {
287
+ style: {
288
+ marginTop: '12px'
289
+ },
290
+ children: [
291
+ /*#__PURE__*/ _jsx("div", {
292
+ style: {
293
+ fontSize: '12px',
294
+ color: 'var(--theme-elevation-600)',
295
+ marginBottom: '6px'
296
+ },
297
+ children: t.pick
298
+ }),
299
+ /*#__PURE__*/ _jsx("div", {
300
+ style: {
301
+ display: 'flex',
302
+ gap: '6px',
303
+ flexWrap: 'wrap'
304
+ },
305
+ children: swatches.map((color)=>/*#__PURE__*/ _jsx("button", {
306
+ type: "button",
307
+ onClick: ()=>chooseSeed(color),
308
+ title: color,
309
+ "aria-label": `${t.useColor} ${color}`,
310
+ "aria-pressed": color === seed,
311
+ style: {
312
+ width: '30px',
313
+ height: '30px',
314
+ borderRadius: '6px',
315
+ background: color,
316
+ cursor: 'pointer',
317
+ border: color === seed ? '2px solid var(--theme-elevation-800)' : '1px solid rgba(0,0,0,0.12)'
318
+ }
319
+ }, color))
320
+ })
321
+ ]
322
+ }) : null
323
+ ]
324
+ });
325
+ }
326
+
327
+ //# sourceMappingURL=PaletteGeneratorField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/fields/PaletteGeneratorField.tsx"],"sourcesContent":["'use client'\n\nimport { useForm, useFormFields } from '@payloadcms/ui'\nimport { ImageUp, Sparkles, Wand2 } from 'lucide-react'\nimport { useCallback, useEffect, useId, useRef, useState } from 'react'\nimport { HexColorInput, HexColorPicker } from 'react-colorful'\nimport {\n createBulkWriter,\n getAppearanceSession,\n useAppearanceSession,\n} from '../hooks/useAppearanceSession.js'\nimport { useThemeTranslations } from '../hooks/useThemeTranslations.js'\nimport {\n extractDominantColors,\n generatePaletteFromColor,\n type GeneratedColorMode,\n} from '../utils/generatePalette.js'\n\n/**\n * Admin tool that generates a full light + dark palette from a single brand\n * colour (or a colour extracted from an uploaded logo/image) and writes it\n * straight into the colour-mode fields.\n */\nexport default function PaletteGeneratorField() {\n const formFields = useFormFields(([state]) => state) as Record<\n string,\n { value?: unknown } | undefined\n >\n const { dispatchFields, setModified } = useForm()\n const t = useThemeTranslations().paletteGenerator\n const fileInputRef = useRef<HTMLInputElement | null>(null)\n\n const currentPrimary = (() => {\n const v = formFields?.['themeConfiguration.lightMode.primary']?.value\n return typeof v === 'string' && v.trim() ? v : '#3b82f6'\n })()\n\n const [seed, setSeed] = useState<string>(currentPrimary)\n const [swatches, setSwatches] = useState<string[]>([])\n const [open, setOpen] = useState(false)\n const [imageError, setImageError] = useState(false)\n const seedTouchedRef = useRef(false)\n\n // Follow the form's primary colour (e.g. after a preset is applied) until the\n // editor picks a seed of their own.\n useEffect(() => {\n if (!seedTouchedRef.current) setSeed(currentPrimary)\n }, [currentPrimary])\n\n const chooseSeed = useCallback((color: string) => {\n seedTouchedRef.current = true\n setSeed(color)\n }, [])\n\n const { locks } = useAppearanceSession()\n const appearanceT = useThemeTranslations().appearance\n const colorsLocked = locks.colors\n const lockNoteId = useId()\n\n const applyPalette = useCallback(() => {\n if (getAppearanceSession().locks.colors) return\n const palette = generatePaletteFromColor(seed)\n const writer = createBulkWriter(formFields, dispatchFields)\n ;(['lightMode', 'darkMode'] as const).forEach((mode) => {\n const colors = palette[mode]\n ;(Object.keys(colors) as Array<keyof GeneratedColorMode>).forEach((key) => {\n writer.write(`themeConfiguration.${mode}.${key}`, colors[key])\n })\n })\n // `dispatchFields` alone does not flip the form's dirty flag, so Payload\n // would keep the Save button disabled after generating a palette.\n setModified(true)\n writer.commit('palette', `${appearanceT.generatedPalette} ${seed}`)\n }, [seed, formFields, dispatchFields, setModified, appearanceT.generatedPalette])\n\n const handleFile = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0]\n if (!file) return\n setImageError(false)\n const url = URL.createObjectURL(file)\n const img = new Image()\n img.onload = () => {\n const colors = extractDominantColors(img, 6)\n setSwatches(colors)\n if (colors[0]) chooseSeed(colors[0])\n URL.revokeObjectURL(url)\n }\n img.onerror = () => {\n setImageError(true)\n URL.revokeObjectURL(url)\n }\n img.src = url\n // Allow re-selecting the same file after an error.\n event.target.value = ''\n }, [chooseSeed])\n\n return (\n <div\n style={{\n padding: '16px',\n borderRadius: '12px',\n border: '1px solid var(--theme-elevation-200)',\n backgroundColor: 'var(--theme-elevation-25)',\n }}\n >\n <div\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: '6px',\n fontSize: '13px',\n fontWeight: 600,\n color: 'var(--theme-elevation-800)',\n }}\n >\n <Sparkles size={14} aria-hidden />\n {t.title}\n </div>\n <div style={{ fontSize: '12px', color: 'var(--theme-elevation-600)', marginBottom: '12px' }}>\n {t.subtitle}\n </div>\n\n <div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'flex-start', gap: '16px' }}>\n {/* Seed colour */}\n <div style={{ display: 'grid', gap: '8px' }}>\n <button\n type=\"button\"\n onClick={() => setOpen((o) => !o)}\n aria-label={`${t.seedLabel}: ${seed}`}\n aria-expanded={open}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n padding: '4px 8px 4px 4px',\n borderRadius: '8px',\n border: '1px solid var(--theme-elevation-200)',\n background: 'var(--theme-elevation-50)',\n cursor: 'pointer',\n }}\n >\n <span\n style={{\n width: '28px',\n height: '28px',\n borderRadius: '6px',\n background: seed,\n border: '1px solid rgba(0,0,0,0.1)',\n }}\n />\n <span style={{ fontFamily: 'monospace', fontSize: '12px', color: 'var(--theme-elevation-700)' }}>\n {seed}\n </span>\n </button>\n {open ? (\n <div style={{ display: 'grid', gap: '8px' }}>\n <HexColorPicker color={seed} onChange={chooseSeed} />\n <HexColorInput\n color={seed}\n onChange={chooseSeed}\n prefixed\n style={{\n width: '160px',\n padding: '6px 8px',\n borderRadius: '6px',\n border: '1px solid var(--theme-elevation-200)',\n fontFamily: 'monospace',\n fontSize: '12px',\n }}\n />\n </div>\n ) : null}\n </div>\n\n {/* Actions */}\n <div style={{ display: 'grid', gap: '8px' }}>\n <button\n type=\"button\"\n onClick={applyPalette}\n disabled={colorsLocked}\n aria-describedby={colorsLocked ? lockNoteId : undefined}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: '6px',\n padding: '8px 14px',\n borderRadius: '8px',\n border: 'none',\n cursor: colorsLocked ? 'not-allowed' : 'pointer',\n background: 'var(--theme-elevation-800)',\n color: 'var(--theme-elevation-0)',\n opacity: colorsLocked ? 0.5 : 1,\n fontWeight: 600,\n fontSize: '12px',\n }}\n >\n <Wand2 size={14} aria-hidden />\n {t.generate}\n </button>\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: '6px',\n padding: '8px 14px',\n borderRadius: '8px',\n border: '1px solid var(--theme-elevation-200)',\n cursor: 'pointer',\n background: 'transparent',\n color: 'var(--theme-elevation-700)',\n fontWeight: 600,\n fontSize: '12px',\n }}\n >\n <ImageUp size={14} aria-hidden />\n {t.fromImage}\n </button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleFile}\n style={{ display: 'none' }}\n />\n <div\n id={lockNoteId}\n style={{ fontSize: '12px', color: 'var(--theme-elevation-600)', maxWidth: '200px' }}\n >\n {colorsLocked ? appearanceT.colorsLocked : t.hint}\n </div>\n </div>\n </div>\n\n {imageError ? (\n <div role=\"alert\" style={{ marginTop: '12px', fontSize: '12px', color: 'var(--theme-error-600)' }}>\n {t.imageError}\n </div>\n ) : null}\n\n {/* Extracted swatches */}\n {swatches.length > 0 ? (\n <div style={{ marginTop: '12px' }}>\n <div style={{ fontSize: '12px', color: 'var(--theme-elevation-600)', marginBottom: '6px' }}>\n {t.pick}\n </div>\n <div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap' }}>\n {swatches.map((color) => (\n <button\n key={color}\n type=\"button\"\n onClick={() => chooseSeed(color)}\n title={color}\n aria-label={`${t.useColor} ${color}`}\n aria-pressed={color === seed}\n style={{\n width: '30px',\n height: '30px',\n borderRadius: '6px',\n background: color,\n cursor: 'pointer',\n border:\n color === seed\n ? '2px solid var(--theme-elevation-800)'\n : '1px solid rgba(0,0,0,0.12)',\n }}\n />\n ))}\n </div>\n </div>\n ) : null}\n </div>\n )\n}\n"],"names":["useForm","useFormFields","ImageUp","Sparkles","Wand2","useCallback","useEffect","useId","useRef","useState","HexColorInput","HexColorPicker","createBulkWriter","getAppearanceSession","useAppearanceSession","useThemeTranslations","extractDominantColors","generatePaletteFromColor","PaletteGeneratorField","formFields","state","dispatchFields","setModified","t","paletteGenerator","fileInputRef","currentPrimary","v","value","trim","seed","setSeed","swatches","setSwatches","open","setOpen","imageError","setImageError","seedTouchedRef","current","chooseSeed","color","locks","appearanceT","appearance","colorsLocked","colors","lockNoteId","applyPalette","palette","writer","forEach","mode","Object","keys","key","write","commit","generatedPalette","handleFile","event","file","target","files","url","URL","createObjectURL","img","Image","onload","revokeObjectURL","onerror","src","div","style","padding","borderRadius","border","backgroundColor","display","alignItems","gap","fontSize","fontWeight","size","aria-hidden","title","marginBottom","subtitle","flexWrap","button","type","onClick","o","aria-label","seedLabel","aria-expanded","background","cursor","span","width","height","fontFamily","onChange","prefixed","disabled","aria-describedby","undefined","opacity","generate","click","fromImage","input","ref","accept","id","maxWidth","hint","role","marginTop","length","pick","map","useColor","aria-pressed"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { TextFieldClientProps } from 'payload';
2
+ export default function StylePresetField(props: TextFieldClientProps): import("react").JSX.Element;
3
+ //# sourceMappingURL=StylePresetField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StylePresetField.d.ts","sourceRoot":"","sources":["../../src/fields/StylePresetField.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AA+HnD,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,+BAgZnE"}