eddev 0.2.19 → 0.2.22
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/config/config-schema.d.ts +5 -0
- package/config/config-schema.js +1 -0
- package/config/get-config.d.ts +3 -0
- package/config/parse-config.d.ts +2 -0
- package/dev-ui/theme.d.ts +4 -5
- package/docs_old/README.md +33 -0
- package/docs_old/babel.config.js +3 -0
- package/docs_old/blog/2019-05-28-first-blog-post.md +12 -0
- package/docs_old/blog/2019-05-29-long-blog-post.md +44 -0
- package/docs_old/blog/2021-08-01-mdx-blog-post.mdx +20 -0
- package/docs_old/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- package/docs_old/blog/2021-08-26-welcome/index.md +25 -0
- package/docs_old/blog/authors.yml +17 -0
- package/docs_old/docs/graphql/1.graphql-overview.md +62 -0
- package/docs_old/docs/graphql/2.query-hooks.md +238 -0
- package/docs_old/docs/graphql/3.extending-wpgraphql.md +3 -0
- package/docs_old/docs/graphql/_category_.json +4 -0
- package/docs_old/docs/graphql/img/graphql-ide.png +0 -0
- package/docs_old/docs/graphql/img/infinite-example.gif +0 -0
- package/docs_old/docs/graphql/img/mutation-example.gif +0 -0
- package/docs_old/docs/gutenberg/1.overview.md +25 -0
- package/docs_old/docs/gutenberg/2.block-definition.md +37 -0
- package/docs_old/docs/gutenberg/3.block-acf.md +13 -0
- package/docs_old/docs/gutenberg/4.block-graphql.md +63 -0
- package/docs_old/docs/gutenberg/5.inline-editing.md +33 -0
- package/docs_old/docs/gutenberg/6.nested-blocks.md +80 -0
- package/docs_old/docs/gutenberg/7.restricting-to-post-types.md +38 -0
- package/docs_old/docs/gutenberg/8.restricting-to-page-templates.md +26 -0
- package/docs_old/docs/gutenberg/9.dynamic-blocks.md +21 -0
- package/docs_old/docs/gutenberg/_category_.json +4 -0
- package/docs_old/docs/gutenberg/img/create-acf-fields-for-block.png +0 -0
- package/docs_old/docs/gutenberg/img/example-acf-single-project-tile.png +0 -0
- package/docs_old/docs/how-to/1.new-site.md +36 -0
- package/docs_old/docs/how-to/2.deployment.md +31 -0
- package/docs_old/docs/how-to/3.menus.md +72 -0
- package/docs_old/docs/how-to/4.options-pages.md +41 -0
- package/docs_old/docs/how-to/5.bundle-size.md +177 -0
- package/docs_old/docs/how-to/6.favicons.md +82 -0
- package/docs_old/docs/how-to/_category_.json +4 -0
- package/docs_old/docs/how-to/img/bundle-analysis-after.png +0 -0
- package/docs_old/docs/how-to/img/bundle-analysis-before.png +0 -0
- package/docs_old/docs/how-to/img/bundle-webpack-after.png +0 -0
- package/docs_old/docs/how-to/img/bundle-webpack-before.png +0 -0
- package/docs_old/docs/how-to/img/favicon-codebase.png +0 -0
- package/docs_old/docs/how-to/img/favicon-figma-export.png +0 -0
- package/docs_old/docs/how-to/img/favicon-figma.png +0 -0
- package/docs_old/docs/intro.md +7 -0
- package/docs_old/docs/known-issues.md +8 -0
- package/docs_old/docs/serverless/1.overview.md +1 -0
- package/docs_old/docs/serverless/2.config.md +1 -0
- package/docs_old/docs/serverless/3.wordpress-vercel.md +8 -0
- package/docs_old/docs/serverless/4.apis.md +1 -0
- package/docs_old/docs/serverless/5.rpc.md +1 -0
- package/docs_old/docs/serverless/_category_.json +4 -0
- package/docs_old/docs/stack/1-WordPress.md +18 -0
- package/docs_old/docs/stack/2-Flywheel.md +15 -0
- package/docs_old/docs/stack/3-React.md +12 -0
- package/docs_old/docs/stack/4-TypeScript.md +13 -0
- package/docs_old/docs/stack/5-WPGraphQL.md +21 -0
- package/docs_old/docs/stack/6-eddev.md +9 -0
- package/docs_old/docs/stack/_category_.json +4 -0
- package/docs_old/docs/tooling/1.scripts.md +25 -0
- package/docs_old/docs/tooling/2.aliases.md +13 -0
- package/docs_old/docs/tooling/3.defines.md +13 -0
- package/docs_old/docs/tooling/4.config-file.md +14 -0
- package/docs_old/docs/tooling/_category_.json +4 -0
- package/docs_old/docs/views/1.overview.md +31 -0
- package/docs_old/docs/views/2.queries.md +18 -0
- package/docs_old/docs/views/3.content-blocks.md +36 -0
- package/docs_old/docs/views/4.app-view.md +35 -0
- package/docs_old/docs/views/5.page-templates.md +20 -0
- package/docs_old/docs/views/_category_.json +4 -0
- package/docs_old/docusaurus.config.js +119 -0
- package/docs_old/package.json +40 -0
- package/docs_old/sidebars.js +26 -0
- package/docs_old/src/components/HomepageFeatures.js +64 -0
- package/docs_old/src/components/HomepageFeatures.module.css +11 -0
- package/docs_old/src/css/custom.css +28 -0
- package/docs_old/src/pages/index.js +36 -0
- package/docs_old/src/pages/index.module.css +23 -0
- package/docs_old/src/pages/markdown-page.md +7 -0
- package/{components/ServerlessRouter.d.ts → docs_old/static/.nojekyll} +0 -0
- package/docs_old/static/img/docusaurus.png +0 -0
- package/docs_old/static/img/favicon.ico +0 -0
- package/docs_old/static/img/logo-black.svg +4 -0
- package/docs_old/static/img/logo-white.svg +4 -0
- package/docs_old/static/img/tutorial/docsVersionDropdown.png +0 -0
- package/docs_old/static/img/tutorial/localeDropdown.png +0 -0
- package/docs_old/static/img/undraw_docusaurus_mountain.svg +170 -0
- package/docs_old/static/img/undraw_docusaurus_react.svg +169 -0
- package/docs_old/static/img/undraw_docusaurus_tree.svg +1 -0
- package/docs_old/yarn.lock +8814 -0
- package/dynamic/dynamic-component.d.ts +0 -1
- package/entry/Root.d.ts +1 -1
- package/package.json +1 -1
- package/serverless/define-rpc-router.d.ts +1 -1
- package/serverless-template/package.json +1 -1
- package/serverless-template/pages/_document.tsx +20 -1
- package/serverless-template/pages/api/rest/[...method].ts +2 -1
- package/style/createStitches.d.ts +4 -5
- package/utils/getRepoName.js +7 -1
- package/components/ServerlessRouter.js +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Block ACF Fields
|
|
2
|
+
|
|
3
|
+
Once you've created a `.tsx` file for your block, it'll automatically be registered with WordPress, and is available to be targetted by ACF field groups.
|
|
4
|
+
|
|
5
|
+
- Create a new ACF Field Group
|
|
6
|
+
- Make sure the block title starts with `Block: ` — this is just convention
|
|
7
|
+
- Add any fields you like.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
:::tip
|
|
12
|
+
We may add the ability to have [ACF Builder](https://github.com/StoutLogic/acf-builder) support for blocks at some point, however for now it's recommended that you use the ACF Interface to build fields for your blocks. ACF Builder is an advanced tool which requires that you have a deeper knowledge of ACF and all of it's configuration options, and may be harder to use for ACF newbies.
|
|
13
|
+
:::
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Block GraphQL Files
|
|
2
|
+
|
|
3
|
+
## Naming
|
|
4
|
+
|
|
5
|
+
A block named `/blocks/sections/newsletter.tsx` can have a single GraphQL query file named `/blocks/sections/newsletter.graphql`.
|
|
6
|
+
|
|
7
|
+
Due to a limitation with GraphQL Codegen, each `.graphql` file must contain one uniquely-named.
|
|
8
|
+
|
|
9
|
+
ACF fields attached to the block can be queried using the naming convention `folderName_blockName`, where the folder name and block name are automatically converted from `dash-case` to `camelCase`.
|
|
10
|
+
|
|
11
|
+
## Execution
|
|
12
|
+
|
|
13
|
+
Block queries are executed automatically by PHP code, so there is no need to call any functions to get the data, or handle any loading/error state. The result of the query is sent straight into the `props` of your block.
|
|
14
|
+
|
|
15
|
+
## Querying ACF Fields
|
|
16
|
+
|
|
17
|
+
GraphQL schemas for your blocks ACF Fields are automatically generated by PHP.
|
|
18
|
+
|
|
19
|
+
Consider this block type:
|
|
20
|
+
|
|
21
|
+
```tsx title="blocks/project/single-project-tile.tsx"
|
|
22
|
+
/**
|
|
23
|
+
* Title: Single Project Tile
|
|
24
|
+
* Description: Displays a single project as a large tile.
|
|
25
|
+
* Category: common
|
|
26
|
+
* Icon: book-alt
|
|
27
|
+
* Keywords: post
|
|
28
|
+
* Types: page, post
|
|
29
|
+
* Mode: preview
|
|
30
|
+
* Supports multiple: true
|
|
31
|
+
*/
|
|
32
|
+
import { defineBlock, EditableText, InnerBlocks } from "eddev/blocks"
|
|
33
|
+
|
|
34
|
+
export default defineBlock("project/single-project-tile", (props) => {
|
|
35
|
+
return <div>...</div>
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
With the following ACF field configuration:
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
The ACF fields will be available under `block.project_singleProjectTile`
|
|
44
|
+
|
|
45
|
+
```graphql title="blocks/project/single-project-tile.graphql"
|
|
46
|
+
query SingleProjectTile {
|
|
47
|
+
block {
|
|
48
|
+
project_singleProjectTile {
|
|
49
|
+
project {
|
|
50
|
+
... on Project {
|
|
51
|
+
id
|
|
52
|
+
title(format: RENDERED)
|
|
53
|
+
projectFields {
|
|
54
|
+
image {
|
|
55
|
+
...BasicImage
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Inline Editing
|
|
2
|
+
|
|
3
|
+
It's trivial to add inline editing to your blocks. This is totally separate from ACF.
|
|
4
|
+
|
|
5
|
+
## `<EditableText />`
|
|
6
|
+
|
|
7
|
+
The most common inline editing function will of course be the ability to edit text in your block. The `<EditableText>` component does just this! Keep in mind, this can only be used on blocks, or components which are used within a block.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
<div>
|
|
11
|
+
<EditableText as="h2" id="title" placeholder="Enter a title" />
|
|
12
|
+
</div>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
When the above code runs in the WordPress editor, an author can click on that element and type into it directly.
|
|
16
|
+
|
|
17
|
+
Note that you can have as many `<EditableText>` components as you want, however each `id` must be unique, since each value is stored as a key/value pair.
|
|
18
|
+
|
|
19
|
+
The `as` prop is required, and can either be an element type (`p`, `h1`, `h2`, etc.) or a component.
|
|
20
|
+
|
|
21
|
+
## `useInlineEditableValue`
|
|
22
|
+
|
|
23
|
+
The `useInlineEditableValue` hook is the foundation of all inline editing in our system — it works a lot like `useState`, except that the content is saved to the block when saving the page, and restored again when the page is loaded.
|
|
24
|
+
|
|
25
|
+
Much like `EditableText`, the hook requires a unique `id` to identify the value.
|
|
26
|
+
|
|
27
|
+
You can store any kind of data, including objects and arrays.
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
const [title, setTitle] = useInlineEditableValue('title');
|
|
31
|
+
|
|
32
|
+
return <input type="text" value={title} onChange={e => setTitle(e.currentTarget.value)}>
|
|
33
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Nested Blocks
|
|
2
|
+
|
|
3
|
+
Nested blocks add a lot of power to Gutenberg — you can create blocks, which themselves contain more blocks.
|
|
4
|
+
|
|
5
|
+
The `<InnerBlocks />` component acts as a container for child blocks.
|
|
6
|
+
|
|
7
|
+
Note that you can only have ONE `InnerBlocks` per-block. This is a WordPress limitation.
|
|
8
|
+
|
|
9
|
+
## Block Tagging
|
|
10
|
+
|
|
11
|
+
Most of the time, we want to restrict where blocks can be used. For example, we might have a set of 'root' blocks which the user can only be added to the root of the page. Then, we might want some 'inline' blocks which can be added to some of those root blocks. We may also have specialised blocks, like a 'Button Row', which can only accept 'Button' blocks.
|
|
12
|
+
|
|
13
|
+
To do this, we've invented a 'tags' system for blocks, which is defined in the comments section of a block.
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
/*
|
|
17
|
+
* ...
|
|
18
|
+
* Tags: root
|
|
19
|
+
* Child tags: inline
|
|
20
|
+
*/
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use 'Tags' to define the tags on this block, as a comma separated list if defining multiple tags. The `'root'` tag is special, in that any block tagged with it can be added to the root of the page.
|
|
24
|
+
|
|
25
|
+
If the block has `InnerBlocks`, you can use `Child tags` to indirectly specify the allowed block types which can be added to this block.
|
|
26
|
+
|
|
27
|
+
## Tagging core blocks
|
|
28
|
+
|
|
29
|
+
You can also tag core blocks, which are blocks which are included by WordPress, using some PHP code.
|
|
30
|
+
|
|
31
|
+
Note that you can tag core blocks multiple times with multiple tags.
|
|
32
|
+
|
|
33
|
+
```php title="backend/blocks.php"
|
|
34
|
+
<?php
|
|
35
|
+
|
|
36
|
+
ED()->tagCoreBlocks("wysiwyg", [
|
|
37
|
+
"core/paragraph",
|
|
38
|
+
"core/list",
|
|
39
|
+
"core/heading",
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
?>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Putting it all together
|
|
46
|
+
|
|
47
|
+
For example, if you have a 'Section' block which accepts some WYSIWYG blocks like headings and paragraphs as well as a 'Button Row' block, and only want to allow 'Button' blocks to be added to the 'Button Row', you might have something like this:
|
|
48
|
+
|
|
49
|
+
```tsx title="blocks/layout/section.tsx"
|
|
50
|
+
/*
|
|
51
|
+
* ...
|
|
52
|
+
* Tags: root
|
|
53
|
+
* Child tags: inline, wysiwyg
|
|
54
|
+
*/
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```tsx title="blocks/buttons/button-row.tsx"
|
|
58
|
+
/*
|
|
59
|
+
* ...
|
|
60
|
+
* Tags: inline
|
|
61
|
+
* Child tags: buttons
|
|
62
|
+
*/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```tsx title="blocks/buttons/button.tsx"
|
|
66
|
+
/*
|
|
67
|
+
* ...
|
|
68
|
+
* Tags: buttons
|
|
69
|
+
*/
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```php title="backend/blocks.php"
|
|
73
|
+
|
|
74
|
+
ED()->tagCoreBlocks("wysiwyg", [
|
|
75
|
+
"core/paragraph",
|
|
76
|
+
"core/list",
|
|
77
|
+
"core/heading",
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Restricting to Post Types
|
|
2
|
+
|
|
3
|
+
You can restrict an individual block type to only be used in certain post types.
|
|
4
|
+
|
|
5
|
+
To do this, just update your block's `Types` property. You can have multiple types, separated by commas.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
/*
|
|
9
|
+
* ...
|
|
10
|
+
* Types: page, post
|
|
11
|
+
* ...
|
|
12
|
+
*/
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Template Locking
|
|
16
|
+
|
|
17
|
+
Taking this a step further, you can also specify required root blocks for a post type.
|
|
18
|
+
|
|
19
|
+
For example, you might want to specify that when creating a new blog post, that only 'Blog Post Header' and 'Blog Post Content' blocks are added to root, and are automatically added when creating a new post, and also cannot be removed.
|
|
20
|
+
|
|
21
|
+
This needs to be done in PHP:
|
|
22
|
+
|
|
23
|
+
```php title="backend/blocks.php"
|
|
24
|
+
<?php
|
|
25
|
+
|
|
26
|
+
ED()->templateLock([
|
|
27
|
+
'type' => 'post',
|
|
28
|
+
'content' => [
|
|
29
|
+
['acf/blog-post-header'],
|
|
30
|
+
['acf/blog-post-content'],
|
|
31
|
+
]
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
:::warning
|
|
37
|
+
Note that you need to use the internal ACF block name when working with template locks. For example, `blog/post-header` becomes `acf/blog-post-header`.
|
|
38
|
+
:::
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Restricting to Page Templates
|
|
2
|
+
|
|
3
|
+
You can restrict an individual block type to only be used in certain page templates
|
|
4
|
+
|
|
5
|
+
To do this, just update your block's `Templates` property. You can have multiple templates, separated by commas.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
/*
|
|
9
|
+
* ...
|
|
10
|
+
* Types: page, post
|
|
11
|
+
* Templates: templates/my-cool-template
|
|
12
|
+
* ...
|
|
13
|
+
*/
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
:::tip
|
|
17
|
+
Be sure to set `Types: page`, otherwise the block wont be available in the page template.
|
|
18
|
+
:::
|
|
19
|
+
|
|
20
|
+
:::warning
|
|
21
|
+
Note that in above example, the block will be available on all `post`s, and all `page`s which have the template `views/templates/my-cool-template.tsx`.
|
|
22
|
+
:::
|
|
23
|
+
|
|
24
|
+
### Template Locking
|
|
25
|
+
|
|
26
|
+
Unfortunately this isn't possible at the moment... mostly due to Gutenberg being a bit crap.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Dynamic Blocks
|
|
2
|
+
|
|
3
|
+
A "dynamic" block is a block that loads asynchronously, only when it's used for the first time. This is useful when a block has a large dependency (think, THREE.js or Swiper). This can drastically reduce the size of your bundle, while also improving the loading time of your site.
|
|
4
|
+
|
|
5
|
+
The downside is that the block won't load until it's first use, which means that it'll be hidden for a moment while it loads. Because of this, it's recommended that you:
|
|
6
|
+
|
|
7
|
+
- Perhaps don't use this feature if your block is likely to be used above-the-fold (maybe a good candidate for dynamic components or dynamic imports)
|
|
8
|
+
- Do a fade in animation when the block is first mounted, so that it's not a jarring experience.
|
|
9
|
+
|
|
10
|
+
It's super easy to enable this feature — just add `Dynamic: true` to your block header comment.
|
|
11
|
+
|
|
12
|
+
```tsx title="/blocks/layouts/section.tsx"
|
|
13
|
+
/**
|
|
14
|
+
* Title: TITLE
|
|
15
|
+
* Description: DESCRIPTION
|
|
16
|
+
* ...etc
|
|
17
|
+
* Dynamic: true
|
|
18
|
+
*/
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
You can read more about dynamic _components_, and other ways to reduce your bundle size here — [Bundle Size Tips →](../how-to/bundle-size)
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Creating a new site
|
|
2
|
+
|
|
3
|
+
### Initial Setup
|
|
4
|
+
|
|
5
|
+
1. [Create a new repo on Github](https://github.com/new)
|
|
6
|
+
1. Set `Owner` to `ed-digital`, and name your repo like `some-client-name`.
|
|
7
|
+
2. Set `Repository Template` to `ed-digital/eddev-starter-theme`
|
|
8
|
+
3. Hit `Create repository`
|
|
9
|
+
2. Create a new site in Local, on your computer.
|
|
10
|
+
1. Use the default settings
|
|
11
|
+
2. Be sure to use `ed_admin` as the username, and create a secure password using 1pass or similar, and the email address should be `web@ed.com.au`
|
|
12
|
+
3. Save the username and password to 1pass.
|
|
13
|
+
3. Install the following plugins:
|
|
14
|
+
1. Advanced Custom Fields Pro ([download](https://www.advancedcustomfields.com/my-account/view-licenses/)
|
|
15
|
+
2. Add `b3JkZXJfaWQ9NDA3NDN8dHlwZT1kZXZlbG9wZXJ8ZGF0ZT0yMDE0LTA5LTI3IDA4OjUwOjIx` as the key for ACF Pro under 'Custom Fields > Updates'
|
|
16
|
+
3. Yoast SEO — install from plugins dashboard
|
|
17
|
+
4. Gutenberg — install from plugins dashboard
|
|
18
|
+
5. Nested Pages — install from plugins dashboard
|
|
19
|
+
6. Admin Menu Editor — install from plugins dashboard (use this to hide unused sidebar items, and tidy up the menu for clients.)
|
|
20
|
+
4. Under Settings -> General, remove 'Just another WordPress Site' and set Timezone to Sydney.
|
|
21
|
+
6. Make sure that all the installed plugins have been activated.
|
|
22
|
+
7. Clone your newly created theme to `wp-content/themes/`, making sure that the repo name and the theme folder name are identical.
|
|
23
|
+
8. Run the following commands inside the theme folder
|
|
24
|
+
1. `composer update`
|
|
25
|
+
2. `yarn`
|
|
26
|
+
3. `yarn add --dev eddev`
|
|
27
|
+
4. `yarn setup`
|
|
28
|
+
9. Activate the theme under Appearance > Themes
|
|
29
|
+
10. Run `yarn dev`
|
|
30
|
+
|
|
31
|
+
### Initial Flywheel Setup
|
|
32
|
+
|
|
33
|
+
1. [Create a new site on Flywheel](https://app.getflywheel.com/new-site)
|
|
34
|
+
1. Select 'My Organisation'
|
|
35
|
+
2. The username and password are irrelevant, as they'll soon be overridden with your locally created details.
|
|
36
|
+
2. Once complete, restart Local, and do an initial push to the new site, deploying to 'Production' with 'Include Database' selected.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Deployment
|
|
2
|
+
|
|
3
|
+
There are some prerequisites for setting up deployment.
|
|
4
|
+
|
|
5
|
+
1. You've got an SSH key set up on your computer, which has been added to both Github and Flywheel. You only need to do this once for your computer.
|
|
6
|
+
2. You've set up the site on Flywheel already, and completed an initial push using Local Sync.
|
|
7
|
+
|
|
8
|
+
Assuming the above prerequisites are met, setting up deployment is easy!
|
|
9
|
+
|
|
10
|
+
1. Grab the Flywheel SSH user for your site.
|
|
11
|
+
1. On the [Flywheel Dashboard](https://app.getflywheel.com/sites), locate the relevant site.
|
|
12
|
+
2. Click the 'Advanced' tab.
|
|
13
|
+
3. Copy the user component from the 'Connect via SSH' section.
|
|
14
|
+
(For example, if the connection string is `ssh team+ed-digital+the-yards@ssh.getflywheel.com`), the value is `team+ed-digital+the-yards`.
|
|
15
|
+
2. Grab your private key.
|
|
16
|
+
1. Assuming you're using your default private key, and that it's in the correct format already, you can just run `cat ~/.ssh/id_rsa | pbcopy` in your terminal to copy this directly to your clipboard.
|
|
17
|
+
3. Paste the secrets into the Github repo's 'Secrets' panel.
|
|
18
|
+
1. Open the repo on Github, and click Settings -> Secrets.
|
|
19
|
+
2. Add a new secret called `SSH_USER` and paste in the value from step 1. Save it!
|
|
20
|
+
3. Add a new secret called `PRIVATE_KEY` and paste in the value from step 2. Save it!
|
|
21
|
+
4. Finally, rename `.github/workflows/main.yaml.disabled` to `.github/workflows/main.yaml` and commit/push the results.
|
|
22
|
+
```
|
|
23
|
+
mv .github/workflows/main.yaml.disabled .github/workflows/main.yaml
|
|
24
|
+
git commit -am "Enable Github Action deployment"
|
|
25
|
+
git push
|
|
26
|
+
```
|
|
27
|
+
5. Monitor the 'Actions' tab on your Github repo page to ensure that the first deployment succeeded.
|
|
28
|
+
|
|
29
|
+
:::tip warning
|
|
30
|
+
Your first deploy might fail to run at the 'Install Dependencies' step. Try running 'Re-run all jobs'.
|
|
31
|
+
:::
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Menus
|
|
2
|
+
|
|
3
|
+
For simplicities sake, all menus are pre-loaded in the starter theme's `views/_app.graphql`. It looks something like this:
|
|
4
|
+
|
|
5
|
+
```graphql title="views/_app.graphql"
|
|
6
|
+
query CommonData {
|
|
7
|
+
menus {
|
|
8
|
+
nodes {
|
|
9
|
+
menuItems {
|
|
10
|
+
nodes {
|
|
11
|
+
...MenuItemFields
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
locations
|
|
15
|
+
slug
|
|
16
|
+
name
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
fragment MenuItemFields on MenuItem {
|
|
22
|
+
label
|
|
23
|
+
title
|
|
24
|
+
target
|
|
25
|
+
url
|
|
26
|
+
cssClasses
|
|
27
|
+
childItems {
|
|
28
|
+
nodes {
|
|
29
|
+
label
|
|
30
|
+
title
|
|
31
|
+
target
|
|
32
|
+
url
|
|
33
|
+
cssClasses
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The fragment is used there so that it's easy to grab TypeScript types for the menu data structure.
|
|
40
|
+
|
|
41
|
+
There's also a `backend/menus.php` file, which has a call to `register_nav_menus` function call, which sets up 'menu locations'. If you need more than just one menu, you can add them there.
|
|
42
|
+
|
|
43
|
+
```php
|
|
44
|
+
register_nav_menus([
|
|
45
|
+
'main' => 'Main Menu',
|
|
46
|
+
'footer' => 'Footer Menu'
|
|
47
|
+
]);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The starter theme also comes with a `components/atoms/Menu.tsx` component. It's included as a component which you can tailor to your needs.
|
|
51
|
+
|
|
52
|
+
To use the menu component, you'll need to specify the menu location which you defined in PHP. Assuming you've got `yarn dev` running, the menu locations will auto-complete with TypeScript.
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
<Menu location="Main">
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
It's recommended that if you need to stylise or customise the Menu, that you kinda keep that file nice and generic, and create a new component which uses the `Menu` component as a base. (Or alternatively, you can always copy/paste the `Menu` code into your new component.)
|
|
59
|
+
|
|
60
|
+
```tsx title="components/atoms/MyCoolMenu.tsx
|
|
61
|
+
import { Menu } from "@components/atoms/Menus"
|
|
62
|
+
|
|
63
|
+
export const MyCoolMenu = styled(Menu, {
|
|
64
|
+
display: "flex",
|
|
65
|
+
li: {
|
|
66
|
+
marginLeft: "20px",
|
|
67
|
+
a: {
|
|
68
|
+
fontFamily: '"Comic Sans"',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Options Pages
|
|
2
|
+
|
|
3
|
+
You can create an 'Options Page' on the WordPress sidebar to create a space for global ACF fields.
|
|
4
|
+
|
|
5
|
+
Creating and consuming an options page can be completed in a few steps:
|
|
6
|
+
|
|
7
|
+
1. Create a new Options Page — check out `backend/options.php` for some example code, noting that `show_in_graphql` must be set to `true`.
|
|
8
|
+
2. Create a new ACF field group, with the fields you need.
|
|
9
|
+
1. Set the Location Rule to `Options Page` `is equal to` `<your new options page>`.
|
|
10
|
+
2. Make sure `Show in GraphQL` is checked.
|
|
11
|
+
3. Set `GraphQL Field Name` to something short and sweet, using pascalCase, eg `myOptions`.
|
|
12
|
+
3. Check the GraphiQL IDE, and look for a root-level field with a similar name to the `menu_slug` defined in your PHP, code, eg. `themeGeneralSettings`. It should have a child field with you `GraphQL Field Name`, which contains your settings!
|
|
13
|
+
|
|
14
|
+
The PHP code should look something like this:
|
|
15
|
+
|
|
16
|
+
```php
|
|
17
|
+
acf_add_options_page([
|
|
18
|
+
'page_title' => 'Theme Settings',
|
|
19
|
+
'menu_title' => 'Theme Settings',
|
|
20
|
+
'menu_slug' => 'theme-general-settings',
|
|
21
|
+
'capability' => 'edit_posts',
|
|
22
|
+
'redirect' => false,
|
|
23
|
+
'show_in_graphql' => true
|
|
24
|
+
]);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Assuming that all worked, you can then query these fields in an `.graphql` file. If it needs to be globally accessible, you can add it to the `_app.graphql` file, and access it using the `useAppData` hook. It'll even be typed with TypeScript.
|
|
28
|
+
|
|
29
|
+
```graphql title="views/_app.graphql"
|
|
30
|
+
query AppData {
|
|
31
|
+
themeGeneralSettings {
|
|
32
|
+
myOptions {
|
|
33
|
+
footerText
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
const footerText = useAppData((data) => data?.themeGeneralSettings?.myOptions?.footerText)
|
|
41
|
+
```
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Bundle Size Tips
|
|
2
|
+
|
|
3
|
+
## Inspecting Bundle Size
|
|
4
|
+
|
|
5
|
+
You can quite easily see the production size of your app by running:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
yarn build
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
Above, we can see that the bundle size is 1.34mb, which is quite large. The note from Webpack says that we should aim for 244kb, which is really quite small. When we eventually move to serverless (via Vercel) for production frontends, we'll certainly aim for this smaller size. For now, with WordPress hosting, 600-700kb is fine. React itself takes up a bit of space on it's own.
|
|
14
|
+
|
|
15
|
+
Let's dig deeper into why the bundle is so large.
|
|
16
|
+
|
|
17
|
+
Our compile system includes the official [Webpack Bundle Analyzer](https://www.npmjs.com/package/webpack-bundle-analyzer) plugin. You can get a detailed analysis of the bundle size using the following:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
yarn build
|
|
21
|
+
open dist/frontend/bundle-report.html
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Your browser will launch the bundle report, which will look something like this.
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
You can see in the example above, that `hls.js` and `moment` + `moment-timezone` are taking up _a lot_ of the bundle size.
|
|
29
|
+
|
|
30
|
+
What can we do about this?
|
|
31
|
+
|
|
32
|
+
## Choosing better libraries
|
|
33
|
+
|
|
34
|
+
In the example above, `moment` is taking up SO much space, for something that is so simple. There are much simpler and smaller libraries that can be used instead.
|
|
35
|
+
|
|
36
|
+
- If all you want is date formatting, check out `date-fns` — which provides a nifty date formatting function. The library is tree-shakable, meaning only the functions from that library you actually use in your code will be included in the bundle. [date-fns →](https://date-fns.org/)
|
|
37
|
+
- If you need something a little more powerful, or if you need something related to timezones, check out `luxon`, which is made by the same team that created `moment`. It's a much lighter alternative, because even though it supports timezones, it uses native browser APIs, instead of bundling info on every timezone/locale. [Luxon →](https://moment.github.io/luxon/index.html#/?id=luxon)
|
|
38
|
+
- (ps. Luxon doesn't come with TypeScript types, but you can install them using `yarn add --dev @types/luxon`)
|
|
39
|
+
|
|
40
|
+
It's always worth reconsidering the libraries we choose, looking for the most modern libraries for certain functions.
|
|
41
|
+
|
|
42
|
+
## Dynamic imports
|
|
43
|
+
|
|
44
|
+
Next up is `hls.js`. It's a library that provides a simple way to load and play HLS streams. It's not huge, but we probably don't need to include it in the main bundle.
|
|
45
|
+
|
|
46
|
+
Say we have the following code:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import Hls from "hls.js"
|
|
50
|
+
|
|
51
|
+
// ...
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const video = ref.current!
|
|
55
|
+
if (src) {
|
|
56
|
+
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
57
|
+
// HLS is already supported natively by this browser
|
|
58
|
+
video.src = props.url
|
|
59
|
+
} else {
|
|
60
|
+
// HLS is not supported by the browser. Use Hls.js instead!
|
|
61
|
+
const hls = new Hls()
|
|
62
|
+
hls.loadSource(props.url!)
|
|
63
|
+
hls.attachMedia(video)
|
|
64
|
+
return () => {
|
|
65
|
+
hls.destroy()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}, [src])
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
We can quite easily refactor this code to use dynamic imports, which will automatically split that library into it's own file, which will be imported separately only if/when it's needed. If we still need TypeScript types for the module, we can also use `import type` syntax, which will only import the types for TypeScript's sake, and not the module itself.
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import type Hls from "hls.js"
|
|
76
|
+
|
|
77
|
+
// ...
|
|
78
|
+
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
const video = ref.current!
|
|
81
|
+
if (props.url) {
|
|
82
|
+
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
83
|
+
// HLS is already supported natively by this browser
|
|
84
|
+
video.src = props.url
|
|
85
|
+
} else {
|
|
86
|
+
// HLS is not supported by the browser. Use Hls.js instead!
|
|
87
|
+
// Declare a reference to the object, so we can can still destroy it when the component transitions out
|
|
88
|
+
let hls: Hls
|
|
89
|
+
import("hls.js").then(({ default: Hls }) => {
|
|
90
|
+
// hls.js has been successfully dynamically imported.
|
|
91
|
+
// Assign to the `hls` variable, so that it can be destroyed properly
|
|
92
|
+
hls = new Hls()
|
|
93
|
+
hls.loadSource(props.url!)
|
|
94
|
+
hls.attachMedia(video)
|
|
95
|
+
})
|
|
96
|
+
return () => {
|
|
97
|
+
// Destroy the hls video, as long as it was created in the first place
|
|
98
|
+
if (hls) {
|
|
99
|
+
hls.destroy()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}, [props.url])
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Dynamicly imported components
|
|
108
|
+
|
|
109
|
+
Similar to Next's `next/dynamic` [(link)](https://nextjs.org/docs/advanced-features/dynamic-import), we have our own `eddev/dynamic` function. This allows you dynamically load components from the `components/*` directory using dynamic imports.
|
|
110
|
+
|
|
111
|
+
```tsx title="blocks/homepage/some-block.tsx"
|
|
112
|
+
import { dynamic } from "eddev/dynamic"
|
|
113
|
+
|
|
114
|
+
const MyDynamicComponent = dynamic(() => import("@components/something/my-dynamic-component"))
|
|
115
|
+
|
|
116
|
+
export default defineBlock("homepage/some-block", () => {
|
|
117
|
+
return (
|
|
118
|
+
<div>
|
|
119
|
+
<p>I appear instantly</p>
|
|
120
|
+
<MyDynamicComponent fallback={<div>Loading...</div>} />
|
|
121
|
+
</div>
|
|
122
|
+
)
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
All the same props from `MyDynamicComponent` are available, and the fallback (which is optional) is added as a prop by the `dynamic` function. It'll show the fallback while the component is loading.
|
|
127
|
+
|
|
128
|
+
When using this feature, you may notice a flash while the component is being loaded, since your page will actually render without the dynamic component first. This means that it may not always be suitable if the component is typically "above the fold". You may want to consider showing a placeholder div, and/or fading in the component using an animation once it's loaded.
|
|
129
|
+
|
|
130
|
+
It's also important to note that the dynamic function expects (by default), a `default` export. Normally, we prefer to export named components, but in this case, `default` is good.
|
|
131
|
+
|
|
132
|
+
```tsx title="components/something/my-dynamic-component.tsx"
|
|
133
|
+
// BAD: export function MyDynamicComponent() {}
|
|
134
|
+
// GOOD:
|
|
135
|
+
export default function MyDynamicComponent() {
|
|
136
|
+
return <div>I'm a dynamic component!</div>
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Internally, we use `@loadable/components` — you can see other options for `dynamic` via their [docs](https://loadable-components.com/). Next also uses this library!
|
|
141
|
+
|
|
142
|
+
## Dynamically imported blocks
|
|
143
|
+
|
|
144
|
+
The `eddev/dynamic` function requires a tiny bit of boilerplate to get going. If you'd like to make a whole _block_ load dynamically, it's even easier. Note though that you wont be able to show a loading fallback.
|
|
145
|
+
|
|
146
|
+
Just add `Dynamic: true` to your block header comment — the compiler will spot this, and automatically turn it into a dynamic component. That's it!
|
|
147
|
+
|
|
148
|
+
```tsx title="blocks/homepage/some-block.tsx"
|
|
149
|
+
/**
|
|
150
|
+
* Title: Some Block
|
|
151
|
+
* Description: A block that does something cool
|
|
152
|
+
* Category: common
|
|
153
|
+
* Icon: book-alt
|
|
154
|
+
* Keywords: post
|
|
155
|
+
* Templates: default
|
|
156
|
+
* Types: page
|
|
157
|
+
* Mode: preview
|
|
158
|
+
* Supports multiple: true
|
|
159
|
+
* Tags: root
|
|
160
|
+
* Child Tags: none
|
|
161
|
+
* Dynamic: true
|
|
162
|
+
*/
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
^ The last line is the important part. You can read more about dynamic blocks [here](../gutenberg/dynamic-blocks).
|
|
166
|
+
|
|
167
|
+
## Validating the changes
|
|
168
|
+
|
|
169
|
+
In the top sections above, we swapped from `moment` to `luxon`, and we also made `hls.js` a dynamic import.
|
|
170
|
+
|
|
171
|
+
After running `yarn build` again, and checking out the visualizer, we can see that these two small changes have reduced our main bundle size by **HALF**.
|
|
172
|
+
|
|
173
|
+

|
|
174
|
+
|
|
175
|
+

|
|
176
|
+
|
|
177
|
+
We could take this even further if we wanted, by making any blocks which use Swiper dynamic blocks, as discussed above.
|