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.
Files changed (73) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +24 -0
  3. package/dist/gatsby-config.js +47 -0
  4. package/dist/gatsby-node.js +7 -0
  5. package/dist/src/components/Bio.js +23 -0
  6. package/dist/src/components/BlogSidebar.js +30 -0
  7. package/dist/src/components/Footer.js +11 -0
  8. package/dist/src/components/FooterMenu.js +26 -0
  9. package/dist/src/components/Header.js +12 -0
  10. package/dist/src/components/HeaderMenu.js +34 -0
  11. package/dist/src/components/HeroHeader.js +39 -0
  12. package/dist/src/components/Layout.js +24 -0
  13. package/dist/src/components/Logo.js +17 -0
  14. package/dist/src/components/Seo.js +59 -0
  15. package/dist/src/components/shortcodes/RemoveContentAreaPadding.js +12 -0
  16. package/dist/src/components/shortcodes/SoamesFeature.js +9 -0
  17. package/dist/src/components/shortcodes/SoamesGalleryMenu.js +15 -0
  18. package/dist/src/components/shortcodes/SoamesIconList.js +15 -0
  19. package/dist/src/components/shortcodes/SoamesSoundCloud.js +18 -0
  20. package/dist/src/components/shortcodes/SoamesTextBlock.js +8 -0
  21. package/dist/src/components/shortcodes/SoamesTextList.js +8 -0
  22. package/dist/src/components/shortcodes/SoamesTitle.js +7 -0
  23. package/dist/src/components/shortcodes/SoamesTitleBar.js +7 -0
  24. package/dist/src/components/shortcodes/SoamesTitleBarLg.js +25 -0
  25. package/dist/src/components/shortcodes/SoamesVideo.js +8 -0
  26. package/dist/src/pages/index.js +9 -0
  27. package/dist/src/templates/blog-post-archive.js +59 -0
  28. package/dist/src/templates/blog-post.js +67 -0
  29. package/dist/src/templates/page.js +33 -0
  30. package/dist/src/utils/shortcodes/Shortcodes.js +105 -0
  31. package/dist/src/utils/shortcodes/getAttributes.js +18 -0
  32. package/dist/src/utils/shortcodes/getContent.js +7 -0
  33. package/gatsby-browser.js +11 -0
  34. package/gatsby-node.js +138 -0
  35. package/gatsby-ssr.js +12 -0
  36. package/package.json +76 -0
  37. package/src/components/Bio.tsx +63 -0
  38. package/src/components/BlogSidebar.tsx +86 -0
  39. package/src/components/Footer.tsx +53 -0
  40. package/src/components/FooterMenu.tsx +66 -0
  41. package/src/components/Header.tsx +37 -0
  42. package/src/components/HeaderMenu.tsx +123 -0
  43. package/src/components/HeroHeader.tsx +75 -0
  44. package/src/components/Layout.tsx +60 -0
  45. package/src/components/Logo.tsx +49 -0
  46. package/src/components/Seo.tsx +84 -0
  47. package/src/components/shortcodes/RemoveContentAreaPadding.tsx +13 -0
  48. package/src/components/shortcodes/SoamesFeature.tsx +54 -0
  49. package/src/components/shortcodes/SoamesGalleryMenu.tsx +63 -0
  50. package/src/components/shortcodes/SoamesIconList.tsx +57 -0
  51. package/src/components/shortcodes/SoamesSoundCloud.tsx +71 -0
  52. package/src/components/shortcodes/SoamesTextBlock.tsx +27 -0
  53. package/src/components/shortcodes/SoamesTextList.tsx +27 -0
  54. package/src/components/shortcodes/SoamesTitle.tsx +23 -0
  55. package/src/components/shortcodes/SoamesTitleBar.tsx +21 -0
  56. package/src/components/shortcodes/SoamesTitleBarLg.tsx +56 -0
  57. package/src/components/shortcodes/SoamesVideo.tsx +34 -0
  58. package/src/styles/soames/base.css +592 -0
  59. package/src/styles/soames/components.css +1551 -0
  60. package/src/styles/soames/layout.css +209 -0
  61. package/src/styles/soames/overrides.css +1779 -0
  62. package/src/styles/soames/typography.css +23 -0
  63. package/src/styles/theme.css +8 -0
  64. package/src/styles/vendor/normalize.css +343 -0
  65. package/src/styles/vendor/wordpress-blocks.css +3451 -0
  66. package/src/templates/blog-post-archive.tsx +167 -0
  67. package/src/templates/blog-post.tsx +183 -0
  68. package/src/templates/page.tsx +65 -0
  69. package/src/utils/shortcodes/Shortcodes.tsx +119 -0
  70. package/src/utils/shortcodes/getAttributes.ts +19 -0
  71. package/src/utils/shortcodes/getContent.ts +5 -0
  72. package/static/js/soames-nav-dropdown.js +646 -0
  73. 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
+ }
@@ -0,0 +1,5 @@
1
+ // src/utils/shortcodes/getContent.ts
2
+
3
+ export function getContent(match: RegExpMatchArray | null): string | null {
4
+ return match && match[2] ? match[2] : null;
5
+ }