@pixelated-tech/components 3.4.3 → 3.5.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 (228) hide show
  1. package/README.md +12 -191
  2. package/dist/components/admin/componentusage/componentAnalysis.js +12 -4
  3. package/dist/components/admin/componentusage/componentDiscovery.js +20 -6
  4. package/dist/components/admin/site-health/site-health-accessibility.js +5 -1
  5. package/dist/components/admin/site-health/site-health-axe-core.js +4 -0
  6. package/dist/components/admin/site-health/site-health-cloudwatch.integration.js +0 -5
  7. package/dist/components/admin/site-health/site-health-cloudwatch.js +7 -1
  8. package/dist/components/admin/site-health/site-health-dependency-vulnerabilities.js +4 -0
  9. package/dist/components/admin/site-health/site-health-github.js +6 -0
  10. package/dist/components/admin/site-health/site-health-google-analytics.js +6 -0
  11. package/dist/components/admin/site-health/site-health-google-search-console.js +6 -0
  12. package/dist/components/admin/site-health/site-health-on-site-seo.integration.js +128 -55
  13. package/dist/components/admin/site-health/site-health-on-site-seo.js +4 -0
  14. package/dist/components/admin/site-health/site-health-overview.js +11 -4
  15. package/dist/components/admin/site-health/site-health-performance.js +4 -0
  16. package/dist/components/admin/site-health/site-health-security.js +5 -1
  17. package/dist/components/admin/site-health/site-health-seo.js +5 -1
  18. package/dist/components/admin/site-health/site-health-template.js +19 -9
  19. package/dist/components/admin/site-health/site-health-uptime.js +4 -0
  20. package/dist/components/callout/callout.js +0 -10
  21. package/dist/components/carousel/carousel.js +15 -4
  22. package/dist/components/carousel/tiles.js +1 -1
  23. package/dist/components/cms/contentful.items.components.js +3 -4
  24. package/dist/components/cms/flickr.js +1 -1
  25. package/dist/components/cms/google.reviews.components.js +3 -3
  26. package/dist/components/cms/instagram.components.js +15 -5
  27. package/dist/components/cms/smartimage.js +2 -2
  28. package/dist/components/cms/wordpress.components.js +32 -6
  29. package/dist/components/cms/yelp.js +5 -0
  30. package/dist/components/config/config.server.js +7 -1
  31. package/dist/components/general/css.js +0 -1
  32. package/dist/components/general/image.js +0 -1
  33. package/dist/components/general/loading.js +2 -1
  34. package/dist/components/general/microinteractions.js +0 -1
  35. package/dist/components/general/modal.css +2 -4
  36. package/dist/components/general/modal.js +72 -30
  37. package/dist/components/general/sidepanel.js +16 -0
  38. package/dist/components/general/tab.js +1 -0
  39. package/dist/components/menu/menu-accordion.css +1 -1
  40. package/dist/components/menu/menu-accordion.js +15 -4
  41. package/dist/components/menu/menu-expando.js +21 -19
  42. package/dist/components/menu/menu-simple.js +14 -14
  43. package/dist/components/nerdjoke/nerdjoke.js +1 -1
  44. package/dist/components/seo/googlesearch.js +0 -1
  45. package/dist/components/seo/schema-blogposting.js +6 -1
  46. package/dist/components/seo/schema-recipe.js +34 -1
  47. package/dist/components/seo/schema-services.js +20 -2
  48. package/dist/components/shoppingcart/ebay.components.js +3 -3
  49. package/dist/components/shoppingcart/shoppingcart.components.js +76 -28
  50. package/dist/components/shoppingcart/shoppingcart.functions.js +4 -4
  51. package/dist/components/sitebuilder/config/CompoundFontSelector.js +13 -4
  52. package/dist/components/sitebuilder/config/ConfigBuilder.css +194 -5
  53. package/dist/components/sitebuilder/config/ConfigBuilder.js +183 -17
  54. package/dist/components/sitebuilder/config/FontSelector.js +13 -2
  55. package/dist/components/sitebuilder/config/routes-form.json +67 -0
  56. package/dist/components/sitebuilder/config/siteinfo-form.json +28 -14
  57. package/dist/components/sitebuilder/config/visualdesignform.json +4 -4
  58. package/dist/components/sitebuilder/form/formbuilder.js +1 -0
  59. package/dist/components/sitebuilder/form/formcomponents.js +2 -3
  60. package/dist/components/sitebuilder/form/formengine.js +6 -5
  61. package/dist/components/sitebuilder/form/formvalidator.js +5 -0
  62. package/dist/components/sitebuilder/page/components/PageBuilderUI.js +5 -1
  63. package/dist/components/structured/buzzwordbingo.css +0 -1
  64. package/dist/components/structured/recipe.js +1 -1
  65. package/dist/components/structured/socialcard.js +2 -2
  66. package/dist/components/utilities/functions.js +82 -1
  67. package/dist/components/utilities/gemini-api.client.js +76 -0
  68. package/dist/components/utilities/gemini-api.server.js +185 -0
  69. package/dist/data/routes.json +5 -5
  70. package/dist/index.adminclient.js +30 -0
  71. package/dist/index.adminserver.js +21 -0
  72. package/dist/index.js +4 -18
  73. package/dist/index.server.js +15 -28
  74. package/dist/types/components/admin/componentusage/componentAnalysis.d.ts.map +1 -1
  75. package/dist/types/components/admin/componentusage/componentDiscovery.d.ts +1 -1
  76. package/dist/types/components/admin/componentusage/componentDiscovery.d.ts.map +1 -1
  77. package/dist/types/components/admin/site-health/site-health-accessibility.d.ts +7 -4
  78. package/dist/types/components/admin/site-health/site-health-accessibility.d.ts.map +1 -1
  79. package/dist/types/components/admin/site-health/site-health-axe-core.d.ts +7 -4
  80. package/dist/types/components/admin/site-health/site-health-axe-core.d.ts.map +1 -1
  81. package/dist/types/components/admin/site-health/site-health-cloudwatch.d.ts +9 -6
  82. package/dist/types/components/admin/site-health/site-health-cloudwatch.d.ts.map +1 -1
  83. package/dist/types/components/admin/site-health/site-health-cloudwatch.integration.d.ts.map +1 -1
  84. package/dist/types/components/admin/site-health/site-health-dependency-vulnerabilities.d.ts +7 -4
  85. package/dist/types/components/admin/site-health/site-health-dependency-vulnerabilities.d.ts.map +1 -1
  86. package/dist/types/components/admin/site-health/site-health-github.d.ts +9 -6
  87. package/dist/types/components/admin/site-health/site-health-github.d.ts.map +1 -1
  88. package/dist/types/components/admin/site-health/site-health-google-analytics.d.ts +9 -6
  89. package/dist/types/components/admin/site-health/site-health-google-analytics.d.ts.map +1 -1
  90. package/dist/types/components/admin/site-health/site-health-google-search-console.d.ts +9 -6
  91. package/dist/types/components/admin/site-health/site-health-google-search-console.d.ts.map +1 -1
  92. package/dist/types/components/admin/site-health/site-health-on-site-seo.d.ts +8 -3
  93. package/dist/types/components/admin/site-health/site-health-on-site-seo.d.ts.map +1 -1
  94. package/dist/types/components/admin/site-health/site-health-on-site-seo.integration.d.ts.map +1 -1
  95. package/dist/types/components/admin/site-health/site-health-overview.d.ts +7 -4
  96. package/dist/types/components/admin/site-health/site-health-overview.d.ts.map +1 -1
  97. package/dist/types/components/admin/site-health/site-health-performance.d.ts +7 -4
  98. package/dist/types/components/admin/site-health/site-health-performance.d.ts.map +1 -1
  99. package/dist/types/components/admin/site-health/site-health-security.d.ts +7 -4
  100. package/dist/types/components/admin/site-health/site-health-security.d.ts.map +1 -1
  101. package/dist/types/components/admin/site-health/site-health-seo.d.ts +7 -4
  102. package/dist/types/components/admin/site-health/site-health-seo.d.ts.map +1 -1
  103. package/dist/types/components/admin/site-health/site-health-template.d.ts +12 -10
  104. package/dist/types/components/admin/site-health/site-health-template.d.ts.map +1 -1
  105. package/dist/types/components/admin/site-health/site-health-uptime.d.ts +7 -4
  106. package/dist/types/components/admin/site-health/site-health-uptime.d.ts.map +1 -1
  107. package/dist/types/components/callout/callout.d.ts +3 -3
  108. package/dist/types/components/callout/callout.d.ts.map +1 -1
  109. package/dist/types/components/carousel/carousel.d.ts +16 -7
  110. package/dist/types/components/carousel/carousel.d.ts.map +1 -1
  111. package/dist/types/components/carousel/tiles.d.ts +3 -6
  112. package/dist/types/components/carousel/tiles.d.ts.map +1 -1
  113. package/dist/types/components/cms/flickr.d.ts +3 -6
  114. package/dist/types/components/cms/flickr.d.ts.map +1 -1
  115. package/dist/types/components/cms/google.reviews.components.d.ts +1 -7
  116. package/dist/types/components/cms/google.reviews.components.d.ts.map +1 -1
  117. package/dist/types/components/cms/hubspot.components.d.ts +1 -2
  118. package/dist/types/components/cms/hubspot.components.d.ts.map +1 -1
  119. package/dist/types/components/cms/instagram.components.d.ts +14 -9
  120. package/dist/types/components/cms/instagram.components.d.ts.map +1 -1
  121. package/dist/types/components/cms/smartimage.d.ts +2 -28
  122. package/dist/types/components/cms/smartimage.d.ts.map +1 -1
  123. package/dist/types/components/cms/wordpress.components.d.ts +33 -14
  124. package/dist/types/components/cms/wordpress.components.d.ts.map +1 -1
  125. package/dist/types/components/cms/yelp.d.ts +9 -4
  126. package/dist/types/components/cms/yelp.d.ts.map +1 -1
  127. package/dist/types/components/config/config.server.d.ts +9 -6
  128. package/dist/types/components/config/config.server.d.ts.map +1 -1
  129. package/dist/types/components/general/loading.d.ts +5 -1
  130. package/dist/types/components/general/loading.d.ts.map +1 -1
  131. package/dist/types/components/general/microinteractions.d.ts +1 -3
  132. package/dist/types/components/general/microinteractions.d.ts.map +1 -1
  133. package/dist/types/components/general/modal.d.ts +11 -5
  134. package/dist/types/components/general/modal.d.ts.map +1 -1
  135. package/dist/types/components/general/semantic.d.ts +3 -3
  136. package/dist/types/components/general/sidepanel.d.ts +20 -13
  137. package/dist/types/components/general/sidepanel.d.ts.map +1 -1
  138. package/dist/types/components/general/tab.d.ts +1 -2
  139. package/dist/types/components/general/tab.d.ts.map +1 -1
  140. package/dist/types/components/menu/menu-accordion.d.ts +22 -9
  141. package/dist/types/components/menu/menu-accordion.d.ts.map +1 -1
  142. package/dist/types/components/menu/menu-expando.d.ts +14 -5
  143. package/dist/types/components/menu/menu-expando.d.ts.map +1 -1
  144. package/dist/types/components/menu/menu-simple.d.ts +4 -5
  145. package/dist/types/components/menu/menu-simple.d.ts.map +1 -1
  146. package/dist/types/components/nerdjoke/nerdjoke.d.ts +1 -1
  147. package/dist/types/components/nerdjoke/nerdjoke.d.ts.map +1 -1
  148. package/dist/types/components/seo/googleanalytics.d.ts.map +1 -1
  149. package/dist/types/components/seo/metadata.components.d.ts +2 -2
  150. package/dist/types/components/seo/metadata.components.d.ts.map +1 -1
  151. package/dist/types/components/seo/schema-blogposting.d.ts +7 -4
  152. package/dist/types/components/seo/schema-blogposting.d.ts.map +1 -1
  153. package/dist/types/components/seo/schema-recipe.d.ts +29 -30
  154. package/dist/types/components/seo/schema-recipe.d.ts.map +1 -1
  155. package/dist/types/components/seo/schema-services.d.ts +19 -9
  156. package/dist/types/components/seo/schema-services.d.ts.map +1 -1
  157. package/dist/types/components/shoppingcart/paypal.d.ts +1 -1
  158. package/dist/types/components/shoppingcart/paypal.d.ts.map +1 -1
  159. package/dist/types/components/shoppingcart/shoppingcart.components.d.ts +77 -28
  160. package/dist/types/components/shoppingcart/shoppingcart.components.d.ts.map +1 -1
  161. package/dist/types/components/shoppingcart/shoppingcart.functions.d.ts +4 -23
  162. package/dist/types/components/shoppingcart/shoppingcart.functions.d.ts.map +1 -1
  163. package/dist/types/components/sitebuilder/config/CompoundFontSelector.d.ts +10 -11
  164. package/dist/types/components/sitebuilder/config/CompoundFontSelector.d.ts.map +1 -1
  165. package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts +41 -174
  166. package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts.map +1 -1
  167. package/dist/types/components/sitebuilder/config/FontSelector.d.ts +12 -13
  168. package/dist/types/components/sitebuilder/config/FontSelector.d.ts.map +1 -1
  169. package/dist/types/components/sitebuilder/form/formbuilder.d.ts +7 -3
  170. package/dist/types/components/sitebuilder/form/formbuilder.d.ts.map +1 -1
  171. package/dist/types/components/sitebuilder/form/formcomponents.d.ts +1 -1
  172. package/dist/types/components/sitebuilder/form/formcomponents.d.ts.map +1 -1
  173. package/dist/types/components/sitebuilder/form/formengine.d.ts +1 -2
  174. package/dist/types/components/sitebuilder/form/formengine.d.ts.map +1 -1
  175. package/dist/types/components/sitebuilder/form/formextractor.d.ts +5 -4
  176. package/dist/types/components/sitebuilder/form/formextractor.d.ts.map +1 -1
  177. package/dist/types/components/sitebuilder/form/formtypes.d.ts +3 -3
  178. package/dist/types/components/sitebuilder/form/formtypes.d.ts.map +1 -1
  179. package/dist/types/components/sitebuilder/form/formvalidator.d.ts +8 -3
  180. package/dist/types/components/sitebuilder/form/formvalidator.d.ts.map +1 -1
  181. package/dist/types/components/sitebuilder/page/components/ComponentPropertiesForm.d.ts +2 -3
  182. package/dist/types/components/sitebuilder/page/components/ComponentPropertiesForm.d.ts.map +1 -1
  183. package/dist/types/components/sitebuilder/page/components/ComponentSelector.d.ts +2 -3
  184. package/dist/types/components/sitebuilder/page/components/ComponentSelector.d.ts.map +1 -1
  185. package/dist/types/components/sitebuilder/page/components/ComponentTree.d.ts +2 -3
  186. package/dist/types/components/sitebuilder/page/components/ComponentTree.d.ts.map +1 -1
  187. package/dist/types/components/sitebuilder/page/components/PageBuilderUI.d.ts +8 -7
  188. package/dist/types/components/sitebuilder/page/components/PageBuilderUI.d.ts.map +1 -1
  189. package/dist/types/components/sitebuilder/page/components/PageEngine.d.ts.map +1 -1
  190. package/dist/types/components/sitebuilder/page/components/SaveLoadSection.d.ts +2 -3
  191. package/dist/types/components/sitebuilder/page/components/SaveLoadSection.d.ts.map +1 -1
  192. package/dist/types/components/sitebuilder/page/lib/componentMap.d.ts +1 -1
  193. package/dist/types/components/structured/markdown.d.ts +1 -3
  194. package/dist/types/components/structured/markdown.d.ts.map +1 -1
  195. package/dist/types/components/structured/recipe.d.ts +5 -32
  196. package/dist/types/components/structured/recipe.d.ts.map +1 -1
  197. package/dist/types/components/structured/socialcard.d.ts +4 -0
  198. package/dist/types/components/structured/socialcard.d.ts.map +1 -1
  199. package/dist/types/components/structured/timeline.d.ts +1 -3
  200. package/dist/types/components/structured/timeline.d.ts.map +1 -1
  201. package/dist/types/components/utilities/functions.d.ts +20 -0
  202. package/dist/types/components/utilities/functions.d.ts.map +1 -1
  203. package/dist/types/components/utilities/gemini-api.client.d.ts +38 -0
  204. package/dist/types/components/utilities/gemini-api.client.d.ts.map +1 -0
  205. package/dist/types/components/utilities/gemini-api.server.d.ts +17 -0
  206. package/dist/types/components/utilities/gemini-api.server.d.ts.map +1 -0
  207. package/dist/types/index.adminclient.d.ts +27 -0
  208. package/dist/types/index.adminclient.d.ts.map +1 -0
  209. package/dist/types/index.adminserver.d.ts +19 -0
  210. package/dist/types/index.adminserver.d.ts.map +1 -0
  211. package/dist/types/index.d.ts +4 -18
  212. package/dist/types/index.server.d.ts +5 -28
  213. package/dist/types/stories/general/sidepanel.stories.d.ts.map +1 -1
  214. package/dist/types/stories/general/smartimage.stories.d.ts +74 -2
  215. package/dist/types/stories/general/smartimage.stories.d.ts.map +1 -1
  216. package/package.json +19 -9
  217. package/README.COMPONENTS.md +0 -2310
  218. package/dist/components/cms/pixelated.linkedin.js +0 -180
  219. package/dist/components/cms/pixelated.linkedin1.js +0 -84
  220. package/dist/components/cms/pixelated.linkedin2.js +0 -92
  221. package/dist/types/components/cms/pixelated.linkedin.d.ts +0 -2
  222. package/dist/types/components/cms/pixelated.linkedin.d.ts.map +0 -1
  223. package/dist/types/components/cms/pixelated.linkedin1.d.ts +0 -2
  224. package/dist/types/components/cms/pixelated.linkedin1.d.ts.map +0 -1
  225. package/dist/types/components/cms/pixelated.linkedin2.d.ts +0 -2
  226. package/dist/types/components/cms/pixelated.linkedin2.d.ts.map +0 -1
  227. package/dist/types/tests/pixelated.menu-expando.test.d.ts +0 -2
  228. package/dist/types/tests/pixelated.menu-expando.test.d.ts.map +0 -1
