soames-gatsby-theme 0.1.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.
- package/LICENSE +29 -0
- package/README.md +24 -0
- package/dist/gatsby-config.js +47 -0
- package/dist/gatsby-node.js +7 -0
- package/dist/src/components/Bio.js +23 -0
- package/dist/src/components/BlogSidebar.js +30 -0
- package/dist/src/components/Footer.js +11 -0
- package/dist/src/components/FooterMenu.js +26 -0
- package/dist/src/components/Header.js +12 -0
- package/dist/src/components/HeaderMenu.js +34 -0
- package/dist/src/components/HeroHeader.js +39 -0
- package/dist/src/components/Layout.js +24 -0
- package/dist/src/components/Logo.js +17 -0
- package/dist/src/components/Seo.js +59 -0
- package/dist/src/components/shortcodes/RemoveContentAreaPadding.js +12 -0
- package/dist/src/components/shortcodes/SoamesFeature.js +9 -0
- package/dist/src/components/shortcodes/SoamesGalleryMenu.js +15 -0
- package/dist/src/components/shortcodes/SoamesIconList.js +15 -0
- package/dist/src/components/shortcodes/SoamesSoundCloud.js +18 -0
- package/dist/src/components/shortcodes/SoamesTextBlock.js +8 -0
- package/dist/src/components/shortcodes/SoamesTextList.js +8 -0
- package/dist/src/components/shortcodes/SoamesTitle.js +7 -0
- package/dist/src/components/shortcodes/SoamesTitleBar.js +7 -0
- package/dist/src/components/shortcodes/SoamesTitleBarLg.js +25 -0
- package/dist/src/components/shortcodes/SoamesVideo.js +8 -0
- package/dist/src/pages/index.js +9 -0
- package/dist/src/templates/blog-post-archive.js +59 -0
- package/dist/src/templates/blog-post.js +67 -0
- package/dist/src/templates/page.js +33 -0
- package/dist/src/utils/shortcodes/Shortcodes.js +105 -0
- package/dist/src/utils/shortcodes/getAttributes.js +18 -0
- package/dist/src/utils/shortcodes/getContent.js +7 -0
- package/gatsby-browser.js +11 -0
- package/gatsby-node.js +138 -0
- package/gatsby-ssr.js +12 -0
- package/package.json +76 -0
- package/src/components/Bio.tsx +63 -0
- package/src/components/BlogSidebar.tsx +86 -0
- package/src/components/Footer.tsx +53 -0
- package/src/components/FooterMenu.tsx +66 -0
- package/src/components/Header.tsx +37 -0
- package/src/components/HeaderMenu.tsx +123 -0
- package/src/components/HeroHeader.tsx +75 -0
- package/src/components/Layout.tsx +60 -0
- package/src/components/Logo.tsx +49 -0
- package/src/components/Seo.tsx +84 -0
- package/src/components/shortcodes/RemoveContentAreaPadding.tsx +13 -0
- package/src/components/shortcodes/SoamesFeature.tsx +54 -0
- package/src/components/shortcodes/SoamesGalleryMenu.tsx +63 -0
- package/src/components/shortcodes/SoamesIconList.tsx +57 -0
- package/src/components/shortcodes/SoamesSoundCloud.tsx +71 -0
- package/src/components/shortcodes/SoamesTextBlock.tsx +27 -0
- package/src/components/shortcodes/SoamesTextList.tsx +27 -0
- package/src/components/shortcodes/SoamesTitle.tsx +23 -0
- package/src/components/shortcodes/SoamesTitleBar.tsx +21 -0
- package/src/components/shortcodes/SoamesTitleBarLg.tsx +56 -0
- package/src/components/shortcodes/SoamesVideo.tsx +34 -0
- package/src/styles/soames/base.css +592 -0
- package/src/styles/soames/components.css +1551 -0
- package/src/styles/soames/layout.css +209 -0
- package/src/styles/soames/overrides.css +1779 -0
- package/src/styles/soames/typography.css +23 -0
- package/src/styles/theme.css +8 -0
- package/src/styles/vendor/normalize.css +343 -0
- package/src/styles/vendor/wordpress-blocks.css +3451 -0
- package/src/templates/blog-post-archive.tsx +167 -0
- package/src/templates/blog-post.tsx +183 -0
- package/src/templates/page.tsx +65 -0
- package/src/utils/shortcodes/Shortcodes.tsx +119 -0
- package/src/utils/shortcodes/getAttributes.ts +19 -0
- package/src/utils/shortcodes/getContent.ts +5 -0
- package/static/js/soames-nav-dropdown.js +646 -0
- package/static/js/soames-navbar-dropdown.js +127 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Link, graphql, PageProps } from "gatsby";
|
|
3
|
+
import parse from "html-react-parser";
|
|
4
|
+
|
|
5
|
+
import Layout from "../components/Layout";
|
|
6
|
+
import Seo from "../components/Seo";
|
|
7
|
+
import HeroHeader from "../components/HeroHeader";
|
|
8
|
+
|
|
9
|
+
interface PostNode {
|
|
10
|
+
excerpt: string;
|
|
11
|
+
uri: string;
|
|
12
|
+
date: string;
|
|
13
|
+
title: string;
|
|
14
|
+
featuredImage?: {
|
|
15
|
+
node: {
|
|
16
|
+
guid: string;
|
|
17
|
+
title: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ArchivePage {
|
|
23
|
+
title: string;
|
|
24
|
+
excerpt: string;
|
|
25
|
+
featuredImage?: {
|
|
26
|
+
node: {
|
|
27
|
+
guid: string;
|
|
28
|
+
title: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface BlogPostArchiveProps extends PageProps<{
|
|
34
|
+
allWpPost: {
|
|
35
|
+
nodes: PostNode[];
|
|
36
|
+
};
|
|
37
|
+
wpPage: ArchivePage;
|
|
38
|
+
}, {
|
|
39
|
+
nextPagePath?: string;
|
|
40
|
+
previousPagePath?: string;
|
|
41
|
+
}> {}
|
|
42
|
+
|
|
43
|
+
const BlogIndex: React.FC<BlogPostArchiveProps> = ({ data, pageContext }) => {
|
|
44
|
+
const posts = data.allWpPost.nodes;
|
|
45
|
+
const archive = data.wpPage;
|
|
46
|
+
const { nextPagePath, previousPagePath } = pageContext;
|
|
47
|
+
|
|
48
|
+
if (!posts.length) {
|
|
49
|
+
return (
|
|
50
|
+
<Layout>
|
|
51
|
+
<Seo title="All posts" />
|
|
52
|
+
<HeroHeader
|
|
53
|
+
title={archive.title ? parse(archive.title) : "Blog"}
|
|
54
|
+
subhead={archive.excerpt ? parse(archive.excerpt) : ""}
|
|
55
|
+
backgroundImage={archive.featuredImage?.node.guid || null}
|
|
56
|
+
backgroundImageTitle={archive.featuredImage?.node.title || null}
|
|
57
|
+
/>
|
|
58
|
+
<p>
|
|
59
|
+
No blog posts found. Add posts to your WordPress site and they'll appear here!
|
|
60
|
+
</p>
|
|
61
|
+
</Layout>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const groupedPosts = posts.reduce<PostNode[][]>((groups, post, index) => {
|
|
66
|
+
if (index % 3 === 0) {
|
|
67
|
+
groups.push([post]);
|
|
68
|
+
} else {
|
|
69
|
+
groups[groups.length - 1].push(post);
|
|
70
|
+
}
|
|
71
|
+
return groups;
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<Layout isHomePage>
|
|
76
|
+
<Seo title="All posts" />
|
|
77
|
+
<HeroHeader
|
|
78
|
+
title={archive.title ? parse(archive.title) : "Blog"}
|
|
79
|
+
subhead={archive.excerpt ? parse(archive.excerpt) : ""}
|
|
80
|
+
backgroundImage={archive.featuredImage?.node.guid || null}
|
|
81
|
+
backgroundImageTitle={archive.featuredImage?.node.title || null}
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
<section className="soames-blog-roll">
|
|
85
|
+
<div className="container">
|
|
86
|
+
{groupedPosts.map((group, index) => (
|
|
87
|
+
<div key={index} className="media-container-row">
|
|
88
|
+
{group.map((post, idx) => (
|
|
89
|
+
<div key={idx} className="card p-3 col-12 col-lg-4">
|
|
90
|
+
<div className="card-wrapper">
|
|
91
|
+
{/* Uncomment and adjust the following block if featured images are to be displayed */}
|
|
92
|
+
{/* {post.featuredImage && (
|
|
93
|
+
<div className="card-img">
|
|
94
|
+
<Link to={`/blog${post.uri}`}>
|
|
95
|
+
<img src={post.featuredImage.node.guid} alt={post.featuredImage.node.title} title={post.featuredImage.node.title} />
|
|
96
|
+
</Link>
|
|
97
|
+
</div>
|
|
98
|
+
)} */}
|
|
99
|
+
<div className="card-box">
|
|
100
|
+
<h4 className="card-title mbr-fonts-style display-5">
|
|
101
|
+
{parse(post.title)}
|
|
102
|
+
</h4>
|
|
103
|
+
<h4 className="mbr-fonts-style display-7">
|
|
104
|
+
{post.date}
|
|
105
|
+
</h4>
|
|
106
|
+
{parse(post.excerpt)}
|
|
107
|
+
</div>
|
|
108
|
+
<div className="mbr-section-btn text-center">
|
|
109
|
+
<Link
|
|
110
|
+
to={`/blog${post.uri}`}
|
|
111
|
+
itemProp="url"
|
|
112
|
+
className="btn btn-primary display-4"
|
|
113
|
+
>
|
|
114
|
+
<span itemProp="headline">Read More</span>
|
|
115
|
+
</Link>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
))}
|
|
120
|
+
</div>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
</section>
|
|
124
|
+
|
|
125
|
+
<section>
|
|
126
|
+
{previousPagePath && (
|
|
127
|
+
<>
|
|
128
|
+
<Link to={`/blog${previousPagePath}`}>Previous page</Link>
|
|
129
|
+
<br />
|
|
130
|
+
</>
|
|
131
|
+
)}
|
|
132
|
+
{nextPagePath && <Link to={`/blog${nextPagePath}`}>Next page</Link>}
|
|
133
|
+
</section>
|
|
134
|
+
</Layout>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export default BlogIndex;
|
|
139
|
+
|
|
140
|
+
export const pageQuery = graphql`
|
|
141
|
+
query WordPressPostArchive($offset: Int!, $postsPerPage: Int!) {
|
|
142
|
+
allWpPost(sort: { date: DESC }, limit: $postsPerPage, skip: $offset) {
|
|
143
|
+
nodes {
|
|
144
|
+
excerpt
|
|
145
|
+
uri
|
|
146
|
+
date(formatString: "MMMM DD, YYYY")
|
|
147
|
+
title
|
|
148
|
+
featuredImage {
|
|
149
|
+
node {
|
|
150
|
+
guid
|
|
151
|
+
title
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
wpPage(isPostsPage: { eq: true }) {
|
|
157
|
+
title
|
|
158
|
+
excerpt
|
|
159
|
+
featuredImage {
|
|
160
|
+
node {
|
|
161
|
+
guid
|
|
162
|
+
title
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
`;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// src/templates/blog-post.tsx
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Link, graphql, PageProps } from "gatsby";
|
|
5
|
+
import { GatsbyImage, IGatsbyImageData } from "gatsby-plugin-image";
|
|
6
|
+
import parse from "html-react-parser";
|
|
7
|
+
|
|
8
|
+
import "../styles/vendor/wordpress-blocks.css";
|
|
9
|
+
|
|
10
|
+
import Bio from "../components/Bio";
|
|
11
|
+
import Layout from "../components/Layout";
|
|
12
|
+
import Seo from "../components/Seo";
|
|
13
|
+
import HeroHeader from "../components/HeroHeader";
|
|
14
|
+
import BlogSidebar from "../components/BlogSidebar";
|
|
15
|
+
|
|
16
|
+
interface FeaturedImage {
|
|
17
|
+
node: {
|
|
18
|
+
altText?: string | null;
|
|
19
|
+
gatsbyImage?: IGatsbyImageData;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Post {
|
|
24
|
+
id: string;
|
|
25
|
+
excerpt?: string | null;
|
|
26
|
+
content?: string | null;
|
|
27
|
+
title?: string | null;
|
|
28
|
+
date?: string | null;
|
|
29
|
+
featuredImage?: FeaturedImage | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface Page {
|
|
33
|
+
title?: string | null;
|
|
34
|
+
excerpt?: string | null;
|
|
35
|
+
featuredImage?: {
|
|
36
|
+
node: {
|
|
37
|
+
guid?: string | null;
|
|
38
|
+
};
|
|
39
|
+
} | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface PostEdge {
|
|
43
|
+
uri: string;
|
|
44
|
+
title?: string | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface BlogPostTemplateProps extends PageProps {
|
|
48
|
+
data: {
|
|
49
|
+
post: Post;
|
|
50
|
+
previous?: PostEdge | null;
|
|
51
|
+
next?: PostEdge | null;
|
|
52
|
+
page?: Page | null;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const BlogPostTemplate: React.FC<BlogPostTemplateProps> = ({ data }) => {
|
|
57
|
+
const { post, previous, next, page } = data;
|
|
58
|
+
|
|
59
|
+
const featuredImageData = post.featuredImage?.node?.gatsbyImage ?? null;
|
|
60
|
+
const featuredImageAlt = post.featuredImage?.node?.altText || "Featured image";
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Layout>
|
|
64
|
+
<Seo title={post.title || ""} description={post.excerpt || undefined} />
|
|
65
|
+
<HeroHeader
|
|
66
|
+
title={page?.title ? parse(page.title) : "Blog"}
|
|
67
|
+
subhead={page?.excerpt ? parse(page.excerpt) : ""}
|
|
68
|
+
backgroundImage={page?.featuredImage?.node?.guid || null}
|
|
69
|
+
backgroundImageTitle={page?.title || null}
|
|
70
|
+
/>
|
|
71
|
+
<section>
|
|
72
|
+
<div className="media-container-row">
|
|
73
|
+
<div className="col-12 col-lg-8">
|
|
74
|
+
<section id="soames-gatsby-content-container" className="soames-gatsby-blog-content">
|
|
75
|
+
<article className="blog-post" itemScope itemType="http://schema.org/Article">
|
|
76
|
+
<header>
|
|
77
|
+
<h1 itemProp="headline">{post.title ? parse(post.title) : ""}</h1>
|
|
78
|
+
<p>{post.date}</p>
|
|
79
|
+
</header>
|
|
80
|
+
|
|
81
|
+
{post.content && (
|
|
82
|
+
<section itemProp="articleBody" className="blog-post-content">
|
|
83
|
+
{parse(post.content)}
|
|
84
|
+
</section>
|
|
85
|
+
)}
|
|
86
|
+
|
|
87
|
+
<hr />
|
|
88
|
+
|
|
89
|
+
<footer>
|
|
90
|
+
<Bio />
|
|
91
|
+
</footer>
|
|
92
|
+
</article>
|
|
93
|
+
|
|
94
|
+
<nav className="blog-post-nav">
|
|
95
|
+
<ul
|
|
96
|
+
style={{
|
|
97
|
+
display: `flex`,
|
|
98
|
+
flexWrap: `wrap`,
|
|
99
|
+
justifyContent: `space-between`,
|
|
100
|
+
listStyle: `none`,
|
|
101
|
+
padding: 0,
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
<li>
|
|
105
|
+
{previous && (
|
|
106
|
+
<Link to={`/blog${previous.uri}`} rel="prev">
|
|
107
|
+
← {previous.title ? parse(previous.title) : ""}
|
|
108
|
+
</Link>
|
|
109
|
+
)}
|
|
110
|
+
</li>
|
|
111
|
+
|
|
112
|
+
<li>
|
|
113
|
+
{next && (
|
|
114
|
+
<Link to={`/blog${next.uri}`} rel="next">
|
|
115
|
+
{next.title ? parse(next.title) : ""} →
|
|
116
|
+
</Link>
|
|
117
|
+
)}
|
|
118
|
+
</li>
|
|
119
|
+
</ul>
|
|
120
|
+
</nav>
|
|
121
|
+
</section>
|
|
122
|
+
</div>
|
|
123
|
+
<div className="col-12 col-lg-4">
|
|
124
|
+
<section id="soames-gatsby-sidebar-container" className="soames-gatsby-sidebar">
|
|
125
|
+
{featuredImageData && (
|
|
126
|
+
<GatsbyImage
|
|
127
|
+
image={featuredImageData}
|
|
128
|
+
alt={featuredImageAlt}
|
|
129
|
+
style={{ marginBottom: 50 }}
|
|
130
|
+
/>
|
|
131
|
+
)}
|
|
132
|
+
|
|
133
|
+
<h1>Recent Posts</h1>
|
|
134
|
+
|
|
135
|
+
<BlogSidebar postId={post.id} />
|
|
136
|
+
</section>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</section>
|
|
140
|
+
</Layout>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export default BlogPostTemplate;
|
|
145
|
+
|
|
146
|
+
export const pageQuery = graphql`
|
|
147
|
+
query BlogPostById($id: String!, $previousPostId: String, $nextPostId: String) {
|
|
148
|
+
post: wpPost(id: { eq: $id }) {
|
|
149
|
+
id
|
|
150
|
+
excerpt
|
|
151
|
+
content
|
|
152
|
+
title
|
|
153
|
+
date(formatString: "MMMM DD, YYYY")
|
|
154
|
+
featuredImage {
|
|
155
|
+
node {
|
|
156
|
+
altText
|
|
157
|
+
gatsbyImage(
|
|
158
|
+
width: 1200
|
|
159
|
+
layout: CONSTRAINED
|
|
160
|
+
placeholder: BLURRED
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
previous: wpPost(id: { eq: $previousPostId }) {
|
|
166
|
+
uri
|
|
167
|
+
title
|
|
168
|
+
}
|
|
169
|
+
next: wpPost(id: { eq: $nextPostId }) {
|
|
170
|
+
uri
|
|
171
|
+
title
|
|
172
|
+
}
|
|
173
|
+
page: wpPage(isPostsPage: { eq: true }) {
|
|
174
|
+
title
|
|
175
|
+
excerpt
|
|
176
|
+
featuredImage {
|
|
177
|
+
node {
|
|
178
|
+
guid
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
`;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { graphql, PageProps } from "gatsby";
|
|
3
|
+
import parse from "html-react-parser";
|
|
4
|
+
|
|
5
|
+
import Layout from "../components/Layout";
|
|
6
|
+
import Seo from "../components/Seo";
|
|
7
|
+
import HeroHeader from "../components/HeroHeader";
|
|
8
|
+
import { Shortcodes } from "../utils/shortcodes/Shortcodes";
|
|
9
|
+
|
|
10
|
+
// Gutenberg block styles
|
|
11
|
+
import "../styles/vendor/wordpress-blocks.css";
|
|
12
|
+
|
|
13
|
+
type PageTemplateProps = PageProps<{
|
|
14
|
+
page: {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
excerpt?: string;
|
|
18
|
+
content?: string;
|
|
19
|
+
featuredImage?: {
|
|
20
|
+
node: {
|
|
21
|
+
title: string;
|
|
22
|
+
guid: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}>;
|
|
27
|
+
|
|
28
|
+
const Page: React.FC<PageTemplateProps> = ({ data: { page } }) => (
|
|
29
|
+
<Layout>
|
|
30
|
+
<Seo title={page.title} />
|
|
31
|
+
<HeroHeader
|
|
32
|
+
title={parse(page.title)}
|
|
33
|
+
subhead={page.excerpt ? parse(page.excerpt) : ""}
|
|
34
|
+
backgroundImage={page.featuredImage?.node.guid || null}
|
|
35
|
+
backgroundImageTitle={page.featuredImage?.node.title || null}
|
|
36
|
+
/>
|
|
37
|
+
{page.content && (
|
|
38
|
+
<section
|
|
39
|
+
id="soames-gatsby-content-container"
|
|
40
|
+
className="soames-gatsby-content"
|
|
41
|
+
>
|
|
42
|
+
<Shortcodes>{page.content}</Shortcodes>
|
|
43
|
+
</section>
|
|
44
|
+
)}
|
|
45
|
+
</Layout>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
export default Page;
|
|
49
|
+
|
|
50
|
+
export const pageQuery = graphql`
|
|
51
|
+
query PageById($id: String!) {
|
|
52
|
+
page: wpPage(id: { eq: $id }) {
|
|
53
|
+
id
|
|
54
|
+
title
|
|
55
|
+
excerpt
|
|
56
|
+
content
|
|
57
|
+
featuredImage {
|
|
58
|
+
node {
|
|
59
|
+
title
|
|
60
|
+
guid
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
import parse, { HTMLReactParserOptions, Element as DomElement } from "html-react-parser";
|
|
3
|
+
|
|
4
|
+
import { getAttributes } from "./getAttributes";
|
|
5
|
+
import { getContent } from "./getContent";
|
|
6
|
+
|
|
7
|
+
import RemoveContentAreaPadding from "../../components/shortcodes/RemoveContentAreaPadding";
|
|
8
|
+
import SoamesTitle from "../../components/shortcodes/SoamesTitle";
|
|
9
|
+
import SoamesTitleBar from "../../components/shortcodes/SoamesTitleBar";
|
|
10
|
+
import SoamesTitleBarLg from "../../components/shortcodes/SoamesTitleBarLg";
|
|
11
|
+
import SoamesTextBlock from "../../components/shortcodes/SoamesTextBlock";
|
|
12
|
+
import SoamesIconList from "../../components/shortcodes/SoamesIconList";
|
|
13
|
+
import SoamesFeature from "../../components/shortcodes/SoamesFeature";
|
|
14
|
+
import SoamesGalleryMenu from "../../components/shortcodes/SoamesGalleryMenu";
|
|
15
|
+
import SoamesVideo from "../../components/shortcodes/SoamesVideo";
|
|
16
|
+
import SoamesTextList from "../../components/shortcodes/SoamesTextList";
|
|
17
|
+
import SoamesSoundCloud from "../../components/shortcodes/SoamesSoundCloud";
|
|
18
|
+
|
|
19
|
+
interface ShortcodesProps {
|
|
20
|
+
children: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type Attributes = {
|
|
24
|
+
[key: string]: string[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const handleShortcodes: HTMLReactParserOptions["replace"] = (node) => {
|
|
28
|
+
if (node.type === "tag" && node.children && node.children.length > 0) {
|
|
29
|
+
const child = node.children[0];
|
|
30
|
+
if (child.type === "text") {
|
|
31
|
+
const shortcode = child.data.trim();
|
|
32
|
+
|
|
33
|
+
if (shortcode === "[soames-remove-content-area-padding]") {
|
|
34
|
+
return <RemoveContentAreaPadding />;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const shortcodeMappings: {
|
|
38
|
+
regex: RegExp;
|
|
39
|
+
component: React.FC<any>;
|
|
40
|
+
propsExtractor: (match: RegExpMatchArray) => any;
|
|
41
|
+
}[] = [
|
|
42
|
+
{
|
|
43
|
+
regex: /\[soames-title([^\]]*)\]([\s\S]*?)\[\/soames-title\]/,
|
|
44
|
+
component: SoamesTitle,
|
|
45
|
+
propsExtractor: (match) => ({ title: getContent(match) }),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
regex: /\[soames-title-bar([^\]]*)\]([\s\S]*?)\[\/soames-title-bar\]/,
|
|
49
|
+
component: SoamesTitleBar,
|
|
50
|
+
propsExtractor: (match) => ({ title: getContent(match) }),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
regex: /\[soames-title-bar-lg([^\]]*)\]([\s\S]*?)\[\/soames-title-bar-lg\]/,
|
|
54
|
+
component: SoamesTitleBarLg,
|
|
55
|
+
propsExtractor: (match) => ({
|
|
56
|
+
title: getContent(match),
|
|
57
|
+
attributes: getAttributes(match),
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
regex: /\[soames-text-block([^\]]*)\]([\s\S]*?)\[\/soames-text-block\]/,
|
|
62
|
+
component: SoamesTextBlock,
|
|
63
|
+
propsExtractor: (match) => ({ content: getContent(match) }),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
regex: /\[soames-text-list([^\]]*)\]([\s\S]*?)\[\/soames-text-list\]/,
|
|
67
|
+
component: SoamesTextList,
|
|
68
|
+
propsExtractor: (match) => ({ content: getContent(match) }),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
regex: /\[soames-icon-list([^\]]*)\]/,
|
|
72
|
+
component: SoamesIconList,
|
|
73
|
+
propsExtractor: (match) => ({ attributes: getAttributes(match) }),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
regex: /\[soames-feature([^\]]*)\]([\s\S]*?)\[\/soames-feature\]/,
|
|
77
|
+
component: SoamesFeature,
|
|
78
|
+
propsExtractor: (match) => ({
|
|
79
|
+
content: getContent(match),
|
|
80
|
+
attributes: getAttributes(match),
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
regex: /\[soames-gallery-menu([^\]]*)\]/,
|
|
85
|
+
component: SoamesGalleryMenu,
|
|
86
|
+
propsExtractor: (match) => ({ attributes: getAttributes(match) }),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
regex: /\[soames-video([^\]]*)\]/,
|
|
90
|
+
component: SoamesVideo,
|
|
91
|
+
propsExtractor: (match) => ({ attributes: getAttributes(match) }),
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
regex: /\[soames-soundcloud([^\]]*)\]/,
|
|
95
|
+
component: SoamesSoundCloud,
|
|
96
|
+
propsExtractor: (match) => ({ attributes: getAttributes(match) }),
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
for (const { regex, component: Component, propsExtractor } of shortcodeMappings) {
|
|
101
|
+
const match = shortcode.match(regex);
|
|
102
|
+
if (match) {
|
|
103
|
+
const props = propsExtractor(match);
|
|
104
|
+
return <Component {...props} />;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return undefined;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const Shortcodes: React.FC<ShortcodesProps> = ({ children }) => {
|
|
114
|
+
const reactElements = parse(children || "", {
|
|
115
|
+
replace: handleShortcodes,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return <div>{reactElements}</div>;
|
|
119
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/utils/shortcodes/getAttributes.ts
|
|
2
|
+
|
|
3
|
+
export function getAttributes(match: RegExpMatchArray | null): Record<string, string[]> | null {
|
|
4
|
+
if (!match || !match[1]) return null;
|
|
5
|
+
|
|
6
|
+
const attributes: Record<string, string[]> = {};
|
|
7
|
+
const attrString = match[1];
|
|
8
|
+
|
|
9
|
+
const regex = /(\w+)=["']?((?:.(?!["']?\s+(?:\S+)=|\s*\/?[>"']))+.)["']?/g;
|
|
10
|
+
let attrMatch;
|
|
11
|
+
|
|
12
|
+
while ((attrMatch = regex.exec(attrString)) !== null) {
|
|
13
|
+
const key = attrMatch[1];
|
|
14
|
+
const value = attrMatch[2].replace(/[″”]+/g, '').split(',');
|
|
15
|
+
attributes[key] = value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return attributes;
|
|
19
|
+
}
|