@pixelated-tech/components 3.4.3 → 3.5.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.
- package/README.md +12 -191
- package/dist/components/admin/componentusage/componentAnalysis.js +12 -4
- package/dist/components/admin/componentusage/componentDiscovery.js +20 -6
- package/dist/components/admin/site-health/site-health-accessibility.js +5 -1
- package/dist/components/admin/site-health/site-health-axe-core.js +4 -0
- package/dist/components/admin/site-health/site-health-cloudwatch.integration.js +0 -5
- package/dist/components/admin/site-health/site-health-cloudwatch.js +7 -1
- package/dist/components/admin/site-health/site-health-core-web-vitals.integration.js +3 -3
- package/dist/components/admin/site-health/site-health-dependency-vulnerabilities.js +4 -0
- package/dist/components/admin/site-health/site-health-github.js +8 -2
- package/dist/components/admin/site-health/site-health-google-analytics.js +6 -0
- package/dist/components/admin/site-health/site-health-google-search-console.js +6 -0
- package/dist/components/admin/site-health/site-health-on-site-seo.integration.js +128 -55
- package/dist/components/admin/site-health/site-health-on-site-seo.js +4 -0
- package/dist/components/admin/site-health/site-health-overview.js +11 -4
- package/dist/components/admin/site-health/site-health-performance.js +4 -0
- package/dist/components/admin/site-health/site-health-security.js +7 -3
- package/dist/components/admin/site-health/site-health-seo.js +5 -1
- package/dist/components/admin/site-health/site-health-template.js +20 -9
- package/dist/components/admin/site-health/site-health-uptime.js +4 -0
- package/dist/components/callout/callout.js +0 -10
- package/dist/components/carousel/carousel.js +15 -4
- package/dist/components/carousel/tiles.js +1 -1
- package/dist/components/cms/contentful.items.components.js +3 -4
- package/dist/components/cms/flickr.js +1 -1
- package/dist/components/cms/google.reviews.components.js +3 -3
- package/dist/components/cms/instagram.components.js +15 -5
- package/dist/components/cms/smartimage.js +2 -2
- package/dist/components/cms/wordpress.components.js +32 -6
- package/dist/components/cms/yelp.js +5 -0
- package/dist/components/config/config.server.js +7 -1
- package/dist/components/general/accordion.js +4 -3
- package/dist/components/general/css.js +0 -1
- package/dist/components/general/image.js +0 -1
- package/dist/components/general/loading.js +2 -1
- package/dist/components/general/microinteractions.js +0 -1
- package/dist/components/general/modal.css +2 -4
- package/dist/components/general/modal.js +72 -30
- package/dist/components/general/sidepanel.js +16 -0
- package/dist/components/general/tab.js +1 -0
- package/dist/components/menu/menu-accordion.css +1 -1
- package/dist/components/menu/menu-accordion.js +15 -4
- package/dist/components/menu/menu-expando.js +21 -19
- package/dist/components/menu/menu-simple.js +14 -14
- package/dist/components/nerdjoke/nerdjoke.js +1 -1
- package/dist/components/seo/faq-accordion.css +125 -0
- package/dist/components/seo/faq-accordion.js +56 -0
- package/dist/components/seo/googlesearch.js +0 -1
- package/dist/components/seo/schema-blogposting.js +6 -1
- package/dist/components/seo/schema-faq.js +6 -0
- package/dist/components/seo/schema-recipe.js +34 -1
- package/dist/components/seo/schema-services.js +20 -2
- package/dist/components/shoppingcart/ebay.components.js +3 -3
- package/dist/components/shoppingcart/shoppingcart.components.js +76 -28
- package/dist/components/shoppingcart/shoppingcart.functions.js +4 -4
- package/dist/components/sitebuilder/config/CompoundFontSelector.js +13 -4
- package/dist/components/sitebuilder/config/ConfigBuilder.css +194 -5
- package/dist/components/sitebuilder/config/ConfigBuilder.js +183 -17
- package/dist/components/sitebuilder/config/FontSelector.js +13 -2
- package/dist/components/sitebuilder/config/routes-form.json +67 -0
- package/dist/components/sitebuilder/config/siteinfo-form.json +28 -14
- package/dist/components/sitebuilder/config/visualdesignform.json +4 -4
- package/dist/components/sitebuilder/form/formbuilder.js +1 -0
- package/dist/components/sitebuilder/form/formcomponents.js +2 -3
- package/dist/components/sitebuilder/form/formengine.js +6 -5
- package/dist/components/sitebuilder/form/formvalidator.js +5 -0
- package/dist/components/sitebuilder/page/components/PageBuilderUI.js +5 -1
- package/dist/components/structured/buzzwordbingo.css +0 -1
- package/dist/components/structured/recipe.js +1 -1
- package/dist/components/structured/socialcard.js +2 -2
- package/dist/components/utilities/functions.js +82 -1
- package/dist/components/utilities/gemini-api.client.js +76 -0
- package/dist/components/utilities/gemini-api.server.js +185 -0
- package/dist/data/routes.json +5 -5
- package/dist/index.adminclient.js +30 -0
- package/dist/index.adminserver.js +19 -0
- package/dist/index.js +11 -18
- package/dist/index.server.js +16 -28
- package/dist/types/components/admin/componentusage/componentAnalysis.d.ts.map +1 -1
- package/dist/types/components/admin/componentusage/componentDiscovery.d.ts +1 -1
- package/dist/types/components/admin/componentusage/componentDiscovery.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-accessibility.d.ts +7 -4
- package/dist/types/components/admin/site-health/site-health-accessibility.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-axe-core.d.ts +7 -4
- package/dist/types/components/admin/site-health/site-health-axe-core.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-cloudwatch.d.ts +9 -6
- package/dist/types/components/admin/site-health/site-health-cloudwatch.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-cloudwatch.integration.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-dependency-vulnerabilities.d.ts +7 -4
- package/dist/types/components/admin/site-health/site-health-dependency-vulnerabilities.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-github.d.ts +9 -6
- package/dist/types/components/admin/site-health/site-health-github.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-google-analytics.d.ts +9 -6
- package/dist/types/components/admin/site-health/site-health-google-analytics.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-google-search-console.d.ts +9 -6
- package/dist/types/components/admin/site-health/site-health-google-search-console.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-on-site-seo.d.ts +8 -3
- package/dist/types/components/admin/site-health/site-health-on-site-seo.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-on-site-seo.integration.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-overview.d.ts +7 -4
- package/dist/types/components/admin/site-health/site-health-overview.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-performance.d.ts +7 -4
- package/dist/types/components/admin/site-health/site-health-performance.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-security.d.ts +7 -4
- package/dist/types/components/admin/site-health/site-health-security.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-seo.d.ts +7 -4
- package/dist/types/components/admin/site-health/site-health-seo.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-template.d.ts +13 -10
- package/dist/types/components/admin/site-health/site-health-template.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-types.d.ts +0 -21
- package/dist/types/components/admin/site-health/site-health-types.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-uptime.d.ts +7 -4
- package/dist/types/components/admin/site-health/site-health-uptime.d.ts.map +1 -1
- package/dist/types/components/callout/callout.d.ts +3 -3
- package/dist/types/components/callout/callout.d.ts.map +1 -1
- package/dist/types/components/carousel/carousel.d.ts +16 -7
- package/dist/types/components/carousel/carousel.d.ts.map +1 -1
- package/dist/types/components/carousel/tiles.d.ts +3 -6
- package/dist/types/components/carousel/tiles.d.ts.map +1 -1
- package/dist/types/components/cms/flickr.d.ts +3 -6
- package/dist/types/components/cms/flickr.d.ts.map +1 -1
- package/dist/types/components/cms/google.reviews.components.d.ts +1 -7
- package/dist/types/components/cms/google.reviews.components.d.ts.map +1 -1
- package/dist/types/components/cms/hubspot.components.d.ts +1 -2
- package/dist/types/components/cms/hubspot.components.d.ts.map +1 -1
- package/dist/types/components/cms/instagram.components.d.ts +14 -9
- package/dist/types/components/cms/instagram.components.d.ts.map +1 -1
- package/dist/types/components/cms/smartimage.d.ts +2 -28
- package/dist/types/components/cms/smartimage.d.ts.map +1 -1
- package/dist/types/components/cms/wordpress.components.d.ts +33 -14
- package/dist/types/components/cms/wordpress.components.d.ts.map +1 -1
- package/dist/types/components/cms/yelp.d.ts +9 -4
- package/dist/types/components/cms/yelp.d.ts.map +1 -1
- package/dist/types/components/config/config.server.d.ts +9 -6
- package/dist/types/components/config/config.server.d.ts.map +1 -1
- package/dist/types/components/general/accordion.d.ts +3 -2
- package/dist/types/components/general/accordion.d.ts.map +1 -1
- package/dist/types/components/general/loading.d.ts +5 -1
- package/dist/types/components/general/loading.d.ts.map +1 -1
- package/dist/types/components/general/microinteractions.d.ts +1 -3
- package/dist/types/components/general/microinteractions.d.ts.map +1 -1
- package/dist/types/components/general/modal.d.ts +11 -5
- package/dist/types/components/general/modal.d.ts.map +1 -1
- package/dist/types/components/general/semantic.d.ts +3 -3
- package/dist/types/components/general/sidepanel.d.ts +20 -13
- package/dist/types/components/general/sidepanel.d.ts.map +1 -1
- package/dist/types/components/general/tab.d.ts +1 -2
- package/dist/types/components/general/tab.d.ts.map +1 -1
- package/dist/types/components/menu/menu-accordion.d.ts +22 -9
- package/dist/types/components/menu/menu-accordion.d.ts.map +1 -1
- package/dist/types/components/menu/menu-expando.d.ts +14 -5
- package/dist/types/components/menu/menu-expando.d.ts.map +1 -1
- package/dist/types/components/menu/menu-simple.d.ts +4 -5
- package/dist/types/components/menu/menu-simple.d.ts.map +1 -1
- package/dist/types/components/nerdjoke/nerdjoke.d.ts +1 -1
- package/dist/types/components/nerdjoke/nerdjoke.d.ts.map +1 -1
- package/dist/types/components/seo/faq-accordion.d.ts +18 -0
- package/dist/types/components/seo/faq-accordion.d.ts.map +1 -0
- package/dist/types/components/seo/googleanalytics.d.ts.map +1 -1
- package/dist/types/components/seo/metadata.components.d.ts +2 -2
- package/dist/types/components/seo/metadata.components.d.ts.map +1 -1
- package/dist/types/components/seo/schema-blogposting.d.ts +7 -4
- package/dist/types/components/seo/schema-blogposting.d.ts.map +1 -1
- package/dist/types/components/seo/schema-faq.d.ts +6 -0
- package/dist/types/components/seo/schema-faq.d.ts.map +1 -0
- package/dist/types/components/seo/schema-recipe.d.ts +29 -30
- package/dist/types/components/seo/schema-recipe.d.ts.map +1 -1
- package/dist/types/components/seo/schema-services.d.ts +19 -9
- package/dist/types/components/seo/schema-services.d.ts.map +1 -1
- package/dist/types/components/shoppingcart/paypal.d.ts +1 -1
- package/dist/types/components/shoppingcart/paypal.d.ts.map +1 -1
- package/dist/types/components/shoppingcart/shoppingcart.components.d.ts +77 -28
- package/dist/types/components/shoppingcart/shoppingcart.components.d.ts.map +1 -1
- package/dist/types/components/shoppingcart/shoppingcart.functions.d.ts +4 -23
- package/dist/types/components/shoppingcart/shoppingcart.functions.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/config/CompoundFontSelector.d.ts +10 -11
- package/dist/types/components/sitebuilder/config/CompoundFontSelector.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts +41 -174
- package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/config/FontSelector.d.ts +12 -13
- package/dist/types/components/sitebuilder/config/FontSelector.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/form/formbuilder.d.ts +7 -3
- package/dist/types/components/sitebuilder/form/formbuilder.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/form/formcomponents.d.ts +1 -1
- package/dist/types/components/sitebuilder/form/formcomponents.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/form/formengine.d.ts +1 -2
- package/dist/types/components/sitebuilder/form/formengine.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/form/formextractor.d.ts +5 -4
- package/dist/types/components/sitebuilder/form/formextractor.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/form/formtypes.d.ts +3 -3
- package/dist/types/components/sitebuilder/form/formtypes.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/form/formvalidator.d.ts +8 -3
- package/dist/types/components/sitebuilder/form/formvalidator.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/page/components/ComponentPropertiesForm.d.ts +2 -3
- package/dist/types/components/sitebuilder/page/components/ComponentPropertiesForm.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/page/components/ComponentSelector.d.ts +2 -3
- package/dist/types/components/sitebuilder/page/components/ComponentSelector.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/page/components/ComponentTree.d.ts +2 -3
- package/dist/types/components/sitebuilder/page/components/ComponentTree.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/page/components/PageBuilderUI.d.ts +8 -7
- package/dist/types/components/sitebuilder/page/components/PageBuilderUI.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/page/components/PageEngine.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/page/components/SaveLoadSection.d.ts +2 -3
- package/dist/types/components/sitebuilder/page/components/SaveLoadSection.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/page/lib/componentMap.d.ts +1 -1
- package/dist/types/components/structured/markdown.d.ts +1 -3
- package/dist/types/components/structured/markdown.d.ts.map +1 -1
- package/dist/types/components/structured/recipe.d.ts +5 -32
- package/dist/types/components/structured/recipe.d.ts.map +1 -1
- package/dist/types/components/structured/socialcard.d.ts +4 -0
- package/dist/types/components/structured/socialcard.d.ts.map +1 -1
- package/dist/types/components/structured/timeline.d.ts +1 -3
- package/dist/types/components/structured/timeline.d.ts.map +1 -1
- package/dist/types/components/utilities/functions.d.ts +20 -0
- package/dist/types/components/utilities/functions.d.ts.map +1 -1
- package/dist/types/components/utilities/gemini-api.client.d.ts +38 -0
- package/dist/types/components/utilities/gemini-api.client.d.ts.map +1 -0
- package/dist/types/components/utilities/gemini-api.server.d.ts +17 -0
- package/dist/types/components/utilities/gemini-api.server.d.ts.map +1 -0
- package/dist/types/index.adminclient.d.ts +27 -0
- package/dist/types/index.adminclient.d.ts.map +1 -0
- package/dist/types/index.adminserver.d.ts +20 -0
- package/dist/types/index.adminserver.d.ts.map +1 -0
- package/dist/types/index.d.ts +11 -18
- package/dist/types/index.server.d.ts +6 -28
- package/dist/types/stories/general/sidepanel.stories.d.ts.map +1 -1
- package/dist/types/stories/general/smartimage.stories.d.ts +74 -2
- package/dist/types/stories/general/smartimage.stories.d.ts.map +1 -1
- package/package.json +19 -9
- package/README.COMPONENTS.md +0 -2310
- package/dist/components/cms/pixelated.linkedin.js +0 -180
- package/dist/components/cms/pixelated.linkedin1.js +0 -84
- package/dist/components/cms/pixelated.linkedin2.js +0 -92
- package/dist/types/components/cms/pixelated.linkedin.d.ts +0 -2
- package/dist/types/components/cms/pixelated.linkedin.d.ts.map +0 -1
- package/dist/types/components/cms/pixelated.linkedin1.d.ts +0 -2
- package/dist/types/components/cms/pixelated.linkedin1.d.ts.map +0 -1
- package/dist/types/components/cms/pixelated.linkedin2.d.ts +0 -2
- package/dist/types/components/cms/pixelated.linkedin2.d.ts.map +0 -1
- package/dist/types/tests/pixelated.menu-expando.test.d.ts +0 -2
- 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)
|
|
84
|
-
newProps.height = parseNumber(height)
|
|
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
|
|
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
|
|
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
|
-
|
|
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;
|
|
@@ -5,10 +5,11 @@ import './accordion.css';
|
|
|
5
5
|
Accordion.propTypes = {
|
|
6
6
|
items: PropTypes.arrayOf(PropTypes.shape({
|
|
7
7
|
title: PropTypes.string.isRequired,
|
|
8
|
-
content: PropTypes.oneOfType([PropTypes.string, PropTypes.node])
|
|
8
|
+
content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
|
9
9
|
open: PropTypes.bool,
|
|
10
10
|
})).isRequired,
|
|
11
|
+
onToggle: PropTypes.func,
|
|
11
12
|
};
|
|
12
|
-
export function Accordion({ items }) {
|
|
13
|
-
return (_jsx("div", { className: "accordion", children: items?.map((item, index) => (item ? (_jsxs("details", { className: "accordion-item", open: item.open ?? undefined, children: [_jsx("summary", { className: "accordion-title", children: _jsx("h3", { id: `accordion-header-${index}`, children: item.title }) }), _jsx("div", { className: "accordion-content", role: "region", "aria-labelledby": `accordion-header-${index}`, children: typeof item.content === 'string' ? (_jsx("p", { children: item.content })) : (item.content) })] }, index)) : null)) }));
|
|
13
|
+
export function Accordion({ items, onToggle }) {
|
|
14
|
+
return (_jsx("div", { className: "accordion", children: items?.map((item, index) => (item ? (_jsxs("details", { className: "accordion-item", open: item.open ?? undefined, onToggle: onToggle ? (e) => onToggle(index, e.target.open) : undefined, children: [_jsx("summary", { className: "accordion-title", children: _jsx("h3", { id: `accordion-header-${index}`, children: item.title }) }), _jsx("div", { className: "accordion-content", role: "region", "aria-labelledby": `accordion-header-${index}`, children: typeof item.content === 'string' ? (_jsx("p", { children: item.content })) : (item.content) })] }, index)) : null)) }));
|
|
14
15
|
}
|
|
@@ -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
|
-
|
|
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:
|
|
7
|
-
left:
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
myModal
|
|
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 =
|
|
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.
|
|
31
|
+
myModalClose.addEventListener('click', handleModalClose);
|
|
38
32
|
}
|
|
39
33
|
;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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, onKeyDown: handleModalKeyDown, tabIndex: -1, "aria-label": "Modal overlay", 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);
|
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
23
|
-
continue;
|
|
24
|
-
|
|
25
|
-
|
|
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(
|
|
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);
|