@@ -1,8 +1,18 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useEffect } from 'react';
4
+ import PropTypes from 'prop-types';
4
5
  import { getInstagramTiles } from './instagram.functions';
5
6
  import { Tiles } from '../carousel/tiles';
7
+ InstagramTiles.propTypes = {
8
+ accessToken: PropTypes.string,
9
+ userId: PropTypes.string,
10
+ limit: PropTypes.number,
11
+ rowCount: PropTypes.number,
12
+ useThumbnails: PropTypes.bool,
13
+ includeVideos: PropTypes.bool,
14
+ includeCaptions: PropTypes.bool,
15
+ };
6
16
  export function InstagramTiles(props) {
7
17
  const [tiles, setTiles] = useState([]);
8
18
  const [loading, setLoading] = useState(true);
@@ -11,12 +21,12 @@ export function InstagramTiles(props) {
11
21
  (async () => {
12
22
  try {
13
23
  const result = await getInstagramTiles({
14
- accessToken: props.accessToken,
15
- userId: props.userId,
24
+ accessToken: props.accessToken ?? undefined,
25
+ userId: props.userId ?? undefined,
16
26
  limit: props.limit ?? 12,
17
- useThumbnails: props.useThumbnails,
18
- includeVideos: props.includeVideos,
19
- includeCaptions: props.includeCaptions,
27
+ useThumbnails: props.useThumbnails ?? undefined,
28
+ includeVideos: props.includeVideos ?? undefined,
29
+ includeCaptions: props.includeCaptions ?? undefined,
20
30
  });
21
31
  setTiles(result);
22
32
  setLoading(false);
@@ -80,8 +80,8 @@ export function SmartImage(props) {
80
80
  newProps.name = safeString(name);
81
81
  newProps.title = safeString(title);
82
82
  newProps.alt = safeString(alt) ?? '';
83
- newProps.width = parseNumber(width) || 500;
84
- newProps.height = parseNumber(height) || 500;
83
+ newProps.width = parseNumber(width ?? undefined) ?? 500;
84
+ newProps.height = parseNumber(height ?? undefined) ?? 500;
85
85
  const filename = (newProps.src).split('/').pop()?.split('?')[0] || '';
86
86
  const imageName = filename.replace(/\.[^.]+$/, '');
87
87
  newProps.id = newProps.id || newProps.name || sanitizeString(newProps.title) || sanitizeString(newProps.alt) || sanitizeString(imageName);
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useState } from 'react';
4
+ import PropTypes from 'prop-types';
4
5
  import { usePixelatedConfig } from "../config/config.client";
5
6
  import { SmartImage } from './smartimage';
6
7
  import { PageGridItem } from '../general/semantic';
@@ -13,6 +14,13 @@ function decodeString(str) {
13
14
  textarea.innerHTML = str;
14
15
  return textarea.value;
15
16
  }
17
+ BlogPostList.propTypes = {
18
+ site: PropTypes.string,
19
+ baseURL: PropTypes.string,
20
+ count: PropTypes.number,
21
+ posts: PropTypes.array,
22
+ showCategories: PropTypes.bool,
23
+ };
16
24
  export function BlogPostList(props) {
17
25
  const { site: propSite, baseURL: propBaseURL, count, posts: cachedPosts, showCategories = true } = props;
18
26
  const config = usePixelatedConfig();
@@ -34,7 +42,12 @@ export function BlogPostList(props) {
34
42
  // Otherwise, fetch from WordPress
35
43
  ToggleLoading({ show: true });
36
44
  (async () => {
37
- const data = (await getWordPressItems({ site, count, baseURL })) ?? [];
45
+ const params = { site };
46
+ if (count !== null && count !== undefined)
47
+ params.count = count;
48
+ if (baseURL !== null && baseURL !== undefined)
49
+ params.baseURL = baseURL;
50
+ const data = (await getWordPressItems(params)) ?? [];
38
51
  const sorted = data.sort((a, b) => ((a.date ?? '') < (b.date ?? '')) ? 1 : -1);
39
52
  setPosts(sorted);
40
53
  ToggleLoading({ show: false });
@@ -42,20 +55,33 @@ export function BlogPostList(props) {
42
55
  }, [site, baseURL, count, cachedPosts]);
43
56
  return (_jsxs(_Fragment, { children: [_jsx(Loading, {}), posts.map((post) => (_jsx(PageGridItem, { children: _jsx(BlogPostSummary, { ID: post.ID, title: post.title, date: post.date, excerpt: post.excerpt, URL: post.URL, categories: post.categories, featured_image: post.featured_image, showCategories: showCategories }) }, post.ID)))] }));
44
57
  }
58
+ BlogPostSummary.propTypes = {
59
+ ID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
60
+ title: PropTypes.string,
61
+ date: PropTypes.string,
62
+ excerpt: PropTypes.string,
63
+ URL: PropTypes.string,
64
+ categories: PropTypes.object,
65
+ featured_image: PropTypes.string,
66
+ showCategories: PropTypes.bool,
67
+ };
45
68
  export function BlogPostSummary(props) {
46
- const myCategoryImages = Object.entries(props.categories).map(([category, index]) => [category.trim().toLowerCase().replace(/[ /]+/g, '-'), index]).sort();
69
+ const myCategoryImages = props.categories ? Object.entries(props.categories).map(([category, index]) => [category?.trim().toLowerCase().replace(/[ /]+/g, '-'), index]).sort() : [];
47
70
  const config = usePixelatedConfig();
48
- const myExcerpt = decodeString(props.excerpt).replace(/\[…\]/g, '<a href="' + props.URL + '" target="_blank" rel="noopener noreferrer">[…]</a>');
49
- return (_jsx("div", { className: "blog-post-summary", children: _jsxs("article", { className: "h-entry", children: [_jsx("h2", { className: "p-name", children: _jsx("a", { className: "u-url blog-post-url", href: props.URL, target: "_blank", rel: "noopener noreferrer", children: decodeString(props.title) }) }), _jsxs("div", { className: "dt-published", children: ["Published: ", new Date(props.date).toLocaleDateString()] }), props.featured_image ? (_jsxs("div", { className: "article-body row-12col", children: [_jsx("div", { className: "article-featured-image grid-s1-e4", children: _jsx(SmartImage, { className: "u-photo", src: props.featured_image, alt: decodeString(props.title), title: decodeString(props.title), style: {}, cloudinaryEnv: config?.cloudinary?.product_env ?? undefined, cloudinaryDomain: config?.cloudinary?.baseUrl ?? undefined, cloudinaryTransforms: config?.cloudinary?.transforms ?? undefined }) }), _jsx("div", { className: "article-excerpt grid-s4-e13", children: _jsx("div", { className: "p-summary", dangerouslySetInnerHTML: { __html: myExcerpt } }) })] })) :
71
+ const myExcerpt = props.excerpt ? decodeString(props.excerpt).replace(/\[…\]/g, '<a href="' + (props.URL || '') + '" target="_blank" rel="noopener noreferrer">[…]</a>') : '';
72
+ return (_jsx("div", { className: "blog-post-summary", children: _jsxs("article", { className: "h-entry", children: [_jsx("h2", { className: "p-name", children: _jsx("a", { className: "u-url blog-post-url", href: props.URL || '', target: "_blank", rel: "noopener noreferrer", children: props.title ? decodeString(props.title) : '' }) }), _jsxs("div", { className: "dt-published", children: ["Published: ", props.date ? new Date(props.date).toLocaleDateString() : ''] }), props.featured_image ? (_jsxs("div", { className: "article-body row-12col", children: [_jsx("div", { className: "article-featured-image grid-s1-e4", children: _jsx(SmartImage, { className: "u-photo", src: props.featured_image, alt: props.title ? decodeString(props.title) : '', title: props.title ? decodeString(props.title) : '', style: {}, cloudinaryEnv: config?.cloudinary?.product_env ?? undefined, cloudinaryDomain: config?.cloudinary?.baseUrl ?? undefined, cloudinaryTransforms: config?.cloudinary?.transforms ?? undefined }) }), _jsx("div", { className: "article-excerpt grid-s4-e13", children: _jsx("div", { className: "p-summary", dangerouslySetInnerHTML: { __html: myExcerpt } }) })] })) :
50
73
  _jsx("div", { className: "article-excerpt grid-s1-e13", children: _jsx("div", { className: "p-summary", dangerouslySetInnerHTML: { __html: myExcerpt } }) }), props.showCategories !== false && (_jsxs("div", { children: ["Categories:", myCategoryImages.map(([categoryImg, index]) => (_jsx("span", { className: "p-category", children: _jsx(SmartImage, { src: `/images/icons/${categoryImg}.png`, title: String(categoryImg), alt: String(categoryImg), cloudinaryEnv: config?.cloudinary?.product_env ?? undefined, cloudinaryDomain: config?.cloudinary?.baseUrl ?? undefined, cloudinaryTransforms: config?.cloudinary?.transforms ?? undefined }) }, categoryImg + "-" + index)))] }))] }) }, props.ID));
51
74
  }
75
+ BlogPostCategories.propTypes = {
76
+ categories: PropTypes.arrayOf(PropTypes.string),
77
+ };
52
78
  export function BlogPostCategories(props) {
53
79
  if (!props.categories || props.categories.length === 0) {
54
80
  return null;
55
81
  }
56
- const myCategoryImages = props.categories.map((category) => (category !== "Uncategorized")
82
+ const myCategoryImages = props.categories.map((category) => (category && category !== "Uncategorized")
57
83
  ? category.trim().toLowerCase().replace(/[ /]+/g, '-')
58
- : undefined).sort();
84
+ : undefined).filter(Boolean).sort();
59
85
  const config = usePixelatedConfig();
60
86
  return (_jsxs("div", { className: "blogPostCategories", children: [_jsx("div", { children: "Categories: " }), myCategoryImages.map((categoryImg, index) => categoryImg ? (_jsx("span", { className: "p-category", children: _jsx(SmartImage, { className: "u-photo", src: `/images/icons/${categoryImg}.png`, title: String(categoryImg), alt: String(categoryImg), cloudinaryEnv: config?.cloudinary?.product_env ?? undefined, cloudinaryDomain: config?.cloudinary?.baseUrl ?? undefined, cloudinaryTransforms: config?.cloudinary?.transforms ?? undefined }) }, categoryImg + "-" + index)) : null)] }));
61
87
  }
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useEffect } from "react";
4
+ import PropTypes from 'prop-types';
4
5
  /*
5
6
  NOTE : development has stopped for this component
6
7
  as Yelp Base API Access costs $229 per month.
@@ -12,6 +13,10 @@ https://www.google.com/search?q=yelp+reviews+react+component&oq=yelp+reviews+rea
12
13
  https://www.reddit.com/r/nextjs/comments/16smhqa/next_js_fetching_data_from_yelp_api/
13
14
  https://helloputnam.medium.com/easiest-way-to-include-business-reviews-on-a-web-app-google-facebook-yelp-etc-de3e243bbe75
14
15
  */
16
+ YelpReviews.propTypes = {
17
+ businessID: PropTypes.string.isRequired,
18
+ key: PropTypes.string,
19
+ };
15
20
  export function YelpReviews(props) {
16
21
  const [reviews, setReviews] = useState([]);
17
22
  const [loading, setLoading] = useState(true);
@@ -1,9 +1,15 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import PropTypes from 'prop-types';
2
3
  import { getClientOnlyPixelatedConfig } from './config';
3
4
  // Server wrapper: reads server env blob and sanitizes it, then mounts the client provider.
4
5
  // Important: do NOT import client components at module scope — dynamically import
5
6
  // the client provider inside the function so this module remains server-safe.
6
- export async function PixelatedServerConfigProvider({ config, children, }) {
7
+ PixelatedServerConfigProvider.propTypes = {
8
+ config: PropTypes.object,
9
+ children: PropTypes.node.isRequired,
10
+ };
11
+ export async function PixelatedServerConfigProvider(props) {
12
+ const { config, children } = props;
7
13
  const cfg = config ?? getClientOnlyPixelatedConfig();
8
14
  const mod = await import('./config.client');
9
15
  const Provider = mod.PixelatedClientConfigProvider;
@@ -1,4 +1,3 @@
1
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2
1
  function isPartialMatchInArray(searchString, array) {
3
2
  // Convert both the search string and array items to lowercase for case-insensitive matching
4
3
  const lowerCaseSearchString = searchString.toLowerCase();
@@ -20,7 +20,6 @@
20
20
  imageObserver.observe(image);
21
21
  });
22
22
  } */
23
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
24
23
  function isInViewport(el) {
25
24
  const rect = el.getBoundingClientRect();
26
25
  return (rect.top >= 0 &&
@@ -7,7 +7,8 @@ https://signalvnoise.com/posts/2577-loading-spinner-animation-using-css-and-webk
7
7
  https://www.andreaverlicchi.eu/blog/css-3-only-spinning-loading-animation/
8
8
  */
9
9
  /* ========== MARKDOWN ========== */
10
- export function Loading() {
10
+ Loading.propTypes = {};
11
+ export function Loading(props) {
11
12
  return (_jsx(_Fragment, { children: _jsx("div", { id: "loadingSpinner", className: "loading", children: _jsxs("div", { className: "spinner", children: [_jsx("div", { className: "bar1" }), _jsx("div", { className: "bar2" }), _jsx("div", { className: "bar3" }), _jsx("div", { className: "bar4" }), _jsx("div", { className: "bar5" }), _jsx("div", { className: "bar6" }), _jsx("div", { className: "bar7" }), _jsx("div", { className: "bar8" }), _jsx("div", { className: "bar9" }), _jsx("div", { className: "bar10" }), _jsx("div", { className: "bar11" }), _jsx("div", { className: "bar12" })] }) }) }));
12
13
  }
13
14
  ToggleLoading.propTypes = {
@@ -29,7 +29,6 @@ export function MicroInteractions(props) {
29
29
  if (props.scrollfadeElements)
30
30
  ScrollFade(props.scrollfadeElements);
31
31
  }
32
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
32
  function isElementInViewport(el) {
34
33
  const rect = el.getBoundingClientRect();
35
34
  return (rect.top >= 0 &&
@@ -1,11 +1,9 @@
1
1
 
2
2
  .modal {
3
-
4
3
  position: fixed; /* Stay in place */
5
4
  z-index: 1000; /* Sit on top */
6
- top: 50%;
7
- left: 50%;
8
- transform: translate(-50%, -50%);
5
+ top: 0;
6
+ left: 0;
9
7
  width: 100%; /* Full width */
10
8
  height: 100%; /* Full height */
11
9
  overflow: auto; /* Enable scroll if needed */
@@ -1,45 +1,87 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect } from 'react';
3
+ import { useEffect, useRef } from 'react';
4
+ import PropTypes from 'prop-types';
4
5
  import './modal.css';
5
6
  /*
6
7
  https://www.w3schools.com/howto/howto_css_modals.asp
7
8
  */
8
- export function Modal(props) {
9
- const myModalID = "myModal" + (props.modalID ?? '');
10
- const myModalCloseID = "myModalClose" + (props.modalID ?? '');
9
+ Modal.propTypes = {
10
+ modalContent: PropTypes.node.isRequired,
11
+ modalID: PropTypes.string,
12
+ isOpen: PropTypes.bool,
13
+ handleCloseEvent: PropTypes.func,
14
+ };
15
+ export function Modal({ modalContent, modalID, isOpen = false, handleCloseEvent }) {
16
+ const myModalID = "myModal" + (modalID ?? '');
17
+ const myModalCloseID = "myModalClose" + (modalID ?? '');
18
+ const modalRef = useRef(null);
11
19
  useEffect(() => {
12
- const handleModalClose = (event) => {
13
- event.preventDefault();
14
- const myModal = document.getElementById(myModalID);
15
- if (myModal) {
16
- myModal.style.display = 'none';
17
- }
18
- };
19
- const myModalClose = document.getElementById(myModalCloseID);
20
- if (myModalClose) {
21
- myModalClose.addEventListener('click', handleModalClose);
22
- }
23
- ;
24
- const handleWindowOnClick = (event) => {
25
- const myModal = document.getElementById(myModalID);
26
- if (event.target == myModal) {
20
+ // Only use DOM event listeners for backward compatibility when handleCloseEvent is not provided
21
+ if (!handleCloseEvent) {
22
+ const handleModalClose = (event) => {
23
+ event.preventDefault();
24
+ const myModal = document.getElementById(myModalID);
27
25
  if (myModal) {
28
- myModal.style.display = "none";
26
+ myModal.style.display = 'none';
29
27
  }
30
- ;
31
- }
32
- };
33
- window.addEventListener('click', handleWindowOnClick);
34
- return () => {
35
- window.removeEventListener('click', handleWindowOnClick);
28
+ };
29
+ const myModalClose = document.getElementById(myModalCloseID);
36
30
  if (myModalClose) {
37
- myModalClose.removeEventListener('click', handleModalClose);
31
+ myModalClose.addEventListener('click', handleModalClose);
38
32
  }
39
33
  ;
40
- };
41
- }, []);
42
- return (_jsx("div", { id: myModalID, className: "modal", style: { display: 'none' }, children: _jsxs("div", { className: "modal-content", children: [_jsx("span", { id: myModalCloseID, className: "modal-close", "aria-hidden": "true", children: "\u00D7" }), props.modalContent] }) }));
34
+ const handleWindowOnClick = (event) => {
35
+ const myModal = document.getElementById(myModalID);
36
+ if (event.target == myModal) {
37
+ if (myModal) {
38
+ myModal.style.display = 'none';
39
+ }
40
+ }
41
+ };
42
+ window.addEventListener('click', handleWindowOnClick);
43
+ return () => {
44
+ window.removeEventListener('click', handleWindowOnClick);
45
+ if (myModalClose) {
46
+ myModalClose.removeEventListener('click', handleModalClose);
47
+ }
48
+ ;
49
+ };
50
+ }
51
+ else {
52
+ // For React approach, add escape key listener
53
+ const handleEscape = (event) => {
54
+ if (event.key === 'Escape') {
55
+ handleCloseEvent();
56
+ }
57
+ };
58
+ document.addEventListener('keydown', handleEscape);
59
+ return () => {
60
+ document.removeEventListener('keydown', handleEscape);
61
+ };
62
+ }
63
+ }, [myModalID, myModalCloseID, handleCloseEvent]);
64
+ const handleCloseClick = handleCloseEvent ? (event) => {
65
+ event.preventDefault();
66
+ handleCloseEvent();
67
+ } : undefined;
68
+ const handleCloseKeyDown = handleCloseEvent ? (event) => {
69
+ if (event.key === 'Enter' || event.key === ' ') {
70
+ event.preventDefault();
71
+ handleCloseEvent();
72
+ }
73
+ } : undefined;
74
+ const handleModalClick = handleCloseEvent ? (event) => {
75
+ if (event.target === modalRef.current) {
76
+ handleCloseEvent();
77
+ }
78
+ } : undefined;
79
+ const handleModalKeyDown = handleCloseEvent ? (event) => {
80
+ if (event.key === 'Escape' && event.target === modalRef.current) {
81
+ handleCloseEvent();
82
+ }
83
+ } : undefined;
84
+ return (_jsx("div", { id: myModalID, className: "modal", style: { display: isOpen ? 'block' : 'none' }, ref: modalRef, onClick: handleModalClick, children: _jsxs("div", { className: "modal-content", role: "dialog", "aria-modal": "true", children: [_jsx("button", { id: myModalCloseID, className: "modal-close", "aria-label": "Close modal", onClick: handleCloseClick, onKeyDown: handleCloseKeyDown, type: "button", children: "\u00D7" }), modalContent] }) }));
43
85
  }
44
86
  export const handleModalOpen = (event, modalID) => {
45
87
  const myModalID = "myModal" + (modalID ?? '');
@@ -3,7 +3,23 @@
3
3
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  import { useEffect, useRef, useState } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
+ import PropTypes from 'prop-types';
6
7
  import './sidepanel.css';
8
+ // Define const arrays for options - used by both PropTypes and form generation
9
+ export const positions = ['left', 'right'];
10
+ SidePanel.propTypes = {
11
+ isOpen: PropTypes.bool.isRequired,
12
+ onClose: PropTypes.func.isRequired,
13
+ onToggle: PropTypes.func,
14
+ position: PropTypes.oneOf([...positions]),
15
+ width: PropTypes.string,
16
+ showOverlay: PropTypes.bool,
17
+ showTab: PropTypes.bool,
18
+ tabIcon: PropTypes.node,
19
+ tabLabel: PropTypes.string,
20
+ children: PropTypes.node,
21
+ className: PropTypes.string,
22
+ };
7
23
  export default function SidePanel({ isOpen, onClose, onToggle, position = 'left', width = '300px', showOverlay = true, showTab = false, tabIcon /* = "≡" */, tabLabel, children, className = '' }) {
8
24
  const portalRootRef = useRef(null);
9
25
  const wrapperRef = useRef(null);
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { useState } from 'react';
3
4
  import PropTypes from 'prop-types';
@@ -26,7 +26,7 @@
26
26
  .panelMenuButton .hamburger{
27
27
  cursor: pointer;
28
28
  font-family: Verdana;
29
- font-size: 1.5rem;
29
+ font-size: 1.75rem;
30
30
  color: #CCC;
31
31
 
32
32
  }
@@ -4,6 +4,15 @@ import { useEffect, useRef } from 'react';
4
4
  import PropTypes from 'prop-types';
5
5
  import './menu-accordion.css';
6
6
  const hamburgerIcon = "≡"; /* (U+2261) */ /* ||| */
7
+ const menuItemShape = PropTypes.shape({
8
+ name: PropTypes.string.isRequired,
9
+ path: PropTypes.string,
10
+ target: PropTypes.string,
11
+ routes: PropTypes.array, // Will be refined after function declaration
12
+ hidden: PropTypes.bool,
13
+ });
14
+ // Update the recursive reference after the shape is defined
15
+ menuItemShape.routes = PropTypes.arrayOf(menuItemShape);
7
16
  function generateMenuItems({ menuData, state = "hide" }) {
8
17
  const myItems = [];
9
18
  let index = 0;
@@ -27,9 +36,8 @@ function generateMenuItems({ menuData, state = "hide" }) {
27
36
  }
28
37
  return myItems;
29
38
  }
30
- /* ========== MENU ========== */
31
39
  MenuAccordion.propTypes = {
32
- menuItems: PropTypes.any.isRequired,
40
+ menuItems: PropTypes.arrayOf(menuItemShape).isRequired,
33
41
  showHidden: PropTypes.bool,
34
42
  };
35
43
  export function MenuAccordion(props) {
@@ -145,7 +153,10 @@ export function MenuAccordion(props) {
145
153
  }
146
154
  /* ========== MENU GROUP ========== */
147
155
  MenuAccordionGroup.propTypes = {
148
- menuItems: PropTypes.object.isRequired,
156
+ menuItems: PropTypes.oneOfType([
157
+ menuItemShape,
158
+ PropTypes.arrayOf(menuItemShape)
159
+ ]).isRequired,
149
160
  state: PropTypes.string,
150
161
  };
151
162
  export function MenuAccordionGroup(props) {
@@ -168,7 +179,7 @@ export function MenuAccordionItem(props) {
168
179
  https://www.unclebigbay.com/blog/building-the-world-simplest-hamburger-with-html-and-css
169
180
  */
170
181
  MenuAccordionButton.propTypes = {};
171
- export function MenuAccordionButton() {
182
+ export function MenuAccordionButton(props) {
172
183
  function slideMobilePanel() {
173
184
  if (typeof window !== 'undefined' && window.moveMenu) {
174
185
  window.moveMenu();
@@ -1,9 +1,18 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */
2
1
  'use client';
3
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
3
  import { useEffect, useRef } from 'react';
5
4
  import PropTypes from 'prop-types';
6
5
  import './menu-expando.css';
6
+ MenuExpando.propTypes = {
7
+ menuItems: PropTypes.oneOfType([
8
+ PropTypes.objectOf(PropTypes.string),
9
+ PropTypes.arrayOf(PropTypes.shape({
10
+ name: PropTypes.string.isRequired,
11
+ path: PropTypes.string.isRequired,
12
+ routes: PropTypes.array,
13
+ }))
14
+ ]).isRequired
15
+ };
7
16
  export function MenuExpando(props) {
8
17
  const detailsRef = useRef(null);
9
18
  const ulRef = useRef(null);
@@ -91,11 +100,11 @@ export function MenuExpando(props) {
91
100
  if (Array.isArray(props.menuItems)) {
92
101
  // Array format like MenuAccordion
93
102
  for (const item of props.menuItems) {
94
- if (item.routes && item.routes.length > 0) {
103
+ if (item && item.routes && item.routes.length > 0) {
95
104
  // Item has nested routes - create expandable submenu
96
105
  myItems.push(_jsx("li", { children: _jsxs("details", { className: "menuExpandoNested", children: [_jsx("summary", { children: _jsx("a", { href: item.path, children: item.name }) }), _jsx("ul", { children: item.routes.map((route) => (_jsx(MenuExpandoItem, { name: route.name, href: route.path }, route.name))) })] }) }, item.name));
97
106
  }
98
- else {
107
+ else if (item) {
99
108
  // Regular item without nested routes
100
109
  myItems.push(_jsx(MenuExpandoItem, { name: item.name, href: item.path }, item.name));
101
110
  }
@@ -104,31 +113,25 @@ export function MenuExpando(props) {
104
113
  else {
105
114
  // Object format
106
115
  for (const itemKey in props.menuItems) {
107
- myItems.push(_jsx(MenuExpandoItem, { name: itemKey, href: props.menuItems[itemKey] }, itemKey));
116
+ const href = props.menuItems[itemKey];
117
+ if (typeof href === 'string') {
118
+ myItems.push(_jsx(MenuExpandoItem, { name: itemKey, href: href }, itemKey));
119
+ }
108
120
  }
109
121
  }
110
122
  return myItems;
111
123
  }
112
124
  return (_jsx("div", { className: "menuExpando", id: "menuExpando", children: _jsxs("details", { className: "menu-expando-wrapper", id: "menu-expando-wrapper", ref: detailsRef, children: [_jsx("summary", {}), _jsx("ul", { ref: ulRef, children: generateMenuItems() })] }) }));
113
125
  }
114
- MenuExpando.propTypes = {
115
- menuItems: PropTypes.oneOfType([
116
- PropTypes.object,
117
- PropTypes.arrayOf(PropTypes.shape({
118
- name: PropTypes.string.isRequired,
119
- path: PropTypes.string.isRequired,
120
- routes: PropTypes.array,
121
- }))
122
- ]).isRequired
123
- };
124
- export function MenuExpandoItem(props) {
125
- return (_jsx("li", { children: _jsx("a", { href: props.href, children: props.name }) }));
126
- }
127
126
  MenuExpandoItem.propTypes = {
128
127
  name: PropTypes.string.isRequired,
129
128
  href: PropTypes.string.isRequired
130
129
  };
131
- export function MenuExpandoButton() {
130
+ export function MenuExpandoItem(props) {
131
+ return (_jsx("li", { children: _jsx("a", { href: props.href, children: props.name }) }));
132
+ }
133
+ MenuExpandoButton.propTypes = {};
134
+ export function MenuExpandoButton(props) {
132
135
  function handleMenuExpandoButtonClick(event) {
133
136
  event.preventDefault();
134
137
  event.stopPropagation();
@@ -144,4 +147,3 @@ export function MenuExpandoButton() {
144
147
  }
145
148
  return (_jsx("div", { className: "menuExpandoButton", id: "menuExpandoButton", onClick: handleMenuExpandoButtonClick, onKeyDown: handleKeyDown, tabIndex: 0, role: "button", "aria-label": "Toggle mobile menu", children: _jsx("img", { src: "/images/icons/mobile-menu2.png", title: "Mobile Menu", alt: "Mobile Menu" }) }));
146
149
  }
147
- MenuExpandoButton.propTypes = {};
@@ -3,26 +3,26 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useEffect } from 'react';
4
4
  import PropTypes from "prop-types";
5
5
  import './menu-simple.css';
6
- /* ========== MENU ========== */
6
+ const menuItemShape = PropTypes.shape({
7
+ name: PropTypes.string.isRequired,
8
+ path: PropTypes.string,
9
+ target: PropTypes.string,
10
+ hidden: PropTypes.bool,
11
+ routes: PropTypes.array,
12
+ });
7
13
  MenuSimple.propTypes = {
8
- // menuItems: PropTypes.object.isRequired,
9
- menuItems: PropTypes.arrayOf(PropTypes.shape({
10
- name: PropTypes.string.isRequired,
11
- path: PropTypes.string.isRequired,
12
- target: PropTypes.string,
13
- hidden: PropTypes.bool,
14
- routes: PropTypes.array,
15
- })).isRequired,
14
+ menuItems: PropTypes.arrayOf(menuItemShape).isRequired,
16
15
  };
17
16
  export function MenuSimple(props) {
18
17
  function generateMenuItems() {
19
18
  const myItems = [];
20
19
  for (const itemKey in props.menuItems) {
21
20
  const myItem = props.menuItems[itemKey];
22
- if (myItem && myItem.routes) {
23
- continue;
24
- } // Skip nested routes
25
- myItems.push(_jsx(MenuSimpleItem, { name: myItem.name, path: myItem.path, target: myItem.target || undefined, hidden: myItem.hidden || undefined }, itemKey));
21
+ if (!myItem)
22
+ continue; // Skip null/undefined items
23
+ if (myItem.routes)
24
+ continue; // Skip nested routes
25
+ myItems.push(_jsx(MenuSimpleItem, { name: myItem.name, path: myItem.path || '', target: myItem.target || undefined, hidden: myItem.hidden || undefined }, itemKey));
26
26
  }
27
27
  return myItems;
28
28
  }
@@ -45,7 +45,7 @@ export function MenuSimple(props) {
45
45
  /* ========== MENU ITEM ========== */
46
46
  MenuSimpleItem.propTypes = {
47
47
  name: PropTypes.string.isRequired,
48
- path: PropTypes.string,
48
+ path: PropTypes.string.isRequired,
49
49
  target: PropTypes.string,
50
50
  hidden: PropTypes.bool,
51
51
  routes: PropTypes.array,
@@ -7,7 +7,7 @@ import "./nerdjoke.css";
7
7
  const debug = false;
8
8
  const TIME_LIMIT = 15;
9
9
  NerdJoke.propTypes = {};
10
- export function NerdJoke( /* props: NerdJokeType */) {
10
+ export function NerdJoke(props) {
11
11
  const [joke, setJoke] = useState({ question: "", answer: "" });
12
12
  const timerIntervalRef = useRef(null);
13
13
  const jokeIntervalRef = useRef(null);
@@ -23,7 +23,6 @@ GoogleSearch.propTypes = {
23
23
  };
24
24
  export function GoogleSearch(props) {
25
25
  if (typeof document !== 'undefined') {
26
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
27
26
  const gsearch = (function () {
28
27
  const gcse = document.createElement("script");
29
28
  gcse.type = "text/javascript";
@@ -1,5 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- export function SchemaBlogPosting({ post }) {
2
+ import PropTypes from 'prop-types';
3
+ SchemaBlogPosting.propTypes = {
4
+ post: PropTypes.object.isRequired,
5
+ };
6
+ export function SchemaBlogPosting(props) {
7
+ const { post } = props;
3
8
  return (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: {
4
9
  __html: JSON.stringify(post),
5
10
  } }));
@@ -1,5 +1,38 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- export function RecipeSchema({ recipe }) {
2
+ import PropTypes from 'prop-types';
3
+ /**
4
+ * Recipe Schema Component
5
+ * Generates JSON-LD structured data for recipes
6
+ * https://schema.org/Recipe
7
+ */
8
+ RecipeSchema.propTypes = {
9
+ recipe: PropTypes.shape({
10
+ '@context': PropTypes.string.isRequired,
11
+ '@type': PropTypes.string.isRequired,
12
+ name: PropTypes.string.isRequired,
13
+ description: PropTypes.string,
14
+ author: PropTypes.shape({
15
+ '@type': PropTypes.string.isRequired,
16
+ name: PropTypes.string.isRequired,
17
+ }),
18
+ datePublished: PropTypes.string,
19
+ image: PropTypes.string,
20
+ recipeYield: PropTypes.string,
21
+ prepTime: PropTypes.string,
22
+ cookTime: PropTypes.string,
23
+ totalTime: PropTypes.string,
24
+ recipeCategory: PropTypes.string,
25
+ recipeCuisine: PropTypes.string,
26
+ recipeIngredient: PropTypes.arrayOf(PropTypes.string),
27
+ recipeInstructions: PropTypes.arrayOf(PropTypes.shape({
28
+ '@type': PropTypes.string.isRequired,
29
+ text: PropTypes.string.isRequired,
30
+ })),
31
+ license: PropTypes.string,
32
+ }).isRequired,
33
+ };
34
+ export function RecipeSchema(props) {
35
+ const { recipe } = props;
3
36
  return (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(recipe) } }));
4
37
  }
5
38
  export default RecipeSchema;