@pixelated-tech/components 3.2.14 → 3.3.1

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 (202) hide show
  1. package/README.COMPONENTS.md +520 -31
  2. package/README.md +163 -49
  3. package/dist/components/cms/cloudinary.image.js +1 -0
  4. package/dist/components/cms/wordpress.components.js +1 -0
  5. package/dist/components/general/sidepanel.js +1 -1
  6. package/dist/components/general/tab.css +105 -0
  7. package/dist/components/general/tab.js +26 -0
  8. package/dist/components/menu/menu-accordion.css +10 -0
  9. package/dist/components/menu/menu-accordion.js +2 -1
  10. package/dist/components/menu/menu-simple.css +0 -7
  11. package/dist/components/seo/metadata.components.js +0 -19
  12. package/dist/components/seo/metadata.functions.js +111 -0
  13. package/dist/components/seo/schema-blogposting.functions.js +42 -0
  14. package/dist/components/seo/schema-blogposting.js +0 -46
  15. package/dist/components/seo/sitemap.js +1 -1
  16. package/dist/components/shoppingcart/shoppingcart.components.js +4 -4
  17. package/dist/components/sitebuilder/config/CompoundFontSelector.css +25 -0
  18. package/dist/components/sitebuilder/config/CompoundFontSelector.js +41 -0
  19. package/dist/components/sitebuilder/config/ConfigBuilder.css +277 -0
  20. package/dist/components/sitebuilder/config/ConfigBuilder.js +380 -0
  21. package/dist/components/sitebuilder/config/ConfigEngine.js +82 -0
  22. package/dist/components/sitebuilder/config/FontSelector.css +82 -0
  23. package/dist/components/sitebuilder/config/FontSelector.js +115 -0
  24. package/dist/components/sitebuilder/config/google-fonts.js +112 -0
  25. package/dist/components/{pagebuilder → sitebuilder}/form/form.css +55 -34
  26. package/dist/components/sitebuilder/form/formbuilder.js +107 -0
  27. package/dist/components/sitebuilder/form/formcomponents.js +380 -0
  28. package/dist/components/sitebuilder/form/formengine.js +82 -0
  29. package/dist/components/{pagebuilder/form/form.js → sitebuilder/form/formextractor.js} +10 -211
  30. package/dist/components/sitebuilder/form/formutils.js +206 -0
  31. package/dist/components/sitebuilder/form/formvalidator.js +123 -0
  32. package/dist/components/{pagebuilder → sitebuilder/page}/components/ComponentPropertiesForm.js +1 -1
  33. package/dist/components/{pagebuilder → sitebuilder/page}/components/PageBuilderUI.js +2 -2
  34. package/dist/components/{pagebuilder → sitebuilder/page}/components/PageEngine.js +1 -1
  35. package/dist/components/sitebuilder/page/documentation/api-examples/save-route-example.js +37 -0
  36. package/dist/components/{pagebuilder → sitebuilder/page}/lib/componentMap.js +3 -3
  37. package/dist/components/{pagebuilder → sitebuilder/page}/lib/componentMetadata.js +2 -2
  38. package/dist/components/{pagebuilder → sitebuilder/page}/lib/pageStorageContentful.js +2 -2
  39. package/dist/components/sitebuilder/page/lib/pageStorageTypes.js +1 -0
  40. package/dist/components/structured/markdown.js +1 -0
  41. package/dist/components/structured/recipe.js +1 -0
  42. package/dist/components/structured/timeline.js +1 -0
  43. package/dist/css/pixelated.global.css +0 -35
  44. package/dist/css/pixelated.grid.scss +4 -0
  45. package/dist/css/pixelated.visualdesign.scss +88 -0
  46. package/dist/data/form.json +18 -18
  47. package/dist/data/routes.json +32 -1
  48. package/dist/data/shipping.to.json +9 -9
  49. package/dist/data/siteinfo-form.json +200 -0
  50. package/dist/data/visualdesignform.json +244 -0
  51. package/dist/index.js +33 -21
  52. package/dist/index.server.js +24 -17
  53. package/dist/types/components/cms/cloudinary.image.d.ts.map +1 -1
  54. package/dist/types/components/cms/wordpress.components.d.ts.map +1 -1
  55. package/dist/types/components/general/semantic.d.ts +3 -3
  56. package/dist/types/components/general/tab.d.ts +18 -0
  57. package/dist/types/components/general/tab.d.ts.map +1 -0
  58. package/dist/types/components/menu/menu-accordion.d.ts.map +1 -1
  59. package/dist/types/components/seo/metadata.components.d.ts +0 -17
  60. package/dist/types/components/seo/metadata.components.d.ts.map +1 -1
  61. package/dist/types/components/seo/{metadata.d.ts → metadata.functions.d.ts} +15 -1
  62. package/dist/types/components/seo/metadata.functions.d.ts.map +1 -0
  63. package/dist/types/components/seo/schema-blogposting.d.ts +1 -25
  64. package/dist/types/components/seo/schema-blogposting.d.ts.map +1 -1
  65. package/dist/types/components/seo/schema-blogposting.functions.d.ts +26 -0
  66. package/dist/types/components/seo/schema-blogposting.functions.d.ts.map +1 -0
  67. package/dist/types/components/seo/sitemap.d.ts.map +1 -1
  68. package/dist/types/components/shoppingcart/shoppingcart.components.d.ts +1 -1
  69. package/dist/types/components/sitebuilder/config/CompoundFontSelector.d.ts +23 -0
  70. package/dist/types/components/sitebuilder/config/CompoundFontSelector.d.ts.map +1 -0
  71. package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts +354 -0
  72. package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts.map +1 -0
  73. package/dist/types/components/sitebuilder/config/ConfigEngine.d.ts +10 -0
  74. package/dist/types/components/sitebuilder/config/ConfigEngine.d.ts.map +1 -0
  75. package/dist/types/components/sitebuilder/config/FontSelector.d.ts +27 -0
  76. package/dist/types/components/sitebuilder/config/FontSelector.d.ts.map +1 -0
  77. package/dist/types/components/sitebuilder/config/google-fonts.d.ts +41 -0
  78. package/dist/types/components/sitebuilder/config/google-fonts.d.ts.map +1 -0
  79. package/dist/types/components/sitebuilder/form/formbuilder.d.ts +11 -0
  80. package/dist/types/components/sitebuilder/form/formbuilder.d.ts.map +1 -0
  81. package/dist/types/components/{pagebuilder → sitebuilder}/form/formcomponents.d.ts +15 -17
  82. package/dist/types/components/sitebuilder/form/formcomponents.d.ts.map +1 -0
  83. package/dist/types/components/{pagebuilder/form/form.submit.d.ts → sitebuilder/form/formemailer.d.ts} +1 -1
  84. package/dist/types/components/sitebuilder/form/formemailer.d.ts.map +1 -0
  85. package/dist/types/components/sitebuilder/form/formengine.d.ts +14 -0
  86. package/dist/types/components/sitebuilder/form/formengine.d.ts.map +1 -0
  87. package/dist/types/components/sitebuilder/form/formextractor.d.ts +25 -0
  88. package/dist/types/components/sitebuilder/form/formextractor.d.ts.map +1 -0
  89. package/dist/types/components/{pagebuilder/form/formvalidations.d.ts → sitebuilder/form/formfieldvalidations.d.ts} +1 -1
  90. package/dist/types/components/sitebuilder/form/formfieldvalidations.d.ts.map +1 -0
  91. package/dist/types/components/sitebuilder/form/formtypes.d.ts +66 -0
  92. package/dist/types/components/sitebuilder/form/formtypes.d.ts.map +1 -0
  93. package/dist/types/components/sitebuilder/form/formutils.d.ts +20 -0
  94. package/dist/types/components/sitebuilder/form/formutils.d.ts.map +1 -0
  95. package/dist/types/components/sitebuilder/form/formvalidator.d.ts +20 -0
  96. package/dist/types/components/sitebuilder/form/formvalidator.d.ts.map +1 -0
  97. package/dist/types/components/sitebuilder/page/components/ComponentPropertiesForm.d.ts.map +1 -0
  98. package/dist/types/components/sitebuilder/page/components/ComponentSelector.d.ts.map +1 -0
  99. package/dist/types/components/sitebuilder/page/components/ComponentTree.d.ts.map +1 -0
  100. package/dist/types/components/{pagebuilder → sitebuilder/page}/components/PageBuilderUI.d.ts +1 -1
  101. package/dist/types/components/sitebuilder/page/components/PageBuilderUI.d.ts.map +1 -0
  102. package/dist/types/components/sitebuilder/page/components/PageEngine.d.ts.map +1 -0
  103. package/dist/types/components/sitebuilder/page/components/SaveLoadSection.d.ts.map +1 -0
  104. package/dist/types/components/sitebuilder/page/documentation/api-examples/save-route-example.d.ts +6 -0
  105. package/dist/types/components/sitebuilder/page/documentation/api-examples/save-route-example.d.ts.map +1 -0
  106. package/dist/types/components/sitebuilder/page/lib/componentGeneration.d.ts.map +1 -0
  107. package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/componentMap.d.ts +3 -3
  108. package/dist/types/components/sitebuilder/page/lib/componentMap.d.ts.map +1 -0
  109. package/dist/types/components/sitebuilder/page/lib/componentMetadata.d.ts.map +1 -0
  110. package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/pageStorageContentful.d.ts +1 -1
  111. package/dist/types/components/sitebuilder/page/lib/pageStorageContentful.d.ts.map +1 -0
  112. package/dist/types/components/sitebuilder/page/lib/pageStorageLocal.d.ts.map +1 -0
  113. package/dist/types/components/sitebuilder/page/lib/pageStorageTypes.d.ts.map +1 -0
  114. package/dist/types/components/sitebuilder/page/lib/propTypeIntrospection.d.ts.map +1 -0
  115. package/dist/types/components/sitebuilder/page/lib/types.d.ts.map +1 -0
  116. package/dist/types/components/sitebuilder/page/lib/usePageBuilder.d.ts.map +1 -0
  117. package/dist/types/components/structured/markdown.d.ts.map +1 -1
  118. package/dist/types/components/structured/recipe.d.ts.map +1 -1
  119. package/dist/types/components/structured/timeline.d.ts.map +1 -1
  120. package/dist/types/index.d.ts +33 -20
  121. package/dist/types/index.server.d.ts +23 -16
  122. package/dist/types/stories/general/tab.stories.d.ts +45 -0
  123. package/dist/types/stories/general/tab.stories.d.ts.map +1 -0
  124. package/dist/types/stories/seo/seo.metadata.stories.d.ts +1 -1
  125. package/dist/types/stories/seo/seo.metadata.stories.d.ts.map +1 -1
  126. package/dist/types/stories/sitebuilder/compoundfontselector.stories.d.ts +51 -0
  127. package/dist/types/stories/sitebuilder/compoundfontselector.stories.d.ts.map +1 -0
  128. package/dist/types/stories/sitebuilder/configbuilder.stories.d.ts +103 -0
  129. package/dist/types/stories/sitebuilder/configbuilder.stories.d.ts.map +1 -0
  130. package/dist/types/stories/{pagebuilder → sitebuilder}/form-builder.stories.d.ts +1 -1
  131. package/dist/types/stories/sitebuilder/form-builder.stories.d.ts.map +1 -0
  132. package/dist/types/stories/{pagebuilder → sitebuilder}/form-engine.stories.d.ts +1 -1
  133. package/dist/types/stories/sitebuilder/form-engine.stories.d.ts.map +1 -0
  134. package/dist/types/stories/{pagebuilder → sitebuilder}/form-extractor.stories.d.ts +1 -1
  135. package/dist/types/stories/sitebuilder/form-extractor.stories.d.ts.map +1 -0
  136. package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.stories.d.ts +1 -1
  137. package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.stories.d.ts.map +1 -1
  138. package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.usageguide.stories.d.ts +1 -1
  139. package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.usageguide.stories.d.ts.map +1 -1
  140. package/dist/types/stories/{pagebuilder → sitebuilder}/pageengine.stories.d.ts +1 -1
  141. package/dist/types/stories/{pagebuilder → sitebuilder}/pageengine.stories.d.ts.map +1 -1
  142. package/dist/types/tests/compoundfontselector.test.d.ts +2 -0
  143. package/dist/types/tests/compoundfontselector.test.d.ts.map +1 -0
  144. package/dist/types/tests/configbuilder.test.d.ts +2 -0
  145. package/dist/types/tests/configbuilder.test.d.ts.map +1 -0
  146. package/dist/types/tests/configengine.test.d.ts +2 -0
  147. package/dist/types/tests/configengine.test.d.ts.map +1 -0
  148. package/dist/types/tests/fontselector.test.d.ts +2 -0
  149. package/dist/types/tests/fontselector.test.d.ts.map +1 -0
  150. package/dist/types/tests/tab.test.d.ts +2 -0
  151. package/dist/types/tests/tab.test.d.ts.map +1 -0
  152. package/package.json +15 -12
  153. package/dist/components/pagebuilder/form/formcomponents.js +0 -359
  154. package/dist/components/seo/metadata.js +0 -108
  155. package/dist/types/components/pagebuilder/components/ComponentPropertiesForm.d.ts.map +0 -1
  156. package/dist/types/components/pagebuilder/components/ComponentSelector.d.ts.map +0 -1
  157. package/dist/types/components/pagebuilder/components/ComponentTree.d.ts.map +0 -1
  158. package/dist/types/components/pagebuilder/components/PageBuilderUI.d.ts.map +0 -1
  159. package/dist/types/components/pagebuilder/components/PageEngine.d.ts.map +0 -1
  160. package/dist/types/components/pagebuilder/components/SaveLoadSection.d.ts.map +0 -1
  161. package/dist/types/components/pagebuilder/form/form.d.ts +0 -46
  162. package/dist/types/components/pagebuilder/form/form.d.ts.map +0 -1
  163. package/dist/types/components/pagebuilder/form/form.submit.d.ts.map +0 -1
  164. package/dist/types/components/pagebuilder/form/formcomponents.d.ts.map +0 -1
  165. package/dist/types/components/pagebuilder/form/formvalidations.d.ts.map +0 -1
  166. package/dist/types/components/pagebuilder/lib/componentGeneration.d.ts.map +0 -1
  167. package/dist/types/components/pagebuilder/lib/componentMap.d.ts.map +0 -1
  168. package/dist/types/components/pagebuilder/lib/componentMetadata.d.ts.map +0 -1
  169. package/dist/types/components/pagebuilder/lib/pageStorageContentful.d.ts.map +0 -1
  170. package/dist/types/components/pagebuilder/lib/pageStorageLocal.d.ts.map +0 -1
  171. package/dist/types/components/pagebuilder/lib/pageStorageTypes.d.ts.map +0 -1
  172. package/dist/types/components/pagebuilder/lib/propTypeIntrospection.d.ts.map +0 -1
  173. package/dist/types/components/pagebuilder/lib/types.d.ts.map +0 -1
  174. package/dist/types/components/pagebuilder/lib/usePageBuilder.d.ts.map +0 -1
  175. package/dist/types/components/seo/metadata.d.ts.map +0 -1
  176. package/dist/types/stories/pagebuilder/form-builder.stories.d.ts.map +0 -1
  177. package/dist/types/stories/pagebuilder/form-engine.stories.d.ts.map +0 -1
  178. package/dist/types/stories/pagebuilder/form-extractor.stories.d.ts.map +0 -1
  179. /package/dist/components/{pagebuilder/form/form.submit.js → sitebuilder/form/formemailer.js} +0 -0
  180. /package/dist/components/{pagebuilder/form/formvalidations.js → sitebuilder/form/formfieldvalidations.js} +0 -0
  181. /package/dist/components/{pagebuilder/lib/pageStorageTypes.js → sitebuilder/form/formtypes.js} +0 -0
  182. /package/dist/components/{pagebuilder → sitebuilder/page}/components/ComponentSelector.js +0 -0
  183. /package/dist/components/{pagebuilder → sitebuilder/page}/components/ComponentTree.js +0 -0
  184. /package/dist/components/{pagebuilder → sitebuilder/page}/components/SaveLoadSection.js +0 -0
  185. /package/dist/components/{pagebuilder → sitebuilder/page}/components/pagebuilder.scss +0 -0
  186. /package/dist/components/{pagebuilder → sitebuilder/page}/lib/componentGeneration.js +0 -0
  187. /package/dist/components/{pagebuilder → sitebuilder/page}/lib/pageStorageLocal.js +0 -0
  188. /package/dist/components/{pagebuilder → sitebuilder/page}/lib/propTypeIntrospection.js +0 -0
  189. /package/dist/components/{pagebuilder → sitebuilder/page}/lib/types.js +0 -0
  190. /package/dist/components/{pagebuilder → sitebuilder/page}/lib/usePageBuilder.js +0 -0
  191. /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/ComponentPropertiesForm.d.ts +0 -0
  192. /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/ComponentSelector.d.ts +0 -0
  193. /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/ComponentTree.d.ts +0 -0
  194. /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/PageEngine.d.ts +0 -0
  195. /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/SaveLoadSection.d.ts +0 -0
  196. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/componentGeneration.d.ts +0 -0
  197. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/componentMetadata.d.ts +0 -0
  198. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/pageStorageLocal.d.ts +0 -0
  199. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/pageStorageTypes.d.ts +0 -0
  200. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/propTypeIntrospection.d.ts +0 -0
  201. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/types.d.ts +0 -0
  202. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/usePageBuilder.d.ts +0 -0
