@pixelated-tech/components 3.13.14 → 3.13.16
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/dist/components/general/schema.functions.js +114 -0
- package/dist/components/general/schema.js +662 -0
- package/dist/components/general/sitemap.js +74 -31
- package/dist/components/general/utilities.js +38 -0
- package/dist/components/integrations/spotify.components.js +43 -0
- package/dist/components/integrations/spotify.functions.js +111 -0
- package/dist/components/integrations/wordpress.components.js +2 -2
- package/dist/config/pixelated.config.json.enc +1 -1
- package/dist/index.js +7 -13
- package/dist/index.server.js +3 -1
- package/dist/scripts/release.sh +9 -15
- package/dist/scripts/update.sh +45 -8
- package/dist/types/components/general/schema.d.ts +266 -0
- package/dist/types/components/general/schema.d.ts.map +1 -0
- package/dist/types/components/general/schema.functions.d.ts +77 -0
- package/dist/types/components/general/schema.functions.d.ts.map +1 -0
- package/dist/types/components/general/sitemap.d.ts +4 -4
- package/dist/types/components/general/sitemap.d.ts.map +1 -1
- package/dist/types/components/general/utilities.d.ts +2 -0
- package/dist/types/components/general/utilities.d.ts.map +1 -1
- package/dist/types/components/integrations/spotify.components.d.ts +27 -0
- package/dist/types/components/integrations/spotify.components.d.ts.map +1 -0
- package/dist/types/components/integrations/spotify.functions.d.ts +57 -0
- package/dist/types/components/integrations/spotify.functions.d.ts.map +1 -0
- package/dist/types/index.d.ts +7 -13
- package/dist/types/index.server.d.ts +3 -1
- package/dist/types/stories/general/schema.stories.d.ts +1 -1
- package/dist/types/stories/general/schema.stories.d.ts.map +1 -1
- package/dist/types/stories/integrations/loremipsum.stories.d.ts +1 -1
- package/dist/types/stories/integrations/schema-podcast.stories.d.ts +45 -0
- package/dist/types/stories/integrations/schema-podcast.stories.d.ts.map +1 -0
- package/dist/types/stories/integrations/spotify.stories.d.ts +19 -0
- package/dist/types/stories/integrations/spotify.stories.d.ts.map +1 -0
- package/dist/types/tests/schema-podcast.test.d.ts +2 -0
- package/dist/types/tests/schema-podcast.test.d.ts.map +1 -0
- package/dist/types/tests/spotify.test.d.ts +2 -0
- package/dist/types/tests/spotify.test.d.ts.map +1 -0
- package/package.json +46 -42
- package/dist/components/general/schema-blogposting.functions.js +0 -44
- package/dist/components/general/schema-blogposting.js +0 -18
- package/dist/components/general/schema-breadcrumb.js +0 -78
- package/dist/components/general/schema-faq.js +0 -38
- package/dist/components/general/schema-localbusiness.js +0 -125
- package/dist/components/general/schema-product.js +0 -51
- package/dist/components/general/schema-recipe.js +0 -58
- package/dist/components/general/schema-review.js +0 -47
- package/dist/components/general/schema-services.js +0 -79
- package/dist/components/general/schema-website.js +0 -148
- package/dist/types/components/general/schema-blogposting.d.ts +0 -10
- package/dist/types/components/general/schema-blogposting.d.ts.map +0 -1
- package/dist/types/components/general/schema-blogposting.functions.d.ts +0 -27
- package/dist/types/components/general/schema-blogposting.functions.d.ts.map +0 -1
- package/dist/types/components/general/schema-breadcrumb.d.ts +0 -14
- package/dist/types/components/general/schema-breadcrumb.d.ts.map +0 -1
- package/dist/types/components/general/schema-faq.d.ts +0 -10
- package/dist/types/components/general/schema-faq.d.ts.map +0 -1
- package/dist/types/components/general/schema-localbusiness.d.ts +0 -53
- package/dist/types/components/general/schema-localbusiness.d.ts.map +0 -1
- package/dist/types/components/general/schema-product.d.ts +0 -38
- package/dist/types/components/general/schema-product.d.ts.map +0 -1
- package/dist/types/components/general/schema-recipe.d.ts +0 -33
- package/dist/types/components/general/schema-recipe.d.ts.map +0 -1
- package/dist/types/components/general/schema-review.d.ts +0 -34
- package/dist/types/components/general/schema-review.d.ts.map +0 -1
- package/dist/types/components/general/schema-services.d.ts +0 -36
- package/dist/types/components/general/schema-services.d.ts.map +0 -1
- package/dist/types/components/general/schema-website.d.ts +0 -40
- package/dist/types/components/general/schema-website.d.ts.map +0 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PodcastEpisodeList } from '@/components/integrations/spotify.components';
|
|
2
|
+
declare const _default: {
|
|
3
|
+
title: string;
|
|
4
|
+
component: typeof PodcastEpisodeList;
|
|
5
|
+
argTypes: {
|
|
6
|
+
episodes: {
|
|
7
|
+
description: string;
|
|
8
|
+
control: {
|
|
9
|
+
type: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export default _default;
|
|
15
|
+
export declare const Default: any;
|
|
16
|
+
export declare const SingleEpisode: any;
|
|
17
|
+
export declare const MultipleEpisodes: any;
|
|
18
|
+
export declare const EmptyList: any;
|
|
19
|
+
//# sourceMappingURL=spotify.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spotify.stories.d.ts","sourceRoot":"","sources":["../../../../src/stories/integrations/spotify.stories.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAA+B,MAAM,8CAA8C,CAAC;;;;;;;;;;;;;AAG/G,wBASE;AA6CF,eAAO,MAAM,OAAO,EAAE,GAAuB,CAAC;AAM9C,eAAO,MAAM,aAAa,EAAE,GAAuB,CAAC;AAMpD,eAAO,MAAM,gBAAgB,EAAE,GAAuB,CAAC;AAMvD,eAAO,MAAM,SAAS,EAAE,GAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-podcast.test.d.ts","sourceRoot":"","sources":["../../../src/tests/schema-podcast.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spotify.test.d.ts","sourceRoot":"","sources":["../../../src/tests/spotify.test.tsx"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixelated-tech/components",
|
|
3
|
-
"version": "3.13.
|
|
3
|
+
"version": "3.13.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": {
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"test:coverage": "vitest run --coverage",
|
|
82
82
|
"test:watch": "vitest",
|
|
83
83
|
"lint": "eslint --fix",
|
|
84
|
+
"release:prep": "npm run update && npm run lint && npm run test && npm run build && npm run buildStorybook && npm run copy2all",
|
|
84
85
|
"release": "./src/scripts/release.sh",
|
|
85
86
|
"vault": "npx tsx src/scripts/config-vault.ts",
|
|
86
87
|
"config:vault": "npm run vault --",
|
|
@@ -112,89 +113,92 @@
|
|
|
112
113
|
"html-entities": "^2.6.0"
|
|
113
114
|
},
|
|
114
115
|
"devDependencies": {
|
|
115
|
-
"@aws-sdk/client-amplify": "^3.
|
|
116
|
-
"@aws-sdk/client-cloudwatch": "^3.
|
|
117
|
-
"@aws-sdk/client-iam": "^3.
|
|
118
|
-
"@aws-sdk/client-route-53": "^3.
|
|
119
|
-
"@aws-sdk/xml-builder": "^3.972.
|
|
116
|
+
"@aws-sdk/client-amplify": "^3.1010.0",
|
|
117
|
+
"@aws-sdk/client-cloudwatch": "^3.1010.0",
|
|
118
|
+
"@aws-sdk/client-iam": "^3.1010.0",
|
|
119
|
+
"@aws-sdk/client-route-53": "^3.1010.0",
|
|
120
|
+
"@aws-sdk/xml-builder": "^3.972.11",
|
|
120
121
|
"@babel/core": "^7.29.0",
|
|
121
122
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
122
123
|
"@babel/plugin-proposal-object-rest-spread": "^7.20.7",
|
|
123
|
-
"@babel/preset-env": "^7.29.
|
|
124
|
+
"@babel/preset-env": "^7.29.2",
|
|
124
125
|
"@babel/preset-react": "^7.28.5",
|
|
125
126
|
"@babel/preset-typescript": "^7.28.5",
|
|
126
|
-
"@eslint/js": "
|
|
127
|
-
"@storybook/addon-a11y": "^10.2.
|
|
128
|
-
"@storybook/addon-docs": "^10.2.
|
|
127
|
+
"@eslint/js": "9.39.4",
|
|
128
|
+
"@storybook/addon-a11y": "^10.2.19",
|
|
129
|
+
"@storybook/addon-docs": "^10.2.19",
|
|
129
130
|
"@storybook/addon-webpack5-compiler-babel": "^4.0.0",
|
|
130
131
|
"@storybook/preset-scss": "^1.0.3",
|
|
131
|
-
"@storybook/react-webpack5": "^10.2.
|
|
132
|
+
"@storybook/react-webpack5": "^10.2.19",
|
|
132
133
|
"@testing-library/dom": "^10.4.1",
|
|
133
134
|
"@testing-library/react": "^16.3.2",
|
|
134
135
|
"@testing-library/user-event": "^14.6.1",
|
|
135
136
|
"@types/md5": "^2.3.6",
|
|
136
|
-
"@types/node": "^25.
|
|
137
|
+
"@types/node": "^25.5.0",
|
|
137
138
|
"@types/prop-types": "^15.7.15",
|
|
138
139
|
"@types/react": "^19.2.14",
|
|
139
140
|
"@types/react-dom": "^19.2.3",
|
|
140
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
141
|
-
"@typescript-eslint/parser": "^8.
|
|
142
|
-
"@vitejs/plugin-react": "^
|
|
143
|
-
"@vitest/coverage-v8": "^4.0
|
|
144
|
-
"@vitest/ui": "^4.0
|
|
141
|
+
"@typescript-eslint/eslint-plugin": "^8.57.1",
|
|
142
|
+
"@typescript-eslint/parser": "^8.57.1",
|
|
143
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
144
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
145
|
+
"@vitest/ui": "^4.1.0",
|
|
145
146
|
"ajv": "^8.18.0",
|
|
146
147
|
"ajv-keywords": "^5.1.0",
|
|
147
|
-
"babel-loader": "^10.1.
|
|
148
|
-
"clean-webpack-plugin": "^
|
|
149
|
-
"copy-webpack-plugin": "^
|
|
148
|
+
"babel-loader": "^10.1.1",
|
|
149
|
+
"clean-webpack-plugin": "^4.0.0",
|
|
150
|
+
"copy-webpack-plugin": "^14.0.0",
|
|
150
151
|
"css-loader": "^7.1.4",
|
|
151
|
-
"eslint": "
|
|
152
|
-
"eslint-plugin-import": "^
|
|
152
|
+
"eslint": "9.39.4",
|
|
153
|
+
"eslint-plugin-import": "^2.32.0",
|
|
153
154
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
154
155
|
"eslint-plugin-react": "^7.37.5",
|
|
155
156
|
"file-loader": "^6.2.0",
|
|
156
|
-
"happy-dom": "^20.8.
|
|
157
|
-
"jsdom": "^
|
|
158
|
-
"less-loader": "^12.3.
|
|
159
|
-
"mini-css-extract-plugin": "^2.10.
|
|
160
|
-
"next": "
|
|
157
|
+
"happy-dom": "^20.8.4",
|
|
158
|
+
"jsdom": "^29.0.0",
|
|
159
|
+
"less-loader": "^12.3.2",
|
|
160
|
+
"mini-css-extract-plugin": "^2.10.1",
|
|
161
|
+
"next": "16.1.7",
|
|
161
162
|
"null-loader": "^4.0.1",
|
|
162
163
|
"prop-types": "^15.8.1",
|
|
163
|
-
"react": "
|
|
164
|
-
"react-dom": "
|
|
164
|
+
"react": "19.2.4",
|
|
165
|
+
"react-dom": "19.2.4",
|
|
165
166
|
"react-redux": "^9.2.0",
|
|
166
167
|
"react-test-renderer": "^19.2.4",
|
|
167
168
|
"redux": "^5.0.1",
|
|
168
|
-
"sass": "^1.
|
|
169
|
+
"sass": "^1.98.0",
|
|
169
170
|
"sass-loader": "^16.0.7",
|
|
170
|
-
"storybook": "^10.2.
|
|
171
|
+
"storybook": "^10.2.19",
|
|
171
172
|
"style-loader": "^4.0.0",
|
|
172
173
|
"ts-loader": "^9.5.4",
|
|
173
174
|
"typescript": "^5.9.3",
|
|
174
175
|
"url-loader": "^4.1.1",
|
|
175
|
-
"vitest": "^4.0
|
|
176
|
+
"vitest": "^4.1.0",
|
|
176
177
|
"webpack": "^5.105.4",
|
|
177
|
-
"webpack-cli": "^
|
|
178
|
+
"webpack-cli": "^7.0.2",
|
|
178
179
|
"webpack-dev-server": "^5.2.3",
|
|
179
180
|
"webpack-node-externals": "^3.0.0"
|
|
180
181
|
},
|
|
181
182
|
"peerDependencies": {
|
|
182
|
-
"next": "^16.
|
|
183
|
-
"react": "^19.2.
|
|
184
|
-
"react-dom": "^19.2.
|
|
183
|
+
"next": "^16.1.7",
|
|
184
|
+
"react": "^19.2.4",
|
|
185
|
+
"react-dom": "^19.2.4"
|
|
185
186
|
},
|
|
186
187
|
"optionalDependencies": {
|
|
187
188
|
"@aws-sdk/client-cloudwatch": "^3.996.0",
|
|
188
189
|
"@aws-sdk/client-route-53": "^3.893.0",
|
|
189
|
-
"
|
|
190
|
-
"
|
|
191
|
-
"
|
|
192
|
-
"eslint
|
|
190
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
191
|
+
"clean-webpack-plugin": "^4.0.0",
|
|
192
|
+
"copy-webpack-plugin": "^14.0.0",
|
|
193
|
+
"eslint": "9.39.4",
|
|
194
|
+
"eslint-plugin-import": "^2.32.0",
|
|
193
195
|
"googleapis": "^171.4.0",
|
|
196
|
+
"jsdom": "^29.0.0",
|
|
194
197
|
"md5": "^2.3.0",
|
|
195
|
-
"puppeteer": "^24.
|
|
198
|
+
"puppeteer": "^24.39.1",
|
|
196
199
|
"react-redux": "*",
|
|
197
200
|
"recharts": "^3.8.0",
|
|
198
|
-
"redux": "*"
|
|
201
|
+
"redux": "*",
|
|
202
|
+
"webpack-cli": "^7.0.2"
|
|
199
203
|
}
|
|
200
204
|
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { decode } from 'html-entities';
|
|
2
|
-
/**
|
|
3
|
-
* Converts WordPress REST API blog post to schema.org BlogPosting format
|
|
4
|
-
* @param post WordPress blog post
|
|
5
|
-
* @param includeFullContent Whether to include articleBody (true) or just description (false)
|
|
6
|
-
*/
|
|
7
|
-
export function mapWordPressToBlogPosting(post, includeFullContent = false) {
|
|
8
|
-
const cleanContent = (content) => {
|
|
9
|
-
if (!content)
|
|
10
|
-
return '';
|
|
11
|
-
// Strip HTML tags and decode all HTML entities
|
|
12
|
-
const stripped = content.replace(/<[^>]*>/g, '');
|
|
13
|
-
return decode(stripped).replace(/\[…\]/g, '').trim();
|
|
14
|
-
};
|
|
15
|
-
const description = cleanContent(post.excerpt);
|
|
16
|
-
const articleBody = includeFullContent ? cleanContent(post.content || '') : undefined;
|
|
17
|
-
const schema = {
|
|
18
|
-
'@context': 'https://schema.org',
|
|
19
|
-
'@type': 'BlogPosting',
|
|
20
|
-
url: post.URL,
|
|
21
|
-
headline: decode(post.title.replace(/<[^>]*>/g, '')),
|
|
22
|
-
description: description || decode(post.title.replace(/<[^>]*>/g, '')),
|
|
23
|
-
datePublished: post.date,
|
|
24
|
-
image: post.featured_image || post.post_thumbnail?.URL,
|
|
25
|
-
articleSection: Array.isArray(post.categories) && post.categories.length > 0
|
|
26
|
-
? post.categories[0]
|
|
27
|
-
: 'Blog',
|
|
28
|
-
keywords: Array.isArray(post.categories) ? post.categories : [],
|
|
29
|
-
};
|
|
30
|
-
if (articleBody) {
|
|
31
|
-
schema.articleBody = articleBody;
|
|
32
|
-
}
|
|
33
|
-
if (post.modified) {
|
|
34
|
-
schema.dateModified = post.modified;
|
|
35
|
-
}
|
|
36
|
-
if (post.author) {
|
|
37
|
-
schema.author = {
|
|
38
|
-
'@type': 'Person',
|
|
39
|
-
name: post.author.name,
|
|
40
|
-
url: `https://${new URL(post.URL).hostname}/author/${post.author.login}`,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
return schema;
|
|
44
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
|
-
/**
|
|
5
|
-
* SchemaBlogPosting — Inject a JSON-LD <script> tag containing a BlogPosting schema object.
|
|
6
|
-
*
|
|
7
|
-
* @param {object} [props.post] - Structured JSON-LD object representing a blog post (BlogPosting schema).
|
|
8
|
-
*/
|
|
9
|
-
SchemaBlogPosting.propTypes = {
|
|
10
|
-
/** Structured BlogPosting JSON-LD object */
|
|
11
|
-
post: PropTypes.object.isRequired,
|
|
12
|
-
};
|
|
13
|
-
export function SchemaBlogPosting(props) {
|
|
14
|
-
const { post } = props;
|
|
15
|
-
return (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: {
|
|
16
|
-
__html: JSON.stringify(post),
|
|
17
|
-
} }));
|
|
18
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
|
-
/**
|
|
5
|
-
* Build breadcrumb trail from root to current path.
|
|
6
|
-
* e.g., "/store/item-slug" produces ["/", "/store", "/store/item-slug"]
|
|
7
|
-
*/
|
|
8
|
-
function buildPathSegments(currentPath) {
|
|
9
|
-
const segments = ['/'];
|
|
10
|
-
if (currentPath === '/')
|
|
11
|
-
return segments;
|
|
12
|
-
const parts = currentPath.split('/').filter(Boolean);
|
|
13
|
-
let accumulated = '';
|
|
14
|
-
for (const part of parts) {
|
|
15
|
-
accumulated += '/' + part;
|
|
16
|
-
segments.push(accumulated);
|
|
17
|
-
}
|
|
18
|
-
return segments;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Determine breadcrumb name for a path segment.
|
|
22
|
-
* Uses route name if exact match found, otherwise uses humanized path segment.
|
|
23
|
-
*/
|
|
24
|
-
function getSegmentName(routes, path, segment) {
|
|
25
|
-
if (path === '/')
|
|
26
|
-
return 'Home';
|
|
27
|
-
// Only use exact route matches with valid paths to avoid duplicating parent breadcrumb names
|
|
28
|
-
const route = routes.find((r) => r.path && r.path === path);
|
|
29
|
-
if (route)
|
|
30
|
-
return route.name || segment;
|
|
31
|
-
// Fallback: humanize the path segment
|
|
32
|
-
return segment
|
|
33
|
-
.split('-')
|
|
34
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
35
|
-
.join(' ');
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* BreadcrumbListSchema — auto-generates a breadcrumb list as JSON-LD from routes.json data.
|
|
39
|
-
* Parses the current path, builds breadcrumb trail by matching path segments to routes array,
|
|
40
|
-
* and embeds as schema.org/BreadcrumbList for SEO rich snippets.
|
|
41
|
-
* Accepts flexible route objects from routes.json with any additional properties.
|
|
42
|
-
*
|
|
43
|
-
* @param {array} [props.routes] - Routes array from routes.json with name and optional path properties.
|
|
44
|
-
* @param {string} [props.currentPath] - Current page path (e.g. "/store/vintage-oakley"). Defaults to "/" if not provided.
|
|
45
|
-
* @param {string} [props.siteUrl] - Full domain URL from siteInfo.url. Defaults to https://example.com.
|
|
46
|
-
*/
|
|
47
|
-
BreadcrumbListSchema.propTypes = {
|
|
48
|
-
/** Routes array from routes.json. Accepts routes with any properties; only uses name and path. */
|
|
49
|
-
routes: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
50
|
-
/** Current page path to generate breadcrumbs for (e.g. "/store/item-slug"). Defaults to "/". */
|
|
51
|
-
currentPath: PropTypes.string,
|
|
52
|
-
/** Site domain URL for constructing full breadcrumb URLs. Defaults to https://example.com. */
|
|
53
|
-
siteUrl: PropTypes.string,
|
|
54
|
-
};
|
|
55
|
-
export function BreadcrumbListSchema({ routes, currentPath = '/', siteUrl = 'https://example.com', }) {
|
|
56
|
-
// Type-safe conversion: routes prop is now flexible (accepts any object)
|
|
57
|
-
// Filter to ensure only valid Route objects with 'name' property
|
|
58
|
-
const validRoutes = (Array.isArray(routes)
|
|
59
|
-
? routes.filter((r) => !!(r && typeof r === 'object' && 'name' in r))
|
|
60
|
-
: []);
|
|
61
|
-
const pathSegments = buildPathSegments(currentPath || '/');
|
|
62
|
-
const finalSiteUrl = siteUrl || 'https://example.com';
|
|
63
|
-
const itemListElement = pathSegments.map((path, index) => {
|
|
64
|
-
const segment = path.split('/').filter(Boolean).pop() || 'Home';
|
|
65
|
-
return {
|
|
66
|
-
'@type': 'ListItem',
|
|
67
|
-
'position': index + 1,
|
|
68
|
-
'name': getSegmentName(validRoutes, path, segment),
|
|
69
|
-
'item': `${finalSiteUrl.replace(/\/$/, '')}${path}`,
|
|
70
|
-
};
|
|
71
|
-
});
|
|
72
|
-
const jsonLD = {
|
|
73
|
-
'@context': 'https://schema.org',
|
|
74
|
-
'@type': 'BreadcrumbList',
|
|
75
|
-
'itemListElement': itemListElement,
|
|
76
|
-
};
|
|
77
|
-
return (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(jsonLD) } }));
|
|
78
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
|
-
// normalizeFaqs turns a JSON-LD FAQPage payload into a form where each
|
|
5
|
-
// question has a single `acceptedAnswer.text` string. Some of our data
|
|
6
|
-
// sources (WordPress, CMS exports) allow multiple answer fragments; we
|
|
7
|
-
// merge them here so the final JSON remains valid for search engines.
|
|
8
|
-
function normalizeFaqs(data) {
|
|
9
|
-
if (!data || typeof data !== 'object')
|
|
10
|
-
return data;
|
|
11
|
-
const faqs = JSON.parse(JSON.stringify(data));
|
|
12
|
-
if (Array.isArray(faqs.mainEntity)) {
|
|
13
|
-
faqs.mainEntity.forEach((entry) => {
|
|
14
|
-
if (entry && entry.acceptedAnswer) {
|
|
15
|
-
const ans = entry.acceptedAnswer;
|
|
16
|
-
if (ans && Array.isArray(ans.text)) {
|
|
17
|
-
ans.text = ans.text.join(' ');
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
return faqs;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* SchemaFAQ — Inject a JSON-LD <script> tag containing an FAQPage schema object.
|
|
26
|
-
*
|
|
27
|
-
* @param {object} [props.faqsData] - Structured JSON-LD object representing an FAQ page (FAQPage schema).
|
|
28
|
-
*/
|
|
29
|
-
SchemaFAQ.propTypes = {
|
|
30
|
-
/** Structured FAQPage JSON-LD object */
|
|
31
|
-
faqsData: PropTypes.object.isRequired,
|
|
32
|
-
};
|
|
33
|
-
export function SchemaFAQ({ faqsData }) {
|
|
34
|
-
const normalized = normalizeFaqs(faqsData);
|
|
35
|
-
return (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: {
|
|
36
|
-
__html: JSON.stringify(normalized),
|
|
37
|
-
} }));
|
|
38
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import PropTypes from "prop-types";
|
|
4
|
-
/**
|
|
5
|
-
* LocalBusiness Schema Component
|
|
6
|
-
* Generates JSON-LD structured data for SEO
|
|
7
|
-
* https://schema.org/LocalBusiness
|
|
8
|
-
*
|
|
9
|
-
* This component uses siteInfo passed as props to generate schema data.
|
|
10
|
-
* It does not use client-side hooks and can be rendered on the server.
|
|
11
|
-
*/
|
|
12
|
-
/**
|
|
13
|
-
* LocalBusinessSchema — generates JSON-LD for a LocalBusiness using provided props or a fallback `siteInfo`.
|
|
14
|
-
*
|
|
15
|
-
* @param {string} [props.name] - Business name (overrides siteInfo.name).
|
|
16
|
-
* @param {object} [props.address] - Address object containing streetAddress, addressLocality, addressRegion, postalCode, and addressCountry.
|
|
17
|
-
* @param {string} [props.streetAddress] - Street address line.
|
|
18
|
-
* @param {string} [props.addressLocality] - City or locality.
|
|
19
|
-
* @param {string} [props.addressRegion] - State, region or province.
|
|
20
|
-
* @param {string} [props.postalCode] - Postal/ZIP code.
|
|
21
|
-
* @param {string} [props.addressCountry] - Country (defaults to 'United States' when missing).
|
|
22
|
-
* @param {string} [props.telephone] - Contact phone number.
|
|
23
|
-
* @param {string} [props.url] - Canonical website URL.
|
|
24
|
-
* @param {string} [props.logo] - Logo image URL.
|
|
25
|
-
* @param {string} [props.image] - Representative image URL.
|
|
26
|
-
* @param {oneOfType} [props.openingHours] - Opening hours string or array in schema.org format.
|
|
27
|
-
* @param {string} [props.description] - Short business description.
|
|
28
|
-
* @param {string} [props.email] - Contact email address.
|
|
29
|
-
* @param {string} [props.priceRange] - Price range (e.g. '$$', optional).
|
|
30
|
-
* @param {arrayOf} [props.sameAs] - Array of social/profile URLs for schema 'sameAs'.
|
|
31
|
-
* @param {object} [props.siteInfo] - Site-level fallback information object.
|
|
32
|
-
*/
|
|
33
|
-
LocalBusinessSchema.propTypes = {
|
|
34
|
-
/** Business name to include in schema (falls back to siteInfo.name). */
|
|
35
|
-
name: PropTypes.string,
|
|
36
|
-
/** Address object for the business */
|
|
37
|
-
address: PropTypes.shape({
|
|
38
|
-
/** Street address for the business. */
|
|
39
|
-
streetAddress: PropTypes.string,
|
|
40
|
-
/** City or locality for the business address. */
|
|
41
|
-
addressLocality: PropTypes.string,
|
|
42
|
-
/** State/region for the business address. */
|
|
43
|
-
addressRegion: PropTypes.string,
|
|
44
|
-
/** Postal or ZIP code for the address. */
|
|
45
|
-
postalCode: PropTypes.string,
|
|
46
|
-
/** Country for the address (defaults to United States when absent). */
|
|
47
|
-
addressCountry: PropTypes.string,
|
|
48
|
-
}),
|
|
49
|
-
/** Street address for the business. */
|
|
50
|
-
streetAddress: PropTypes.string,
|
|
51
|
-
/** City or locality for the business address. */
|
|
52
|
-
addressLocality: PropTypes.string,
|
|
53
|
-
/** State/region for the business address. */
|
|
54
|
-
addressRegion: PropTypes.string,
|
|
55
|
-
/** Postal or ZIP code for the address. */
|
|
56
|
-
postalCode: PropTypes.string,
|
|
57
|
-
/** Country for the address (defaults to United States when absent). */
|
|
58
|
-
addressCountry: PropTypes.string,
|
|
59
|
-
/** Contact telephone number. */
|
|
60
|
-
telephone: PropTypes.string,
|
|
61
|
-
/** Canonical website URL. */
|
|
62
|
-
url: PropTypes.string,
|
|
63
|
-
/** Logo image URL for schema/logo property. */
|
|
64
|
-
logo: PropTypes.string,
|
|
65
|
-
/** Representative image URL. */
|
|
66
|
-
image: PropTypes.string,
|
|
67
|
-
/** Opening hours as a string or array in schema.org format (e.g., "Mo-Fr 09:00-17:00"). */
|
|
68
|
-
openingHours: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
|
69
|
-
/** Short description for schema. */
|
|
70
|
-
description: PropTypes.string,
|
|
71
|
-
/** Contact email address. */
|
|
72
|
-
email: PropTypes.string,
|
|
73
|
-
/** Price range string (e.g. '$$'). */
|
|
74
|
-
priceRange: PropTypes.string,
|
|
75
|
-
/** Array of profile/URL strings for sameAs (social links). */
|
|
76
|
-
sameAs: PropTypes.arrayOf(PropTypes.string), // Social media profiles
|
|
77
|
-
/** Site-level fallback information object (used when props omitted). */
|
|
78
|
-
siteInfo: PropTypes.object // Required siteinfo from parent component
|
|
79
|
-
};
|
|
80
|
-
export function LocalBusinessSchema(props) {
|
|
81
|
-
// const config = usePixelatedConfig();
|
|
82
|
-
const siteInfo = props.siteInfo;
|
|
83
|
-
// Use props if provided, otherwise fall back to siteInfo
|
|
84
|
-
const name = props.name || siteInfo?.name;
|
|
85
|
-
const address = props.address || siteInfo?.address;
|
|
86
|
-
const streetAddress = props.streetAddress || siteInfo?.address?.streetAddress;
|
|
87
|
-
const addressLocality = props.addressLocality || siteInfo?.address?.addressLocality;
|
|
88
|
-
const addressRegion = props.addressRegion || siteInfo?.address?.addressRegion;
|
|
89
|
-
const postalCode = props.postalCode || siteInfo?.address?.postalCode;
|
|
90
|
-
const addressCountry = props.addressCountry || siteInfo?.address?.addressCountry || 'United States';
|
|
91
|
-
const telephone = props.telephone || siteInfo?.telephone;
|
|
92
|
-
const url = props.url || siteInfo?.url;
|
|
93
|
-
const logo = props.logo || siteInfo?.image;
|
|
94
|
-
const image = props.image || siteInfo?.image || logo;
|
|
95
|
-
const openingHours = props.openingHours;
|
|
96
|
-
const description = props.description || siteInfo?.description;
|
|
97
|
-
const email = props.email || siteInfo?.email;
|
|
98
|
-
const priceRange = props.priceRange || siteInfo?.priceRange;
|
|
99
|
-
const sameAs = props.sameAs || siteInfo?.sameAs;
|
|
100
|
-
const schemaData = {
|
|
101
|
-
'@context': 'https://schema.org',
|
|
102
|
-
'@type': 'LocalBusiness',
|
|
103
|
-
name,
|
|
104
|
-
address: {
|
|
105
|
-
'@type': 'PostalAddress',
|
|
106
|
-
...(address || {
|
|
107
|
-
streetAddress,
|
|
108
|
-
addressLocality,
|
|
109
|
-
addressRegion,
|
|
110
|
-
postalCode,
|
|
111
|
-
addressCountry
|
|
112
|
-
})
|
|
113
|
-
},
|
|
114
|
-
telephone,
|
|
115
|
-
url,
|
|
116
|
-
...(logo && { logo }),
|
|
117
|
-
...(image && { image }),
|
|
118
|
-
...(openingHours && { openingHours }),
|
|
119
|
-
...(description && { description }),
|
|
120
|
-
...(email && { email }),
|
|
121
|
-
...(priceRange && { priceRange }),
|
|
122
|
-
...(sameAs && sameAs.length > 0 && { sameAs })
|
|
123
|
-
};
|
|
124
|
-
return (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(schemaData) } }));
|
|
125
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
|
-
/**
|
|
5
|
-
* ProductSchema — embeds a product/offer as JSON-LD for SEO (schema.org/Product).
|
|
6
|
-
*
|
|
7
|
-
* @param {shape} [props.product] - Product object conforming to schema.org/Product; will be serialized as JSON-LD.
|
|
8
|
-
* @param {string} [props.product.name] - The product name.
|
|
9
|
-
* @param {string} [props.product.description] - Product description.
|
|
10
|
-
* @param {shape} [props.product.brand] - Brand information (name and @type).
|
|
11
|
-
* @param {shape} [props.product.offers] - Offer information including price, currency, URL, and availability.
|
|
12
|
-
*/
|
|
13
|
-
ProductSchema.propTypes = {
|
|
14
|
-
/** Product information object to be serialized as JSON-LD. */
|
|
15
|
-
product: PropTypes.shape({
|
|
16
|
-
'@context': PropTypes.string.isRequired,
|
|
17
|
-
'@type': PropTypes.string.isRequired,
|
|
18
|
-
name: PropTypes.string.isRequired,
|
|
19
|
-
description: PropTypes.string,
|
|
20
|
-
image: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
|
21
|
-
brand: PropTypes.shape({
|
|
22
|
-
'@type': PropTypes.string.isRequired,
|
|
23
|
-
name: PropTypes.string.isRequired,
|
|
24
|
-
}),
|
|
25
|
-
offers: PropTypes.oneOfType([
|
|
26
|
-
PropTypes.shape({
|
|
27
|
-
'@type': PropTypes.string.isRequired,
|
|
28
|
-
url: PropTypes.string,
|
|
29
|
-
priceCurrency: PropTypes.string,
|
|
30
|
-
price: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
31
|
-
availability: PropTypes.string,
|
|
32
|
-
}),
|
|
33
|
-
PropTypes.arrayOf(PropTypes.shape({
|
|
34
|
-
'@type': PropTypes.string.isRequired,
|
|
35
|
-
url: PropTypes.string,
|
|
36
|
-
priceCurrency: PropTypes.string,
|
|
37
|
-
price: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
38
|
-
availability: PropTypes.string,
|
|
39
|
-
}))
|
|
40
|
-
]),
|
|
41
|
-
aggregateRating: PropTypes.shape({
|
|
42
|
-
'@type': PropTypes.string,
|
|
43
|
-
ratingValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
44
|
-
reviewCount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
45
|
-
}),
|
|
46
|
-
}).isRequired,
|
|
47
|
-
};
|
|
48
|
-
export function ProductSchema(props) {
|
|
49
|
-
const { product } = props;
|
|
50
|
-
return (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(product) } }));
|
|
51
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
|
-
/**
|
|
5
|
-
* Recipe Schema Component
|
|
6
|
-
* Generates JSON-LD structured data for recipes
|
|
7
|
-
* https://schema.org/Recipe
|
|
8
|
-
*/
|
|
9
|
-
/**
|
|
10
|
-
* RecipeSchema — embeds a recipe as JSON-LD for SEO (schema.org/Recipe).
|
|
11
|
-
*
|
|
12
|
-
* @param {shape} [props.recipe] - Recipe object conforming to schema.org/Recipe; will be serialized as JSON-LD.
|
|
13
|
-
* @param {string} [props.name] - Recipe title.
|
|
14
|
-
* @param {string} [props.description] - Short recipe description.
|
|
15
|
-
* @param {shape} [props.author] - Author information (name and @type).
|
|
16
|
-
* @param {string} [props.datePublished] - ISO date the recipe was published.
|
|
17
|
-
* @param {string} [props.image] - Primary image URL for the recipe.
|
|
18
|
-
* @param {string} [props.recipeYield] - Yield or serving size (e.g., '4 servings').
|
|
19
|
-
* @param {string} [props.prepTime] - Prep time in ISO 8601 duration (e.g. 'PT20M').
|
|
20
|
-
* @param {string} [props.cookTime] - Cook time in ISO 8601 duration.
|
|
21
|
-
* @param {string} [props.totalTime] - Total time in ISO 8601 duration.
|
|
22
|
-
* @param {string} [props.recipeCategory] - Category of the recipe (e.g., 'Dessert').
|
|
23
|
-
* @param {string} [props.recipeCuisine] - Cuisine (e.g., 'Italian').
|
|
24
|
-
* @param {arrayOf} [props.recipeIngredient] - List of ingredient strings.
|
|
25
|
-
* @param {arrayOf} [props.recipeInstructions] - Structured list of instruction steps or paragraphs.
|
|
26
|
-
* @param {string} [props.license] - License URL or short string for the recipe content.
|
|
27
|
-
*/
|
|
28
|
-
RecipeSchema.propTypes = {
|
|
29
|
-
/** Recipe information object to be serialized as JSON-LD. */
|
|
30
|
-
recipe: PropTypes.shape({
|
|
31
|
-
'@context': PropTypes.string.isRequired,
|
|
32
|
-
'@type': PropTypes.string.isRequired,
|
|
33
|
-
name: PropTypes.string.isRequired,
|
|
34
|
-
description: PropTypes.string,
|
|
35
|
-
author: PropTypes.shape({
|
|
36
|
-
'@type': PropTypes.string.isRequired,
|
|
37
|
-
name: PropTypes.string.isRequired,
|
|
38
|
-
}),
|
|
39
|
-
datePublished: PropTypes.string,
|
|
40
|
-
image: PropTypes.string,
|
|
41
|
-
recipeYield: PropTypes.string,
|
|
42
|
-
prepTime: PropTypes.string,
|
|
43
|
-
cookTime: PropTypes.string,
|
|
44
|
-
totalTime: PropTypes.string,
|
|
45
|
-
recipeCategory: PropTypes.string,
|
|
46
|
-
recipeCuisine: PropTypes.string,
|
|
47
|
-
recipeIngredient: PropTypes.arrayOf(PropTypes.string),
|
|
48
|
-
recipeInstructions: PropTypes.arrayOf(PropTypes.shape({
|
|
49
|
-
'@type': PropTypes.string.isRequired,
|
|
50
|
-
text: PropTypes.string.isRequired,
|
|
51
|
-
})),
|
|
52
|
-
license: PropTypes.string,
|
|
53
|
-
}).isRequired,
|
|
54
|
-
};
|
|
55
|
-
export function RecipeSchema(props) {
|
|
56
|
-
const { recipe } = props;
|
|
57
|
-
return (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(recipe) } }));
|
|
58
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
|
-
/**
|
|
5
|
-
* ReviewSchema — embeds a review as JSON-LD for SEO (schema.org/Review).
|
|
6
|
-
*
|
|
7
|
-
* @param {shape} [props.review] - Review object conforming to schema.org/Review; will be serialized as JSON-LD.
|
|
8
|
-
* @param {string} [props.review.name] - The headline or title of the review.
|
|
9
|
-
* @param {string} [props.review.reviewBody] - The body of the review content.
|
|
10
|
-
* @param {string} [props.review.datePublished] - ISO date the review was published.
|
|
11
|
-
* @param {shape} [props.review.author] - Author information (name and @type).
|
|
12
|
-
* @param {shape} [props.review.itemReviewed] - The item being reviewed (product, service, etc.).
|
|
13
|
-
* @param {shape} [props.review.reviewRating] - Rating information including ratingValue, bestRating, worstRating.
|
|
14
|
-
* @param {shape} [props.review.publisher] - Organization publishing the review.
|
|
15
|
-
*/
|
|
16
|
-
ReviewSchema.propTypes = {
|
|
17
|
-
/** Review information object to be serialized as JSON-LD. */
|
|
18
|
-
review: PropTypes.shape({
|
|
19
|
-
'@context': PropTypes.string.isRequired,
|
|
20
|
-
'@type': PropTypes.string.isRequired,
|
|
21
|
-
name: PropTypes.string.isRequired,
|
|
22
|
-
reviewBody: PropTypes.string,
|
|
23
|
-
datePublished: PropTypes.string,
|
|
24
|
-
author: PropTypes.shape({
|
|
25
|
-
'@type': PropTypes.string.isRequired,
|
|
26
|
-
name: PropTypes.string.isRequired,
|
|
27
|
-
}),
|
|
28
|
-
itemReviewed: PropTypes.shape({
|
|
29
|
-
'@type': PropTypes.string.isRequired,
|
|
30
|
-
name: PropTypes.string,
|
|
31
|
-
}),
|
|
32
|
-
reviewRating: PropTypes.shape({
|
|
33
|
-
'@type': PropTypes.string.isRequired,
|
|
34
|
-
ratingValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
35
|
-
bestRating: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
36
|
-
worstRating: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
37
|
-
}),
|
|
38
|
-
publisher: PropTypes.shape({
|
|
39
|
-
'@type': PropTypes.string.isRequired,
|
|
40
|
-
name: PropTypes.string,
|
|
41
|
-
}),
|
|
42
|
-
}).isRequired,
|
|
43
|
-
};
|
|
44
|
-
export function ReviewSchema(props) {
|
|
45
|
-
const { review } = props;
|
|
46
|
-
return (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(review) } }));
|
|
47
|
-
}
|