@kilivi/payloadcms-theme-management 0.5.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +531 -40
  2. package/dist/app/api/google-fonts/route.d.ts +31 -0
  3. package/dist/app/api/google-fonts/route.d.ts.map +1 -0
  4. package/dist/app/api/google-fonts/route.js +925 -0
  5. package/dist/app/api/google-fonts/route.js.map +1 -0
  6. package/dist/fields/FontSelectField.js +1 -1
  7. package/dist/fields/FontSelectField.js.map +1 -1
  8. package/dist/fields/ThemeTokenSelectField.d.ts.map +1 -1
  9. package/dist/fields/ThemeTokenSelectField.js +12 -5
  10. package/dist/fields/ThemeTokenSelectField.js.map +1 -1
  11. package/dist/fields/themeConfigurationField.d.ts.map +1 -1
  12. package/dist/fields/themeConfigurationField.js +25 -15
  13. package/dist/fields/themeConfigurationField.js.map +1 -1
  14. package/dist/globals/ThemeSettings/FontPicker/FontPreview.d.ts +17 -0
  15. package/dist/globals/ThemeSettings/FontPicker/FontPreview.d.ts.map +1 -0
  16. package/dist/globals/ThemeSettings/FontPicker/FontPreview.js +229 -0
  17. package/dist/globals/ThemeSettings/FontPicker/FontPreview.js.map +1 -0
  18. package/dist/globals/ThemeSettings/FontPicker/PreviewContent.d.ts +20 -0
  19. package/dist/globals/ThemeSettings/FontPicker/PreviewContent.d.ts.map +1 -0
  20. package/dist/globals/ThemeSettings/FontPicker/PreviewContent.js +400 -0
  21. package/dist/globals/ThemeSettings/FontPicker/PreviewContent.js.map +1 -0
  22. package/dist/globals/ThemeSettings/FontPicker/index.d.ts +25 -0
  23. package/dist/globals/ThemeSettings/FontPicker/index.d.ts.map +1 -0
  24. package/dist/globals/ThemeSettings/FontPicker/index.js +483 -0
  25. package/dist/globals/ThemeSettings/FontPicker/index.js.map +1 -0
  26. package/dist/globals/ThemeSettings/FontPicker/styles.css +337 -0
  27. package/dist/globals/ThemeSettings/FontPicker/translations.d.ts +76 -0
  28. package/dist/globals/ThemeSettings/FontPicker/translations.d.ts.map +1 -0
  29. package/dist/globals/ThemeSettings/FontPicker/translations.js +147 -0
  30. package/dist/globals/ThemeSettings/FontPicker/translations.js.map +1 -0
  31. package/dist/globals/ThemeSettings/config.d.ts +10 -0
  32. package/dist/globals/ThemeSettings/config.d.ts.map +1 -0
  33. package/dist/globals/ThemeSettings/config.js +171 -0
  34. package/dist/globals/ThemeSettings/config.js.map +1 -0
  35. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.d.ts +5 -0
  36. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.d.ts.map +1 -0
  37. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.js +177 -0
  38. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.js.map +1 -0
  39. package/dist/globals/ThemeSettings/fields/TypographyInfoField.d.ts +5 -0
  40. package/dist/globals/ThemeSettings/fields/TypographyInfoField.d.ts.map +1 -0
  41. package/dist/globals/ThemeSettings/fields/TypographyInfoField.js +89 -0
  42. package/dist/globals/ThemeSettings/fields/TypographyInfoField.js.map +1 -0
  43. package/dist/globals/ThemeSettings/index.d.ts +4 -0
  44. package/dist/globals/ThemeSettings/index.d.ts.map +1 -0
  45. package/dist/globals/ThemeSettings/index.js +4 -0
  46. package/dist/globals/ThemeSettings/index.js.map +1 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +119 -20
  49. package/dist/index.js.map +1 -1
  50. package/dist/types.d.ts +9 -1
  51. package/dist/types.d.ts.map +1 -1
  52. package/dist/types.js.map +1 -1
  53. package/dist/utilities/getThemeSettings.d.ts +34 -0
  54. package/dist/utilities/getThemeSettings.d.ts.map +1 -0
  55. package/dist/utilities/getThemeSettings.js +67 -0
  56. package/dist/utilities/getThemeSettings.js.map +1 -0
  57. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/globals/ThemeSettings/FontPicker/PreviewContent.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\nimport type { GoogleFont } from '../../../app/api/google-fonts/route'\nimport { FontPreview } from './FontPreview'\nimport type { Translations } from './translations'\n\nexport type PreviewTab = 'typography' | 'blog' | 'landing' | 'ui'\n\nexport interface PreviewContentProps {\n font: GoogleFont | null\n activeTab: PreviewTab\n translations: Translations\n}\n\n/**\n * PreviewContent component\n *\n * Displays different preview modes for the selected font:\n * - Typography: Headers and body text with different weights\n * - Blog Post: Simulated blog post layout\n * - Landing Page: Hero section with CTA\n * - UI Elements: Buttons, inputs, and links\n */\nexport const PreviewContent: React.FC<PreviewContentProps> = ({\n font,\n activeTab,\n translations,\n}) => {\n if (!font) {\n return (\n <div\n style={{\n padding: '40px',\n textAlign: 'center',\n color: 'var(--theme-elevation-400)',\n }}\n >\n {translations.selectFont}\n </div>\n )\n }\n\n const renderTypography = () => (\n <div style={{ padding: '24px' }}>\n <div style={{ marginBottom: '32px' }}>\n <div\n style={{\n fontSize: '12px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--theme-elevation-500)',\n marginBottom: '8px',\n }}\n >\n {translations.typography.h1.split(' ')[0]} (H1)\n </div>\n <FontPreview font={font} variant=\"h1\" text={translations.typography.h1} />\n </div>\n\n <div style={{ marginBottom: '32px' }}>\n <div\n style={{\n fontSize: '12px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--theme-elevation-500)',\n marginBottom: '8px',\n }}\n >\n {translations.typography.h2.split(' ')[0]} (H2)\n </div>\n <FontPreview font={font} variant=\"h2\" text={translations.typography.h2} />\n </div>\n\n <div style={{ marginBottom: '32px' }}>\n <div\n style={{\n fontSize: '12px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--theme-elevation-500)',\n marginBottom: '8px',\n }}\n >\n {translations.typography.h3.split(' ')[0]} (H3)\n </div>\n <FontPreview font={font} variant=\"h3\" text={translations.typography.h3} />\n </div>\n\n <div style={{ marginBottom: '32px' }}>\n <div\n style={{\n fontSize: '12px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--theme-elevation-500)',\n marginBottom: '8px',\n }}\n >\n Body\n </div>\n <FontPreview font={font} variant=\"body\" text={translations.typography.body} />\n </div>\n\n <div style={{ marginBottom: '32px' }}>\n <div\n style={{\n fontSize: '12px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--theme-elevation-500)',\n marginBottom: '8px',\n }}\n >\n Small\n </div>\n <FontPreview font={font} variant=\"small\" text={translations.typography.small} />\n </div>\n\n <div>\n <div\n style={{\n fontSize: '12px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--theme-elevation-500)',\n marginBottom: '8px',\n }}\n >\n Characters\n </div>\n <FontPreview font={font} variant=\"chars\" />\n </div>\n </div>\n )\n\n const renderBlog = () => (\n <div style={{ padding: '24px', maxWidth: '700px', margin: '0 auto' }}>\n <div style={{ marginBottom: '24px' }}>\n <FontPreview font={font} variant=\"h1\" text={translations.blog.title} />\n </div>\n\n <div style={{ marginBottom: '24px', opacity: 0.7 }}>\n <FontPreview font={font} variant=\"h3\" text={translations.blog.subtitle} />\n </div>\n\n <div style={{ marginBottom: '16px' }}>\n <FontPreview font={font} variant=\"body\" text={translations.blog.body} />\n </div>\n\n <div style={{ marginBottom: '16px' }}>\n <FontPreview font={font} variant=\"body\" text={translations.blog.body.split('.')[1] + '.'} />\n </div>\n </div>\n )\n\n const renderLanding = () => (\n <div\n style={{\n padding: '48px 24px',\n textAlign: 'center',\n background:\n 'linear-gradient(135deg, var(--theme-elevation-50) 0%, var(--theme-elevation-100) 100%)',\n minHeight: '400px',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n gap: '24px',\n }}\n >\n <div style={{ maxWidth: '800px' }}>\n <FontPreview font={font} variant=\"h1\" text={translations.landing.hero} />\n </div>\n\n <div style={{ maxWidth: '600px', opacity: 0.8 }}>\n <FontPreview font={font} variant=\"h3\" text={translations.landing.subheading} />\n </div>\n\n <div style={{ marginTop: '16px' }}>\n <div\n style={{\n display: 'inline-block',\n padding: '12px 32px',\n background: 'var(--theme-elevation-900)',\n color: 'var(--theme-elevation-0)',\n borderRadius: '8px',\n fontSize: '18px',\n fontWeight: 600,\n cursor: 'pointer',\n }}\n >\n <FontPreview font={font} variant=\"body\" text={translations.landing.cta} />\n </div>\n </div>\n </div>\n )\n\n const renderUI = () => (\n <div style={{ padding: '24px' }}>\n <div style={{ marginBottom: '32px' }}>\n <div\n style={{\n fontSize: '12px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--theme-elevation-500)',\n marginBottom: '12px',\n }}\n >\n Button\n </div>\n <div\n style={{\n display: 'inline-block',\n padding: '10px 24px',\n background: 'var(--theme-elevation-900)',\n color: 'var(--theme-elevation-0)',\n borderRadius: '6px',\n fontSize: '16px',\n fontWeight: 500,\n cursor: 'pointer',\n }}\n >\n <FontPreview font={font} variant=\"body\" text={translations.ui.button} />\n </div>\n </div>\n\n <div style={{ marginBottom: '32px' }}>\n <div\n style={{\n fontSize: '12px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--theme-elevation-500)',\n marginBottom: '12px',\n }}\n >\n Input\n </div>\n <div\n style={{\n width: '100%',\n maxWidth: '400px',\n padding: '10px 16px',\n background: 'var(--theme-elevation-0)',\n border: '1px solid var(--theme-elevation-300)',\n borderRadius: '6px',\n fontSize: '16px',\n }}\n >\n <FontPreview font={font} variant=\"body\" text={translations.ui.input} />\n </div>\n </div>\n\n <div>\n <div\n style={{\n fontSize: '12px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--theme-elevation-500)',\n marginBottom: '12px',\n }}\n >\n Link\n </div>\n <div\n style={{\n color: 'var(--theme-elevation-700)',\n fontSize: '16px',\n textDecoration: 'underline',\n cursor: 'pointer',\n }}\n >\n <FontPreview font={font} variant=\"body\" text={translations.ui.link} />\n </div>\n </div>\n </div>\n )\n\n switch (activeTab) {\n case 'typography':\n return renderTypography()\n case 'blog':\n return renderBlog()\n case 'landing':\n return renderLanding()\n case 'ui':\n return renderUI()\n default:\n return null\n }\n}\n"],"names":["React","FontPreview","PreviewContent","font","activeTab","translations","div","style","padding","textAlign","color","selectFont","renderTypography","marginBottom","fontSize","fontWeight","textTransform","letterSpacing","typography","h1","split","variant","text","h2","h3","body","small","renderBlog","maxWidth","margin","blog","title","opacity","subtitle","renderLanding","background","minHeight","display","flexDirection","justifyContent","alignItems","gap","landing","hero","subheading","marginTop","borderRadius","cursor","cta","renderUI","ui","button","width","border","input","textDecoration","link"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { TextFieldClientProps } from 'payload';
2
+ import React from 'react';
3
+ import { type Language } from './translations.js';
4
+ export interface FontPickerProps {
5
+ value?: string;
6
+ onChange: (fontFamily: string) => void;
7
+ language?: Language;
8
+ }
9
+ /**
10
+ * FontPicker component
11
+ *
12
+ * A comprehensive Google Fonts picker with:
13
+ * - Two-column layout (preview panel + selection panel)
14
+ * - Search and filters (category, language subset)
15
+ * - Multiple preview modes (typography, blog, landing, ui)
16
+ * - Lazy font loading (only on hover/scroll)
17
+ * - Infinite scroll with pagination
18
+ * - Favorites system with localStorage
19
+ * - View mode toggle (row/grid)
20
+ */
21
+ declare const FontPickerField: React.FC<TextFieldClientProps>;
22
+ declare const FontPickerComponent: React.FC<FontPickerProps>;
23
+ export { FontPickerComponent };
24
+ export default FontPickerField;
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/globals/ThemeSettings/FontPicker/index.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,KAAmD,MAAM,OAAO,CAAA;AAQvE,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAQlE,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB;AAED;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAanD,CAAA;AAED,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA2TlD,CAAA;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAA;AAC9B,eAAe,eAAe,CAAA"}
@@ -0,0 +1,483 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useField } from '@payloadcms/ui';
4
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
5
+ import { FontPreview } from './FontPreview.js';
6
+ import { PreviewContent } from './PreviewContent.js';
7
+ import { getTranslations } from './translations.js';
8
+ // Import CSS for styling
9
+ if (typeof window !== 'undefined') {
10
+ // @ts-expect-error - Dynamic CSS import
11
+ import('./styles.css').catch(()=>{});
12
+ }
13
+ /**
14
+ * FontPicker component
15
+ *
16
+ * A comprehensive Google Fonts picker with:
17
+ * - Two-column layout (preview panel + selection panel)
18
+ * - Search and filters (category, language subset)
19
+ * - Multiple preview modes (typography, blog, landing, ui)
20
+ * - Lazy font loading (only on hover/scroll)
21
+ * - Infinite scroll with pagination
22
+ * - Favorites system with localStorage
23
+ * - View mode toggle (row/grid)
24
+ */ const FontPickerField = (props)=>{
25
+ const { path } = props;
26
+ const { value, setValue } = useField({
27
+ path
28
+ });
29
+ const language = 'en' // Could be extracted from admin config
30
+ ;
31
+ const handleChange = useCallback((fontFamily)=>{
32
+ setValue(fontFamily);
33
+ }, [
34
+ setValue
35
+ ]);
36
+ return /*#__PURE__*/ _jsx(FontPickerComponent, {
37
+ value: value,
38
+ onChange: handleChange,
39
+ language: language
40
+ });
41
+ };
42
+ const FontPickerComponent = ({ value, onChange, language = 'en' })=>{
43
+ const t = getTranslations(language);
44
+ // State
45
+ const [fonts, setFonts] = useState([]);
46
+ const [loading, setLoading] = useState(false);
47
+ const [search, setSearch] = useState('');
48
+ const [category, setCategory] = useState('all');
49
+ const [subset, setSubset] = useState('all');
50
+ const [offset, setOffset] = useState(0);
51
+ const [hasMore, setHasMore] = useState(true);
52
+ const [selectedFont, setSelectedFont] = useState(null);
53
+ const [hoveredFont, setHoveredFont] = useState(null);
54
+ const [previewTab, setPreviewTab] = useState(()=>{
55
+ if (typeof window !== 'undefined') {
56
+ const saved = localStorage.getItem('fontPickerPreviewTab');
57
+ return saved || 'typography';
58
+ }
59
+ return 'typography';
60
+ });
61
+ const [viewMode, setViewMode] = useState(()=>{
62
+ if (typeof window !== 'undefined') {
63
+ const saved = localStorage.getItem('fontPickerViewMode');
64
+ return saved || 'row';
65
+ }
66
+ return 'row';
67
+ });
68
+ const [favorites, setFavorites] = useState(()=>{
69
+ if (typeof window !== 'undefined') {
70
+ const saved = localStorage.getItem('fontPickerFavorites');
71
+ return saved ? JSON.parse(saved) : [];
72
+ }
73
+ return [];
74
+ });
75
+ const [loadedFonts, setLoadedFonts] = useState(new Set());
76
+ // Refs
77
+ const searchTimeoutRef = useRef(null);
78
+ const observerRef = useRef(null);
79
+ const loadMoreRef = useRef(null);
80
+ // Fetch fonts
81
+ const fetchFonts = useCallback(async (reset = false)=>{
82
+ if (loading) return;
83
+ setLoading(true);
84
+ try {
85
+ const currentOffset = reset ? 0 : offset;
86
+ const params = new URLSearchParams({
87
+ search,
88
+ category,
89
+ subset,
90
+ limit: '100',
91
+ offset: currentOffset.toString()
92
+ });
93
+ const response = await fetch(`/api/google-fonts?${params}`);
94
+ const data = await response.json();
95
+ if (reset) {
96
+ setFonts(data.fonts);
97
+ setOffset(100);
98
+ } else {
99
+ setFonts((prev)=>[
100
+ ...prev,
101
+ ...data.fonts
102
+ ]);
103
+ setOffset((prev)=>prev + 100);
104
+ }
105
+ setHasMore(currentOffset + data.fonts.length < data.total);
106
+ } catch (error) {
107
+ console.error('Error fetching fonts:', error);
108
+ } finally{
109
+ setLoading(false);
110
+ }
111
+ }, [
112
+ search,
113
+ category,
114
+ subset,
115
+ offset,
116
+ loading
117
+ ]);
118
+ // Initial load
119
+ useEffect(()=>{
120
+ fetchFonts(true);
121
+ }, [
122
+ search,
123
+ category,
124
+ subset
125
+ ]);
126
+ // Debounced search
127
+ useEffect(()=>{
128
+ if (searchTimeoutRef.current) {
129
+ clearTimeout(searchTimeoutRef.current);
130
+ }
131
+ searchTimeoutRef.current = setTimeout(()=>{
132
+ setOffset(0);
133
+ fetchFonts(true);
134
+ }, 300);
135
+ return ()=>{
136
+ if (searchTimeoutRef.current) {
137
+ clearTimeout(searchTimeoutRef.current);
138
+ }
139
+ };
140
+ }, [
141
+ search
142
+ ]);
143
+ // Infinite scroll
144
+ useEffect(()=>{
145
+ if (!loadMoreRef.current) return;
146
+ observerRef.current = new IntersectionObserver((entries)=>{
147
+ if (entries[0].isIntersecting && hasMore && !loading) {
148
+ fetchFonts();
149
+ }
150
+ }, {
151
+ threshold: 0.5
152
+ });
153
+ observerRef.current.observe(loadMoreRef.current);
154
+ return ()=>{
155
+ if (observerRef.current) {
156
+ observerRef.current.disconnect();
157
+ }
158
+ };
159
+ }, [
160
+ hasMore,
161
+ loading,
162
+ fetchFonts
163
+ ]);
164
+ // Save preferences to localStorage
165
+ useEffect(()=>{
166
+ localStorage.setItem('fontPickerViewMode', viewMode);
167
+ }, [
168
+ viewMode
169
+ ]);
170
+ useEffect(()=>{
171
+ localStorage.setItem('fontPickerPreviewTab', previewTab);
172
+ }, [
173
+ previewTab
174
+ ]);
175
+ useEffect(()=>{
176
+ localStorage.setItem('fontPickerFavorites', JSON.stringify(favorites));
177
+ }, [
178
+ favorites
179
+ ]);
180
+ // Set initial selected font
181
+ useEffect(()=>{
182
+ if (value && fonts.length > 0 && !selectedFont) {
183
+ const font = fonts.find((f)=>f.family === value);
184
+ if (font) {
185
+ setSelectedFont(font);
186
+ }
187
+ }
188
+ }, [
189
+ value,
190
+ fonts,
191
+ selectedFont
192
+ ]);
193
+ // Handlers
194
+ const handleFontSelect = (font)=>{
195
+ setSelectedFont(font);
196
+ onChange(font.family);
197
+ };
198
+ const handleFontHover = (font)=>{
199
+ setHoveredFont(font);
200
+ // Mark font as needing to be loaded
201
+ if (!loadedFonts.has(font.family)) {
202
+ setLoadedFonts((prev)=>new Set([
203
+ ...prev,
204
+ font.family
205
+ ]));
206
+ }
207
+ };
208
+ const toggleFavorite = (fontFamily)=>{
209
+ setFavorites((prev)=>prev.includes(fontFamily) ? prev.filter((f)=>f !== fontFamily) : [
210
+ ...prev,
211
+ fontFamily
212
+ ]);
213
+ };
214
+ const previewFont = hoveredFont || selectedFont;
215
+ return /*#__PURE__*/ _jsxs("div", {
216
+ className: "font-picker",
217
+ children: [
218
+ /*#__PURE__*/ _jsxs("div", {
219
+ className: "font-picker__preview-panel",
220
+ children: [
221
+ /*#__PURE__*/ _jsx("div", {
222
+ className: "font-picker__preview-tabs",
223
+ children: [
224
+ 'typography',
225
+ 'blog',
226
+ 'landing',
227
+ 'ui'
228
+ ].map((tab)=>/*#__PURE__*/ _jsx("button", {
229
+ className: `font-picker__preview-tab ${previewTab === tab ? 'font-picker__preview-tab--active' : ''}`,
230
+ onClick: ()=>setPreviewTab(tab),
231
+ children: t.previewTabs[tab]
232
+ }, tab))
233
+ }),
234
+ /*#__PURE__*/ _jsx("div", {
235
+ className: "font-picker__preview-content",
236
+ children: /*#__PURE__*/ _jsx(PreviewContent, {
237
+ font: previewFont,
238
+ activeTab: previewTab,
239
+ translations: t
240
+ })
241
+ })
242
+ ]
243
+ }),
244
+ /*#__PURE__*/ _jsxs("div", {
245
+ className: "font-picker__selection-panel",
246
+ children: [
247
+ /*#__PURE__*/ _jsxs("div", {
248
+ className: "font-picker__controls",
249
+ children: [
250
+ /*#__PURE__*/ _jsx("input", {
251
+ type: "text",
252
+ className: "font-picker__search",
253
+ placeholder: t.search,
254
+ value: search,
255
+ onChange: (e)=>setSearch(e.target.value)
256
+ }),
257
+ /*#__PURE__*/ _jsxs("div", {
258
+ className: "font-picker__filters",
259
+ children: [
260
+ /*#__PURE__*/ _jsxs("select", {
261
+ className: "font-picker__select",
262
+ value: category,
263
+ onChange: (e)=>setCategory(e.target.value),
264
+ children: [
265
+ /*#__PURE__*/ _jsx("option", {
266
+ value: "all",
267
+ children: t.categories.all
268
+ }),
269
+ /*#__PURE__*/ _jsx("option", {
270
+ value: "sans-serif",
271
+ children: t.categories['sans-serif']
272
+ }),
273
+ /*#__PURE__*/ _jsx("option", {
274
+ value: "serif",
275
+ children: t.categories.serif
276
+ }),
277
+ /*#__PURE__*/ _jsx("option", {
278
+ value: "display",
279
+ children: t.categories.display
280
+ }),
281
+ /*#__PURE__*/ _jsx("option", {
282
+ value: "handwriting",
283
+ children: t.categories.handwriting
284
+ }),
285
+ /*#__PURE__*/ _jsx("option", {
286
+ value: "monospace",
287
+ children: t.categories.monospace
288
+ })
289
+ ]
290
+ }),
291
+ /*#__PURE__*/ _jsxs("select", {
292
+ className: "font-picker__select",
293
+ value: subset,
294
+ onChange: (e)=>setSubset(e.target.value),
295
+ children: [
296
+ /*#__PURE__*/ _jsx("option", {
297
+ value: "all",
298
+ children: t.subsets.all
299
+ }),
300
+ /*#__PURE__*/ _jsx("option", {
301
+ value: "latin",
302
+ children: t.subsets.latin
303
+ }),
304
+ /*#__PURE__*/ _jsx("option", {
305
+ value: "latin-ext",
306
+ children: t.subsets['latin-ext']
307
+ }),
308
+ /*#__PURE__*/ _jsx("option", {
309
+ value: "cyrillic",
310
+ children: t.subsets.cyrillic
311
+ }),
312
+ /*#__PURE__*/ _jsx("option", {
313
+ value: "greek",
314
+ children: t.subsets.greek
315
+ }),
316
+ /*#__PURE__*/ _jsx("option", {
317
+ value: "vietnamese",
318
+ children: t.subsets.vietnamese
319
+ }),
320
+ /*#__PURE__*/ _jsx("option", {
321
+ value: "arabic",
322
+ children: t.subsets.arabic
323
+ }),
324
+ /*#__PURE__*/ _jsx("option", {
325
+ value: "hebrew",
326
+ children: t.subsets.hebrew
327
+ }),
328
+ /*#__PURE__*/ _jsx("option", {
329
+ value: "thai",
330
+ children: t.subsets.thai
331
+ }),
332
+ /*#__PURE__*/ _jsx("option", {
333
+ value: "japanese",
334
+ children: t.subsets.japanese
335
+ }),
336
+ /*#__PURE__*/ _jsx("option", {
337
+ value: "korean",
338
+ children: t.subsets.korean
339
+ }),
340
+ /*#__PURE__*/ _jsx("option", {
341
+ value: "chinese",
342
+ children: t.subsets.chinese
343
+ })
344
+ ]
345
+ })
346
+ ]
347
+ }),
348
+ /*#__PURE__*/ _jsxs("div", {
349
+ className: "font-picker__view-toggle",
350
+ children: [
351
+ /*#__PURE__*/ _jsx("button", {
352
+ className: `font-picker__view-button ${viewMode === 'row' ? 'font-picker__view-button--active' : ''}`,
353
+ onClick: ()=>setViewMode('row'),
354
+ title: t.viewModes.row,
355
+ children: /*#__PURE__*/ _jsxs("svg", {
356
+ width: "20",
357
+ height: "20",
358
+ viewBox: "0 0 20 20",
359
+ fill: "currentColor",
360
+ children: [
361
+ /*#__PURE__*/ _jsx("rect", {
362
+ x: "2",
363
+ y: "4",
364
+ width: "16",
365
+ height: "2"
366
+ }),
367
+ /*#__PURE__*/ _jsx("rect", {
368
+ x: "2",
369
+ y: "9",
370
+ width: "16",
371
+ height: "2"
372
+ }),
373
+ /*#__PURE__*/ _jsx("rect", {
374
+ x: "2",
375
+ y: "14",
376
+ width: "16",
377
+ height: "2"
378
+ })
379
+ ]
380
+ })
381
+ }),
382
+ /*#__PURE__*/ _jsx("button", {
383
+ className: `font-picker__view-button ${viewMode === 'grid' ? 'font-picker__view-button--active' : ''}`,
384
+ onClick: ()=>setViewMode('grid'),
385
+ title: t.viewModes.grid,
386
+ children: /*#__PURE__*/ _jsxs("svg", {
387
+ width: "20",
388
+ height: "20",
389
+ viewBox: "0 0 20 20",
390
+ fill: "currentColor",
391
+ children: [
392
+ /*#__PURE__*/ _jsx("rect", {
393
+ x: "2",
394
+ y: "2",
395
+ width: "7",
396
+ height: "7"
397
+ }),
398
+ /*#__PURE__*/ _jsx("rect", {
399
+ x: "11",
400
+ y: "2",
401
+ width: "7",
402
+ height: "7"
403
+ }),
404
+ /*#__PURE__*/ _jsx("rect", {
405
+ x: "2",
406
+ y: "11",
407
+ width: "7",
408
+ height: "7"
409
+ }),
410
+ /*#__PURE__*/ _jsx("rect", {
411
+ x: "11",
412
+ y: "11",
413
+ width: "7",
414
+ height: "7"
415
+ })
416
+ ]
417
+ })
418
+ })
419
+ ]
420
+ })
421
+ ]
422
+ }),
423
+ /*#__PURE__*/ _jsxs("div", {
424
+ className: `font-picker__list font-picker__list--${viewMode}`,
425
+ children: [
426
+ fonts.map((font)=>/*#__PURE__*/ _jsxs("div", {
427
+ className: `font-picker__item ${selectedFont?.family === font.family ? 'font-picker__item--selected' : ''}`,
428
+ onClick: ()=>handleFontSelect(font),
429
+ onMouseEnter: ()=>handleFontHover(font),
430
+ children: [
431
+ /*#__PURE__*/ _jsxs("div", {
432
+ className: "font-picker__item-header",
433
+ children: [
434
+ /*#__PURE__*/ _jsx("span", {
435
+ className: "font-picker__item-name",
436
+ children: font.family
437
+ }),
438
+ /*#__PURE__*/ _jsx("button", {
439
+ className: `font-picker__favorite ${favorites.includes(font.family) ? 'font-picker__favorite--active' : ''}`,
440
+ onClick: (e)=>{
441
+ e.stopPropagation();
442
+ toggleFavorite(font.family);
443
+ },
444
+ title: favorites.includes(font.family) ? 'Remove from favorites' : 'Add to favorites',
445
+ children: "★"
446
+ })
447
+ ]
448
+ }),
449
+ /*#__PURE__*/ _jsx("div", {
450
+ className: "font-picker__item-preview",
451
+ children: /*#__PURE__*/ _jsx(FontPreview, {
452
+ font: font,
453
+ variant: "row",
454
+ text: t.typography.body
455
+ })
456
+ })
457
+ ]
458
+ }, font.family)),
459
+ loading && /*#__PURE__*/ _jsx("div", {
460
+ className: "font-picker__loading",
461
+ children: t.loading
462
+ }),
463
+ !loading && fonts.length === 0 && /*#__PURE__*/ _jsx("div", {
464
+ className: "font-picker__empty",
465
+ children: t.noFonts
466
+ }),
467
+ hasMore && /*#__PURE__*/ _jsx("div", {
468
+ ref: loadMoreRef,
469
+ style: {
470
+ height: '20px'
471
+ }
472
+ })
473
+ ]
474
+ })
475
+ ]
476
+ })
477
+ ]
478
+ });
479
+ };
480
+ export { FontPickerComponent };
481
+ export default FontPickerField;
482
+
483
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/globals/ThemeSettings/FontPicker/index.tsx"],"sourcesContent":["'use client'\n\nimport { useField } from '@payloadcms/ui'\nimport type { TextFieldClientProps } from 'payload'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport type {\n FontCategory,\n GoogleFont,\n LanguageSubset,\n} from '../../../app/api/google-fonts/route.js'\nimport { FontPreview } from './FontPreview.js'\nimport { PreviewContent, type PreviewTab } from './PreviewContent.js'\nimport { getTranslations, type Language } from './translations.js'\n\n// Import CSS for styling\nif (typeof window !== 'undefined') {\n // @ts-expect-error - Dynamic CSS import\n import('./styles.css').catch(() => {})\n}\n\nexport interface FontPickerProps {\n value?: string\n onChange: (fontFamily: string) => void\n language?: Language\n}\n\n/**\n * FontPicker component\n *\n * A comprehensive Google Fonts picker with:\n * - Two-column layout (preview panel + selection panel)\n * - Search and filters (category, language subset)\n * - Multiple preview modes (typography, blog, landing, ui)\n * - Lazy font loading (only on hover/scroll)\n * - Infinite scroll with pagination\n * - Favorites system with localStorage\n * - View mode toggle (row/grid)\n */\nconst FontPickerField: React.FC<TextFieldClientProps> = (props) => {\n const { path } = props\n const { value, setValue } = useField<string>({ path })\n const language: Language = 'en' // Could be extracted from admin config\n\n const handleChange = useCallback(\n (fontFamily: string) => {\n setValue(fontFamily)\n },\n [setValue],\n )\n\n return <FontPickerComponent value={value} onChange={handleChange} language={language} />\n}\n\nconst FontPickerComponent: React.FC<FontPickerProps> = ({ value, onChange, language = 'en' }) => {\n const t = getTranslations(language)\n\n // State\n const [fonts, setFonts] = useState<GoogleFont[]>([])\n const [loading, setLoading] = useState(false)\n const [search, setSearch] = useState('')\n const [category, setCategory] = useState<FontCategory>('all')\n const [subset, setSubset] = useState<LanguageSubset>('all')\n const [offset, setOffset] = useState(0)\n const [hasMore, setHasMore] = useState(true)\n const [selectedFont, setSelectedFont] = useState<GoogleFont | null>(null)\n const [hoveredFont, setHoveredFont] = useState<GoogleFont | null>(null)\n const [previewTab, setPreviewTab] = useState<PreviewTab>(() => {\n if (typeof window !== 'undefined') {\n const saved = localStorage.getItem('fontPickerPreviewTab')\n return (saved as PreviewTab) || 'typography'\n }\n return 'typography'\n })\n const [viewMode, setViewMode] = useState<'row' | 'grid'>(() => {\n if (typeof window !== 'undefined') {\n const saved = localStorage.getItem('fontPickerViewMode')\n return (saved as 'row' | 'grid') || 'row'\n }\n return 'row'\n })\n const [favorites, setFavorites] = useState<string[]>(() => {\n if (typeof window !== 'undefined') {\n const saved = localStorage.getItem('fontPickerFavorites')\n return saved ? JSON.parse(saved) : []\n }\n return []\n })\n const [loadedFonts, setLoadedFonts] = useState<Set<string>>(new Set())\n\n // Refs\n const searchTimeoutRef = useRef<NodeJS.Timeout | null>(null)\n const observerRef = useRef<IntersectionObserver | null>(null)\n const loadMoreRef = useRef<HTMLDivElement>(null)\n\n // Fetch fonts\n const fetchFonts = useCallback(\n async (reset = false) => {\n if (loading) return\n\n setLoading(true)\n\n try {\n const currentOffset = reset ? 0 : offset\n const params = new URLSearchParams({\n search,\n category,\n subset,\n limit: '100',\n offset: currentOffset.toString(),\n })\n\n const response = await fetch(`/api/google-fonts?${params}`)\n const data = await response.json()\n\n if (reset) {\n setFonts(data.fonts)\n setOffset(100)\n } else {\n setFonts((prev) => [...prev, ...data.fonts])\n setOffset((prev) => prev + 100)\n }\n\n setHasMore(currentOffset + data.fonts.length < data.total)\n } catch (error) {\n console.error('Error fetching fonts:', error)\n } finally {\n setLoading(false)\n }\n },\n [search, category, subset, offset, loading],\n )\n\n // Initial load\n useEffect(() => {\n fetchFonts(true)\n }, [search, category, subset])\n\n // Debounced search\n useEffect(() => {\n if (searchTimeoutRef.current) {\n clearTimeout(searchTimeoutRef.current)\n }\n\n searchTimeoutRef.current = setTimeout(() => {\n setOffset(0)\n fetchFonts(true)\n }, 300)\n\n return () => {\n if (searchTimeoutRef.current) {\n clearTimeout(searchTimeoutRef.current)\n }\n }\n }, [search])\n\n // Infinite scroll\n useEffect(() => {\n if (!loadMoreRef.current) return\n\n observerRef.current = new IntersectionObserver(\n (entries) => {\n if (entries[0].isIntersecting && hasMore && !loading) {\n fetchFonts()\n }\n },\n { threshold: 0.5 },\n )\n\n observerRef.current.observe(loadMoreRef.current)\n\n return () => {\n if (observerRef.current) {\n observerRef.current.disconnect()\n }\n }\n }, [hasMore, loading, fetchFonts])\n\n // Save preferences to localStorage\n useEffect(() => {\n localStorage.setItem('fontPickerViewMode', viewMode)\n }, [viewMode])\n\n useEffect(() => {\n localStorage.setItem('fontPickerPreviewTab', previewTab)\n }, [previewTab])\n\n useEffect(() => {\n localStorage.setItem('fontPickerFavorites', JSON.stringify(favorites))\n }, [favorites])\n\n // Set initial selected font\n useEffect(() => {\n if (value && fonts.length > 0 && !selectedFont) {\n const font = fonts.find((f) => f.family === value)\n if (font) {\n setSelectedFont(font)\n }\n }\n }, [value, fonts, selectedFont])\n\n // Handlers\n const handleFontSelect = (font: GoogleFont) => {\n setSelectedFont(font)\n onChange(font.family)\n }\n\n const handleFontHover = (font: GoogleFont) => {\n setHoveredFont(font)\n // Mark font as needing to be loaded\n if (!loadedFonts.has(font.family)) {\n setLoadedFonts((prev) => new Set([...prev, font.family]))\n }\n }\n\n const toggleFavorite = (fontFamily: string) => {\n setFavorites((prev) =>\n prev.includes(fontFamily) ? prev.filter((f) => f !== fontFamily) : [...prev, fontFamily],\n )\n }\n\n const previewFont = hoveredFont || selectedFont\n\n return (\n <div className=\"font-picker\">\n {/* eslint-disable-next-line */}\n {/* Preview Panel */}\n <div className=\"font-picker__preview-panel\">\n {/* Preview Tabs */}\n <div className=\"font-picker__preview-tabs\">\n {(['typography', 'blog', 'landing', 'ui'] as PreviewTab[]).map((tab) => (\n <button\n key={tab}\n className={`font-picker__preview-tab ${previewTab === tab ? 'font-picker__preview-tab--active' : ''}`}\n onClick={() => setPreviewTab(tab)}\n >\n {t.previewTabs[tab]}\n </button>\n ))}\n </div>\n\n {/* Preview Content */}\n <div className=\"font-picker__preview-content\">\n <PreviewContent font={previewFont} activeTab={previewTab} translations={t} />\n </div>\n </div>\n\n {/* Selection Panel */}\n <div className=\"font-picker__selection-panel\">\n {/* Controls */}\n <div className=\"font-picker__controls\">\n {/* Search */}\n <input\n type=\"text\"\n className=\"font-picker__search\"\n placeholder={t.search}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n\n {/* Filters */}\n <div className=\"font-picker__filters\">\n <select\n className=\"font-picker__select\"\n value={category}\n onChange={(e) => setCategory(e.target.value as FontCategory)}\n >\n <option value=\"all\">{t.categories.all}</option>\n <option value=\"sans-serif\">{t.categories['sans-serif']}</option>\n <option value=\"serif\">{t.categories.serif}</option>\n <option value=\"display\">{t.categories.display}</option>\n <option value=\"handwriting\">{t.categories.handwriting}</option>\n <option value=\"monospace\">{t.categories.monospace}</option>\n </select>\n\n <select\n className=\"font-picker__select\"\n value={subset}\n onChange={(e) => setSubset(e.target.value as LanguageSubset)}\n >\n <option value=\"all\">{t.subsets.all}</option>\n <option value=\"latin\">{t.subsets.latin}</option>\n <option value=\"latin-ext\">{t.subsets['latin-ext']}</option>\n <option value=\"cyrillic\">{t.subsets.cyrillic}</option>\n <option value=\"greek\">{t.subsets.greek}</option>\n <option value=\"vietnamese\">{t.subsets.vietnamese}</option>\n <option value=\"arabic\">{t.subsets.arabic}</option>\n <option value=\"hebrew\">{t.subsets.hebrew}</option>\n <option value=\"thai\">{t.subsets.thai}</option>\n <option value=\"japanese\">{t.subsets.japanese}</option>\n <option value=\"korean\">{t.subsets.korean}</option>\n <option value=\"chinese\">{t.subsets.chinese}</option>\n </select>\n </div>\n\n {/* View Mode Toggle */}\n <div className=\"font-picker__view-toggle\">\n <button\n className={`font-picker__view-button ${viewMode === 'row' ? 'font-picker__view-button--active' : ''}`}\n onClick={() => setViewMode('row')}\n title={t.viewModes.row}\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <rect x=\"2\" y=\"4\" width=\"16\" height=\"2\" />\n <rect x=\"2\" y=\"9\" width=\"16\" height=\"2\" />\n <rect x=\"2\" y=\"14\" width=\"16\" height=\"2\" />\n </svg>\n </button>\n <button\n className={`font-picker__view-button ${viewMode === 'grid' ? 'font-picker__view-button--active' : ''}`}\n onClick={() => setViewMode('grid')}\n title={t.viewModes.grid}\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <rect x=\"2\" y=\"2\" width=\"7\" height=\"7\" />\n <rect x=\"11\" y=\"2\" width=\"7\" height=\"7\" />\n <rect x=\"2\" y=\"11\" width=\"7\" height=\"7\" />\n <rect x=\"11\" y=\"11\" width=\"7\" height=\"7\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Font List */}\n <div className={`font-picker__list font-picker__list--${viewMode}`}>\n {fonts.map((font) => (\n <div\n key={font.family}\n className={`font-picker__item ${\n selectedFont?.family === font.family ? 'font-picker__item--selected' : ''\n }`}\n onClick={() => handleFontSelect(font)}\n onMouseEnter={() => handleFontHover(font)}\n >\n <div className=\"font-picker__item-header\">\n <span className=\"font-picker__item-name\">{font.family}</span>\n <button\n className={`font-picker__favorite ${\n favorites.includes(font.family) ? 'font-picker__favorite--active' : ''\n }`}\n onClick={(e) => {\n e.stopPropagation()\n toggleFavorite(font.family)\n }}\n title={\n favorites.includes(font.family) ? 'Remove from favorites' : 'Add to favorites'\n }\n >\n ★\n </button>\n </div>\n <div className=\"font-picker__item-preview\">\n <FontPreview font={font} variant=\"row\" text={t.typography.body} />\n </div>\n </div>\n ))}\n\n {/* Loading indicator */}\n {loading && <div className=\"font-picker__loading\">{t.loading}</div>}\n\n {/* No fonts found */}\n {!loading && fonts.length === 0 && <div className=\"font-picker__empty\">{t.noFonts}</div>}\n\n {/* Infinite scroll trigger */}\n {hasMore && <div ref={loadMoreRef} style={{ height: '20px' }} />}\n </div>\n </div>\n </div>\n )\n}\n\nexport { FontPickerComponent }\nexport default FontPickerField\n"],"names":["useField","React","useCallback","useEffect","useRef","useState","FontPreview","PreviewContent","getTranslations","window","catch","FontPickerField","props","path","value","setValue","language","handleChange","fontFamily","FontPickerComponent","onChange","t","fonts","setFonts","loading","setLoading","search","setSearch","category","setCategory","subset","setSubset","offset","setOffset","hasMore","setHasMore","selectedFont","setSelectedFont","hoveredFont","setHoveredFont","previewTab","setPreviewTab","saved","localStorage","getItem","viewMode","setViewMode","favorites","setFavorites","JSON","parse","loadedFonts","setLoadedFonts","Set","searchTimeoutRef","observerRef","loadMoreRef","fetchFonts","reset","currentOffset","params","URLSearchParams","limit","toString","response","fetch","data","json","prev","length","total","error","console","current","clearTimeout","setTimeout","IntersectionObserver","entries","isIntersecting","threshold","observe","disconnect","setItem","stringify","font","find","f","family","handleFontSelect","handleFontHover","has","toggleFavorite","includes","filter","previewFont","div","className","map","tab","button","onClick","previewTabs","activeTab","translations","input","type","placeholder","e","target","select","option","categories","all","serif","display","handwriting","monospace","subsets","latin","cyrillic","greek","vietnamese","arabic","hebrew","thai","japanese","korean","chinese","title","viewModes","row","svg","width","height","viewBox","fill","rect","x","y","grid","onMouseEnter","span","stopPropagation","variant","text","typography","body","noFonts","ref","style"],"mappings":"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"}