@@ -0,0 +1,380 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from 'react';
4
+ import PropTypes from 'prop-types';
5
+ import { Tab } from '../../general/tab';
6
+ import { Accordion } from '../../general/accordion';
7
+ import { FormEngine } from '../form/formengine';
8
+ import siteInfoForm from '../../../data/siteinfo-form.json';
9
+ import visualDesignForm from '../../../data/visualdesignform.json';
10
+ import defaultConfigData from '../../../data/routes.json';
11
+ import './ConfigBuilder.css';
12
+ const SiteInfoPropTypes = {
13
+ name: PropTypes.string.isRequired,
14
+ author: PropTypes.string.isRequired,
15
+ description: PropTypes.string.isRequired,
16
+ url: PropTypes.string.isRequired,
17
+ email: PropTypes.string.isRequired,
18
+ favicon: PropTypes.string.isRequired,
19
+ favicon_sizes: PropTypes.string.isRequired,
20
+ favicon_type: PropTypes.string.isRequired,
21
+ theme_color: PropTypes.string.isRequired,
22
+ background_color: PropTypes.string.isRequired,
23
+ default_locale: PropTypes.string.isRequired,
24
+ display: PropTypes.string.isRequired,
25
+ image: PropTypes.string,
26
+ image_height: PropTypes.string,
27
+ image_width: PropTypes.string,
28
+ telephone: PropTypes.string,
29
+ address: PropTypes.shape({
30
+ streetAddress: PropTypes.string.isRequired,
31
+ addressLocality: PropTypes.string.isRequired,
32
+ addressRegion: PropTypes.string.isRequired,
33
+ postalCode: PropTypes.string.isRequired,
34
+ addressCountry: PropTypes.string.isRequired,
35
+ }),
36
+ priceRange: PropTypes.string,
37
+ sameAs: PropTypes.arrayOf(PropTypes.string.isRequired),
38
+ keywords: PropTypes.string,
39
+ };
40
+ const RoutePropTypes = {
41
+ path: PropTypes.string.isRequired,
42
+ component: PropTypes.string.isRequired,
43
+ title: PropTypes.string,
44
+ description: PropTypes.string,
45
+ };
46
+ const VisualDesignPropTypes = {
47
+ 'primary-color': PropTypes.shape({
48
+ value: PropTypes.string.isRequired,
49
+ type: PropTypes.string.isRequired,
50
+ group: PropTypes.string.isRequired,
51
+ label: PropTypes.string.isRequired
52
+ }).isRequired,
53
+ 'secondary-color': PropTypes.shape({
54
+ value: PropTypes.string.isRequired,
55
+ type: PropTypes.string.isRequired,
56
+ group: PropTypes.string.isRequired,
57
+ label: PropTypes.string.isRequired
58
+ }).isRequired,
59
+ 'accent1-color': PropTypes.shape({
60
+ value: PropTypes.string.isRequired,
61
+ type: PropTypes.string.isRequired,
62
+ group: PropTypes.string.isRequired,
63
+ label: PropTypes.string.isRequired
64
+ }).isRequired,
65
+ 'accent2-color': PropTypes.shape({
66
+ value: PropTypes.string.isRequired,
67
+ type: PropTypes.string.isRequired,
68
+ group: PropTypes.string.isRequired,
69
+ label: PropTypes.string.isRequired
70
+ }).isRequired,
71
+ 'bg-color': PropTypes.shape({
72
+ value: PropTypes.string.isRequired,
73
+ type: PropTypes.string.isRequired,
74
+ group: PropTypes.string.isRequired,
75
+ label: PropTypes.string.isRequired
76
+ }).isRequired,
77
+ 'text-color': PropTypes.shape({
78
+ value: PropTypes.string.isRequired,
79
+ type: PropTypes.string.isRequired,
80
+ group: PropTypes.string.isRequired,
81
+ label: PropTypes.string.isRequired
82
+ }).isRequired,
83
+ 'header-font': PropTypes.shape({
84
+ value: PropTypes.string.isRequired,
85
+ type: PropTypes.string.isRequired,
86
+ group: PropTypes.string.isRequired,
87
+ label: PropTypes.string.isRequired
88
+ }).isRequired,
89
+ 'body-font': PropTypes.shape({
90
+ value: PropTypes.string.isRequired,
91
+ type: PropTypes.string.isRequired,
92
+ group: PropTypes.string.isRequired,
93
+ label: PropTypes.string.isRequired
94
+ }).isRequired,
95
+ 'font-size1-min': PropTypes.shape({
96
+ value: PropTypes.string.isRequired,
97
+ type: PropTypes.string.isRequired,
98
+ group: PropTypes.string.isRequired,
99
+ label: PropTypes.string.isRequired
100
+ }).isRequired,
101
+ 'font-size1-max': PropTypes.shape({
102
+ value: PropTypes.string.isRequired,
103
+ type: PropTypes.string.isRequired,
104
+ group: PropTypes.string.isRequired,
105
+ label: PropTypes.string.isRequired
106
+ }).isRequired,
107
+ 'font-size2-min': PropTypes.shape({
108
+ value: PropTypes.string.isRequired,
109
+ type: PropTypes.string.isRequired,
110
+ group: PropTypes.string.isRequired,
111
+ label: PropTypes.string.isRequired
112
+ }).isRequired,
113
+ 'font-size2-max': PropTypes.shape({
114
+ value: PropTypes.string.isRequired,
115
+ type: PropTypes.string.isRequired,
116
+ group: PropTypes.string.isRequired,
117
+ label: PropTypes.string.isRequired
118
+ }).isRequired,
119
+ 'font-size3-min': PropTypes.shape({
120
+ value: PropTypes.string.isRequired,
121
+ type: PropTypes.string.isRequired,
122
+ group: PropTypes.string.isRequired,
123
+ label: PropTypes.string.isRequired
124
+ }).isRequired,
125
+ 'font-size3-max': PropTypes.shape({
126
+ value: PropTypes.string.isRequired,
127
+ type: PropTypes.string.isRequired,
128
+ group: PropTypes.string.isRequired,
129
+ label: PropTypes.string.isRequired
130
+ }).isRequired,
131
+ 'font-size4-min': PropTypes.shape({
132
+ value: PropTypes.string.isRequired,
133
+ type: PropTypes.string.isRequired,
134
+ group: PropTypes.string.isRequired,
135
+ label: PropTypes.string.isRequired
136
+ }).isRequired,
137
+ 'font-size4-max': PropTypes.shape({
138
+ value: PropTypes.string.isRequired,
139
+ type: PropTypes.string.isRequired,
140
+ group: PropTypes.string.isRequired,
141
+ label: PropTypes.string.isRequired
142
+ }).isRequired,
143
+ 'font-size5-min': PropTypes.shape({
144
+ value: PropTypes.string.isRequired,
145
+ type: PropTypes.string.isRequired,
146
+ group: PropTypes.string.isRequired,
147
+ label: PropTypes.string.isRequired
148
+ }).isRequired,
149
+ 'font-size5-max': PropTypes.shape({
150
+ value: PropTypes.string.isRequired,
151
+ type: PropTypes.string.isRequired,
152
+ group: PropTypes.string.isRequired,
153
+ label: PropTypes.string.isRequired
154
+ }).isRequired,
155
+ 'font-size6-min': PropTypes.shape({
156
+ value: PropTypes.string.isRequired,
157
+ type: PropTypes.string.isRequired,
158
+ group: PropTypes.string.isRequired,
159
+ label: PropTypes.string.isRequired
160
+ }).isRequired,
161
+ 'font-size6-max': PropTypes.shape({
162
+ value: PropTypes.string.isRequired,
163
+ type: PropTypes.string.isRequired,
164
+ group: PropTypes.string.isRequired,
165
+ label: PropTypes.string.isRequired
166
+ }).isRequired,
167
+ 'font-min-screen': PropTypes.shape({
168
+ value: PropTypes.string.isRequired,
169
+ type: PropTypes.string.isRequired,
170
+ group: PropTypes.string.isRequired,
171
+ label: PropTypes.string.isRequired
172
+ }).isRequired,
173
+ 'font-max-screen': PropTypes.shape({
174
+ value: PropTypes.string.isRequired,
175
+ type: PropTypes.string.isRequired,
176
+ group: PropTypes.string.isRequired,
177
+ label: PropTypes.string.isRequired
178
+ }).isRequired,
179
+ };
180
+ const SiteConfigPropTypes = {
181
+ siteInfo: PropTypes.shape(SiteInfoPropTypes).isRequired,
182
+ routes: PropTypes.arrayOf(PropTypes.shape(RoutePropTypes).isRequired).isRequired,
183
+ visualdesign: PropTypes.shape(VisualDesignPropTypes),
184
+ };
185
+ const ConfigBuilderPropTypes = {
186
+ initialConfig: PropTypes.shape(SiteConfigPropTypes),
187
+ onSave: PropTypes.func,
188
+ };
189
+ export function ConfigBuilder(props) {
190
+ const { initialConfig, onSave } = props;
191
+ const defaultConfig = {
192
+ siteInfo: defaultConfigData.siteInfo,
193
+ routes: [], // Start with empty routes, the JSON structure is different
194
+ visualdesign: defaultConfigData.visualdesign
195
+ };
196
+ const [config, setConfig] = useState({
197
+ ...defaultConfig,
198
+ ...initialConfig,
199
+ siteInfo: { ...defaultConfig.siteInfo, ...initialConfig?.siteInfo },
200
+ routes: initialConfig?.routes || [],
201
+ visualdesign: { ...defaultConfig.visualdesign, ...initialConfig?.visualdesign }
202
+ });
203
+ const [socialLinks, setSocialLinks] = useState(initialConfig?.siteInfo?.sameAs || ['']);
204
+ const [isFormValid, setIsFormValid] = useState(false);
205
+ // Validate form whenever config changes
206
+ useEffect(() => {
207
+ const siteInfo = config.siteInfo || {};
208
+ const isValid = (siteInfo.name || '').trim() !== '' &&
209
+ (siteInfo.author || '').trim() !== '' &&
210
+ (siteInfo.description || '').trim() !== '' &&
211
+ (siteInfo.url || '').trim() !== '' &&
212
+ (siteInfo.email || '').trim() !== '' &&
213
+ // Basic email validation
214
+ /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(siteInfo.email || '');
215
+ setIsFormValid(isValid);
216
+ }, [config]);
217
+ const handleFileUpload = (event) => {
218
+ const file = event.target.files?.[0];
219
+ if (!file)
220
+ return;
221
+ const reader = new FileReader();
222
+ reader.onload = (e) => {
223
+ try {
224
+ const jsonContent = e.target?.result;
225
+ const parsedConfig = JSON.parse(jsonContent);
226
+ // Validate the structure
227
+ if (parsedConfig.siteInfo && parsedConfig.routes) {
228
+ setConfig(parsedConfig);
229
+ setSocialLinks(parsedConfig.siteInfo.sameAs || ['']);
230
+ }
231
+ else {
232
+ alert('Invalid configuration file. Expected siteInfo and routes properties.');
233
+ }
234
+ }
235
+ catch {
236
+ alert('Error parsing JSON file. Please ensure it contains valid JSON.');
237
+ }
238
+ };
239
+ reader.readAsText(file);
240
+ // Reset the input
241
+ event.target.value = '';
242
+ };
243
+ useEffect(() => {
244
+ if (initialConfig) {
245
+ setConfig((prev) => ({
246
+ siteInfo: { ...prev.siteInfo, ...initialConfig.siteInfo },
247
+ routes: initialConfig.routes || [],
248
+ visualdesign: initialConfig.visualdesign || prev.visualdesign || {}
249
+ }));
250
+ setSocialLinks(initialConfig.siteInfo?.sameAs || ['']);
251
+ }
252
+ }, [initialConfig]);
253
+ // Prepare form data for FormEngine with current values
254
+ const formData = {
255
+ fields: siteInfoForm.fields.map(field => ({
256
+ ...field,
257
+ props: {
258
+ ...field.props,
259
+ value: config.siteInfo[field.props.name] || '',
260
+ defaultValue: config.siteInfo[field.props.name] || field.props.defaultValue || '',
261
+ onChange: (value) => {
262
+ setConfig((prev) => ({
263
+ ...prev,
264
+ siteInfo: {
265
+ ...prev.siteInfo,
266
+ [field.props.name]: value
267
+ }
268
+ }));
269
+ }
270
+ }
271
+ }))
272
+ };
273
+ // Visual Design form data
274
+ const visualFormData = {
275
+ fields: (visualDesignForm.fields || []).map((field) => ({
276
+ ...field,
277
+ props: {
278
+ ...field.props,
279
+ value: (config.visualdesign && config.visualdesign[field.props.name]) ? (config.visualdesign[field.props.name].value ?? config.visualdesign[field.props.name]) : '',
280
+ defaultValue: (config.visualdesign && config.visualdesign[field.props.name]) ? (config.visualdesign[field.props.name].value ?? config.visualdesign[field.props.name]) : field.props.defaultValue || '',
281
+ onChange: (value) => {
282
+ setConfig((prev) => ({
283
+ ...prev,
284
+ visualdesign: {
285
+ ...(prev.visualdesign || {}),
286
+ [field.props.name]: { value }
287
+ }
288
+ }));
289
+ }
290
+ }
291
+ }))
292
+ };
293
+ const handleFormSubmit = (event) => {
294
+ event.preventDefault();
295
+ const formData = new FormData(event.target);
296
+ const siteInfoData = {};
297
+ // Extract form data
298
+ for (const [key, value] of formData.entries()) {
299
+ siteInfoData[key] = value;
300
+ }
301
+ // Update config with form data
302
+ setConfig(prev => ({
303
+ ...prev,
304
+ siteInfo: {
305
+ ...prev.siteInfo,
306
+ ...siteInfoData,
307
+ sameAs: socialLinks.filter(link => link.trim() !== '')
308
+ }
309
+ }));
310
+ };
311
+ const updateAddress = (field, value) => {
312
+ setConfig(prev => ({
313
+ ...prev,
314
+ siteInfo: {
315
+ ...prev.siteInfo,
316
+ address: {
317
+ ...(prev.siteInfo.address || {}),
318
+ [field]: value
319
+ }
320
+ }
321
+ }));
322
+ };
323
+ const addSocialLink = () => {
324
+ setSocialLinks(prev => [...prev, '']);
325
+ };
326
+ const updateSocialLink = (index, value) => {
327
+ setSocialLinks(prev => prev.map((link, i) => i === index ? value : link));
328
+ };
329
+ const removeSocialLink = (index) => {
330
+ setSocialLinks(prev => prev.filter((_, i) => i !== index));
331
+ };
332
+ const addRoute = () => {
333
+ setConfig(prev => ({
334
+ ...prev,
335
+ routes: [...prev.routes, { path: '', component: '', title: '', description: '' }]
336
+ }));
337
+ };
338
+ const updateRoute = (index, field, value) => {
339
+ setConfig(prev => ({
340
+ ...prev,
341
+ routes: prev.routes.map((route, i) => i === index ? { ...route, [field]: value } : route)
342
+ }));
343
+ };
344
+ const removeRoute = (index) => {
345
+ setConfig(prev => ({
346
+ ...prev,
347
+ routes: prev.routes.filter((_, i) => i !== index)
348
+ }));
349
+ };
350
+ const handleSave = () => {
351
+ if (!isFormValid) {
352
+ alert('Please fill in all required fields correctly before saving.');
353
+ return;
354
+ }
355
+ onSave?.(config);
356
+ };
357
+ return (_jsxs("div", { className: "config-builder", children: [_jsx("h2", { children: "Config Builder" }), _jsxs("div", { className: "file-upload-section", children: [_jsx("label", { htmlFor: "config-file-upload", className: "file-upload-label", children: "Load Configuration File:" }), _jsx("input", { id: "config-file-upload", type: "file", accept: ".json", onChange: handleFileUpload, className: "file-upload-input" })] }), _jsx(Tab, { tabs: [
358
+ {
359
+ id: 'siteinfo',
360
+ label: 'Site Info',
361
+ content: (_jsxs("div", { className: "site-info-section", children: [_jsx(FormEngine, { formData: formData, onSubmitHandler: handleFormSubmit, name: "siteinfo", id: "siteinfo" }), _jsxs("div", { className: "address-section", children: [_jsx("h4", { children: "Address" }), _jsxs("div", { className: "field-group", children: [_jsx("label", { htmlFor: "street-address", children: "Street Address" }), _jsx("input", { id: "street-address", type: "text", placeholder: "Street Address", value: config.siteInfo.address?.streetAddress || '', onChange: (e) => updateAddress('streetAddress', e.target.value) })] }), _jsxs("div", { className: "field-group", children: [_jsx("label", { htmlFor: "city", children: "City" }), _jsx("input", { id: "city", type: "text", placeholder: "City", value: config.siteInfo.address?.addressLocality || '', onChange: (e) => updateAddress('addressLocality', e.target.value) })] }), _jsxs("div", { className: "field-group", children: [_jsx("label", { htmlFor: "state-region", children: "State/Region" }), _jsx("input", { id: "state-region", type: "text", placeholder: "State/Region", value: config.siteInfo.address?.addressRegion || '', onChange: (e) => updateAddress('addressRegion', e.target.value) })] }), _jsxs("div", { className: "field-group", children: [_jsx("label", { htmlFor: "postal-code", children: "Postal Code" }), _jsx("input", { id: "postal-code", type: "text", placeholder: "Postal Code", value: config.siteInfo.address?.postalCode || '', onChange: (e) => updateAddress('postalCode', e.target.value) })] }), _jsxs("div", { className: "field-group", children: [_jsx("label", { htmlFor: "country", children: "Country" }), _jsx("input", { id: "country", type: "text", placeholder: "Country", value: config.siteInfo.address?.addressCountry || '', onChange: (e) => updateAddress('addressCountry', e.target.value) })] })] }), _jsxs("div", { className: "social-links-section", children: [_jsx("h4", { children: "Social Links" }), socialLinks.map((link, index) => (_jsxs("div", { className: "field-group social-link-item", children: [_jsx("input", { type: "url", placeholder: "https://social-link.com", value: link, onChange: (e) => updateSocialLink(index, e.target.value) }), _jsx("button", { type: "button", onClick: () => removeSocialLink(index), children: "Remove" })] }, index))), _jsx("button", { type: "button", onClick: addSocialLink, children: "Add Social Link" })] })] }))
362
+ },
363
+ {
364
+ id: 'routes',
365
+ label: 'Routes',
366
+ content: (_jsxs("div", { className: "routes-section", children: [_jsxs("div", { className: "routes-list", children: [_jsxs("div", { className: "route-headers", children: [_jsx("span", { children: "Path" }), _jsx("span", { children: "Component" }), _jsx("span", { children: "Title" }), _jsx("span", { children: "Description" }), _jsx("span", { children: "Actions" })] }), config.routes.map((route, index) => (_jsxs("div", { className: "route-item", children: [_jsx("input", { type: "text", placeholder: "Path", value: route.path, onChange: (e) => updateRoute(index, 'path', e.target.value) }), _jsx("input", { type: "text", placeholder: "Component", value: route.component, onChange: (e) => updateRoute(index, 'component', e.target.value) }), _jsx("input", { type: "text", placeholder: "Title", value: route.title || '', onChange: (e) => updateRoute(index, 'title', e.target.value) }), _jsx("input", { type: "text", placeholder: "Description", value: route.description || '', onChange: (e) => updateRoute(index, 'description', e.target.value) }), _jsx("button", { onClick: () => removeRoute(index), children: "Remove" })] }, index)))] }), _jsx("button", { onClick: addRoute, children: "Add Route" })] }))
367
+ },
368
+ {
369
+ id: 'visualdesign',
370
+ label: 'Visual Design',
371
+ content: (_jsx("div", { className: "visual-design-section", children: _jsx(FormEngine, { formData: visualFormData, name: "visualdesign", id: "visualdesign" }) }))
372
+ }
373
+ ], orientation: "top" }), _jsx("button", { onClick: handleSave, disabled: !isFormValid, className: isFormValid ? 'save-button-valid' : 'save-button-invalid', children: "Save Config" }), !isFormValid && (_jsx("div", { className: "validation-message", children: "Please fill in all required fields (marked with *) before saving." })), _jsx(Accordion, { items: [
374
+ {
375
+ title: 'Configuration Preview',
376
+ content: _jsx("pre", { children: JSON.stringify(config, null, 2) })
377
+ }
378
+ ] })] }));
379
+ }
380
+ ConfigBuilder.propTypes = ConfigBuilderPropTypes;
@@ -0,0 +1,82 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { generateGoogleFontsUrl } from './google-fonts';
3
+ export function VisualDesignStyles({ visualdesign }) {
4
+ const tokens = visualdesign || {};
5
+ const resolveValue = (v) => (v && typeof v === 'object' && 'value' in v) ? v.value : v;
6
+ const varLines = [];
7
+ // Always include base font sizing first (from pixelated.visualdesign.scss)
8
+ varLines.push('--font-size1-min: 2.00rem;', '--font-size1-max: 3.00rem;', '--font-size2-min: 1.35rem;', '--font-size2-max: 1.75rem;', '--font-size3-min: 1.17rem;', '--font-size3-max: 1.35rem;', '--font-size4-min: 1.00rem;', '--font-size4-max: 1.25rem;', '--font-size5-min: 0.83rem;', '--font-size5-max: 1.00rem;', '--font-size6-min: 0.67rem;', '--font-size6-max: 0.85rem;', '--font-min-screen: 375px;', '--font-max-screen: 1440px;');
9
+ for (const [key, val] of Object.entries(tokens)) {
10
+ const value = resolveValue(val);
11
+ if (value === undefined || value === null)
12
+ continue;
13
+ // Handle font family construction from 3-field structure (legacy)
14
+ if (key.endsWith('-primary')) {
15
+ const baseKey = key.replace('-primary', '');
16
+ const primary = value;
17
+ const fallback = resolveValue(tokens[`${baseKey}-fallback`]);
18
+ const generic = resolveValue(tokens[`${baseKey}-generic`]);
19
+ // Build font stack: "Primary Font", Fallback, generic
20
+ const fontStack = [primary, fallback, generic].filter(Boolean).map(f => `"${f}"`).join(', ');
21
+ varLines.push(`--${baseKey}-family: ${fontStack};`);
22
+ }
23
+ else if (key === 'header-font' || key === 'body-font') {
24
+ // Handle compound font values (new format)
25
+ varLines.push(`--${key}: ${value};`);
26
+ }
27
+ else if (!key.endsWith('-fallback') && !key.endsWith('-generic')) {
28
+ // Skip fallback and generic fields as they're handled above
29
+ varLines.push(`--${key}: ${value};`);
30
+ }
31
+ }
32
+ const fontLines = [];
33
+ // Generate clamp calculations for font sizes
34
+ for (let i = 1; i <= 6; i++) {
35
+ varLines.push(`--font-size${i}: clamp(var(--font-size${i}-min), calc(var(--font-size${i}-min) + ((var(--font-size${i}-max) - var(--font-size${i}-min)) * ((100vw - var(--font-min-screen)) / (var(--font-max-screen) - var(--font-min-screen))))), var(--font-size${i}-max));`);
36
+ }
37
+ // Generate h1-h6 font-size rules
38
+ for (let i = 1; i <= 6; i++) {
39
+ fontLines.push(`h${i} { font-size: var(--font-size${i}); }`);
40
+ }
41
+ const css = [
42
+ ':root {',
43
+ ...varLines.map(l => ` ${l}`),
44
+ '}',
45
+ '',
46
+ '/* Base visual design styles */',
47
+ 'body {',
48
+ ' background-color: var(--bg-color) !important;',
49
+ ' color: var(--text-color);',
50
+ ' font-family: var(--body-font);',
51
+ '}',
52
+ 'h1, h2, h3, h4, h5, h6 {',
53
+ ' line-height: 1.1;',
54
+ ' font-family: var(--header-font);',
55
+ '}',
56
+ 'html { font-size: 1.0rem; /* 16px; */ }',
57
+ '',
58
+ ...fontLines
59
+ ].join('\n');
60
+ return _jsx("style", { dangerouslySetInnerHTML: { __html: css } });
61
+ }
62
+ /**
63
+ * Component to handle Google Fonts imports - should be used in the document head
64
+ */
65
+ export function GoogleFontsImports({ visualdesign }) {
66
+ const tokens = visualdesign || {};
67
+ const fonts = [];
68
+ // Extract Google font names from the new 3-field font structure
69
+ for (const [key, val] of Object.entries(tokens)) {
70
+ if (key.endsWith('-primary') && typeof val === 'string' && val.trim()) {
71
+ // Only include fonts that are not web-safe (web-safe fonts don't need Google Fonts import)
72
+ const webSafeFonts = ['Arial', 'Helvetica', 'Times New Roman', 'Times', 'Courier New', 'Courier', 'Georgia', 'Palatino', 'Garamond', 'Bookman', 'Comic Sans MS', 'Trebuchet MS', 'Arial Black', 'Impact', 'Lucida Sans Unicode', 'Lucida Grande', 'MS Sans Serif', 'MS Serif', 'New York', 'System', 'Apple System', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue'];
73
+ if (!webSafeFonts.includes(val.trim())) {
74
+ fonts.push(val.trim());
75
+ }
76
+ }
77
+ }
78
+ const googleFontsUrl = generateGoogleFontsUrl(fonts);
79
+ if (!googleFontsUrl)
80
+ return null;
81
+ return (_jsxs(_Fragment, { children: [_jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }), _jsx("link", { href: googleFontsUrl, rel: "stylesheet" })] }));
82
+ }
@@ -0,0 +1,82 @@
1
+ .font-selector-container {
2
+ margin-bottom: 1rem;
3
+ }
4
+
5
+ .font-selector-label {
6
+ display: block;
7
+ margin-bottom: 0.5rem;
8
+ font-weight: 500;
9
+ }
10
+
11
+ .font-selector-required {
12
+ color: #e53e3e;
13
+ margin-left: 0.25rem;
14
+ }
15
+
16
+ .font-selector-tooltip {
17
+ margin-left: 0.5rem;
18
+ cursor: help;
19
+ opacity: 0.7;
20
+ }
21
+
22
+ .font-selector-input-container {
23
+ position: relative;
24
+ }
25
+
26
+ .font-selector-input {
27
+ width: 100%;
28
+ padding: 0.5rem;
29
+ border: 1px solid #d1d5db;
30
+ border-radius: 0.375rem;
31
+ font-size: 0.875rem;
32
+ }
33
+
34
+ .font-selector-input:focus {
35
+ outline: none;
36
+ border-color: #3b82f6;
37
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
38
+ }
39
+
40
+ .font-selector-dropdown {
41
+ position: absolute;
42
+ top: 100%;
43
+ left: 0;
44
+ right: 0;
45
+ background: white;
46
+ border: 1px solid #d1d5db;
47
+ border-radius: 0.375rem;
48
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
49
+ max-height: 200px;
50
+ overflow-y: auto;
51
+ z-index: 1000;
52
+ }
53
+
54
+ .font-selector-option {
55
+ padding: 0.5rem;
56
+ cursor: pointer;
57
+ border-bottom: 1px solid #f3f4f6;
58
+ }
59
+
60
+ .font-selector-option:hover {
61
+ background-color: #f9fafb;
62
+ }
63
+
64
+ .font-selector-option:last-child {
65
+ border-bottom: none;
66
+ }
67
+
68
+ .font-selector-loading {
69
+ padding: 0.5rem;
70
+ color: #6b7280;
71
+ font-style: italic;
72
+ }
73
+
74
+ .font-selector-font-name {
75
+ font-weight: 500;
76
+ }
77
+
78
+ .font-selector-font-category {
79
+ color: #6b7280;
80
+ font-size: 0.875rem;
81
+ margin-left: 0.5rem;
82
+ }
@@ -0,0 +1,115 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo } from 'react';
3
+ import PropTypes from 'prop-types';
4
+ import { getFontOptions } from './google-fonts';
5
+ import './FontSelector.css';
6
+ const WEBSafe_FONTS = [
7
+ { value: 'Arial', label: 'Arial' },
8
+ { value: 'Bookman', label: 'Bookman' },
9
+ { value: 'Comic Sans MS', label: 'Comic Sans MS' },
10
+ { value: 'Courier New', label: 'Courier New' },
11
+ { value: 'Garamond', label: 'Garamond' },
12
+ { value: 'Geneva', label: 'Geneva' },
13
+ { value: 'Georgia', label: 'Georgia' },
14
+ { value: 'Helvetica', label: 'Helvetica' },
15
+ { value: 'Impact', label: 'Impact' },
16
+ { value: 'Lucida Console', label: 'Lucida Console' },
17
+ { value: 'Monaco', label: 'Monaco' },
18
+ { value: 'Palatino', label: 'Palatino' },
19
+ { value: 'Tahoma', label: 'Tahoma' },
20
+ { value: 'Times New Roman', label: 'Times New Roman' },
21
+ { value: 'Trebuchet MS', label: 'Trebuchet MS' },
22
+ { value: 'Verdana', label: 'Verdana' },
23
+ ];
24
+ const GENERIC_FAMILIES = [
25
+ { value: 'cursive', label: 'cursive' },
26
+ { value: 'fantasy', label: 'fantasy' },
27
+ { value: 'monospace', label: 'monospace' },
28
+ { value: 'sans-serif', label: 'sans-serif' },
29
+ { value: 'serif', label: 'serif' },
30
+ { value: 'system-ui', label: 'system-ui' },
31
+ { value: 'ui-monospace', label: 'ui-monospace' },
32
+ { value: 'ui-rounded', label: 'ui-rounded' },
33
+ { value: 'ui-sans-serif', label: 'ui-sans-serif' },
34
+ { value: 'ui-serif', label: 'ui-serif' },
35
+ ];
36
+ export function FontSelector({ id, name, label, fontType, required = false, placeholder, value = '', onChange }) {
37
+ const [inputValue, setInputValue] = useState(value);
38
+ const [googleFonts, setGoogleFonts] = useState([]);
39
+ const [isLoading, setIsLoading] = useState(false);
40
+ const [showDropdown, setShowDropdown] = useState(false);
41
+ // Load Google Fonts for autocomplete
42
+ useEffect(() => {
43
+ if (fontType === 'google') {
44
+ setIsLoading(true);
45
+ getFontOptions().then(options => {
46
+ setGoogleFonts(options);
47
+ setIsLoading(false);
48
+ }).catch(() => {
49
+ setIsLoading(false);
50
+ });
51
+ }
52
+ }, [fontType]);
53
+ // Get options based on font type
54
+ const options = useMemo(() => {
55
+ switch (fontType) {
56
+ case 'google':
57
+ return googleFonts;
58
+ case 'websafe':
59
+ return WEBSafe_FONTS;
60
+ case 'generic':
61
+ return GENERIC_FAMILIES;
62
+ default:
63
+ return [];
64
+ }
65
+ }, [fontType, googleFonts]);
66
+ // Filter options based on input
67
+ const filteredOptions = useMemo(() => {
68
+ if (!inputValue)
69
+ return options.slice(0, 10); // Show first 10 when no input
70
+ return options.filter(option => option.label.toLowerCase().includes(inputValue.toLowerCase()) ||
71
+ option.value.toLowerCase().includes(inputValue.toLowerCase())).slice(0, 10); // Limit to 10 results
72
+ }, [options, inputValue]);
73
+ const handleInputChange = (e) => {
74
+ const newValue = e.target.value;
75
+ setInputValue(newValue);
76
+ setShowDropdown(true);
77
+ onChange?.(newValue);
78
+ };
79
+ const handleOptionSelect = (option) => {
80
+ setInputValue(option.value);
81
+ setShowDropdown(false);
82
+ onChange?.(option.value);
83
+ };
84
+ const handleFocus = () => {
85
+ setShowDropdown(true);
86
+ };
87
+ const handleBlur = () => {
88
+ // Delay hiding dropdown to allow clicks on options
89
+ setTimeout(() => setShowDropdown(false), 200);
90
+ };
91
+ // Generate tooltip with Google Fonts link for Google fonts
92
+ const getTooltip = () => {
93
+ if (fontType === 'google' && inputValue) {
94
+ const fontName = inputValue.replace(/\s+/g, '+');
95
+ return `👁️ [Preview ${inputValue} on Google Fonts](https://fonts.google.com/specimen/${fontName})`;
96
+ }
97
+ return null;
98
+ };
99
+ return (_jsxs("div", { className: "font-selector-container", children: [_jsxs("label", { htmlFor: id, className: "font-selector-label", children: [label, required && _jsx("span", { className: "font-selector-required", children: "*" }), getTooltip() && (_jsx("span", { className: "font-selector-tooltip", title: getTooltip().replace(/\[([^\]]+)\]\([^)]+\)/, '$1'), children: "\uD83D\uDC41\uFE0F" }))] }), _jsxs("div", { className: "font-selector-input-container", children: [_jsx("input", { type: "text", id: id, name: name, value: inputValue, onChange: handleInputChange, onFocus: handleFocus, onBlur: handleBlur, placeholder: placeholder, required: required, autoComplete: "off", className: "font-selector-input" }), showDropdown && filteredOptions.length > 0 && (_jsx("div", { className: "font-selector-dropdown", children: isLoading ? (_jsx("div", { className: "font-selector-loading", children: "Loading fonts..." })) : (filteredOptions.map((option) => (_jsxs("div", { className: "font-selector-option", onClick: () => handleOptionSelect(option), onKeyDown: (e) => {
100
+ if (e.key === 'Enter' || e.key === ' ') {
101
+ e.preventDefault();
102
+ handleOptionSelect(option);
103
+ }
104
+ }, tabIndex: 0, role: "option", "aria-selected": inputValue === option.value, children: [_jsx("span", { className: "font-selector-font-name", children: option.label }), option.category && (_jsxs("span", { className: "font-selector-font-category", children: ["(", option.category, ")"] }))] }, option.value)))) }))] })] }));
105
+ }
106
+ FontSelector.propTypes = {
107
+ id: PropTypes.string.isRequired,
108
+ name: PropTypes.string.isRequired,
109
+ label: PropTypes.string.isRequired,
110
+ fontType: PropTypes.oneOf(['google', 'websafe', 'generic']).isRequired,
111
+ required: PropTypes.bool,
112
+ placeholder: PropTypes.string,
113
+ value: PropTypes.string,
114
+ onChange: PropTypes.func,
115
+ };