feat: v4.0.0 (#1016)

Co-authored-by: Denys Konovalov <kontakt@denyskon.de>
Co-authored-by: Mathieu COSYNS <64072917+Mathieu-COSYNS@users.noreply.github.com>
This commit is contained in:
Daniel Lautzenheiser
2024-01-03 15:14:09 -05:00
committed by GitHub
parent 682576ffc4
commit 799c7e6936
732 changed files with 48477 additions and 10886 deletions

View File

@ -114,7 +114,6 @@ module.exports = {
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/explicit-function-return-type': [0],
'@typescript-eslint/explicit-module-boundary-types': [0],
'@typescript-eslint/no-duplicate-imports': 'error',
'@typescript-eslint/no-use-before-define': [
'error',
{ functions: false, classes: true, variables: true },

View File

@ -1,6 +1,6 @@
{
"title": "Help us build the CMS of the future",
"subtitle": "Get support, give support and find out what's new through the channels below.",
"subtitle": "Get support, give support and find out what is new through the channels below.",
"sections": [
{
"title": "Contributing",

View File

@ -4,7 +4,7 @@ title: Bundling
weight: 5
---
This tutorial guides you through the steps for adding Static CMS via a package manager to a site that's built with a common [static site generator](https://www.staticgen.com/). If you want to start form a template, the [Next Template](/docs/start-with-a-template) provides a great example of bundling in action.
This tutorial guides you through the steps for adding Static CMS via a package manager to a site that is built with a common [static site generator](https://www.staticgen.com/). If you want to start form a template, the [Next Template](/docs/start-with-a-template) provides a great example of bundling in action.
## Installation
@ -58,11 +58,11 @@ Make sure the file containing the CMS object will be built as a page, with `@sta
## Configuration
Configuration is different for every site, so we'll break it down into parts. Add all the code snippets in this section to your `admin/config.js` file (which is passed into the `CMS.init({ config })` call).
Configuration is different for every site, so we will break it down into parts. Add all the code snippets in this section to your `admin/config.js` file (which is passed into the `CMS.init({ config })` call).
### Backend
We're using [Netlify](https://www.netlify.com) for our hosting and authentication in this tutorial, so backend configuration is fairly straightforward.
We are using [Netlify](https://www.netlify.com) for our hosting and authentication in this tutorial, so backend configuration is fairly straightforward.
For GitHub and GitLab repositories, you can start your Static CMS `config` file with these lines:
@ -84,7 +84,22 @@ backend:
_(For Bitbucket repositories, use the [Bitbucket backend](/docs/bitbucket-backend) instructions instead.)_
The configuration above specifies your backend protocol and your publication branch. Git Gateway is an open source API that acts as a proxy between authenticated users of your site and your site repo. (We'll get to the details of that in the [Authentication section](#authentication) below.) If you leave out the `branch` declaration, it defaults to `main`.
The configuration above specifies your backend protocol and your publication branch. Git Gateway is an open source API that acts as a proxy between authenticated users of your site and your site repo. (We will get to the details of that in the [Authentication section](#authentication) below.) If you leave out the `branch` declaration, it defaults to `main`.
### Editorial Workflow <BetaImage />
By default, saving a post in the CMS interface pushes a commit directly to the publication branch specified in `backend`. However, you also have the option to enable the [Editorial Workflow](/docs/editorial-workflow), which adds an interface for drafting, reviewing, and approving posts. To do this, add the following line to your `config.yml`:
<CodeTabs>
```yaml
publish_mode: editorial_workflow
```
```js
publish_mode: 'editorial_workflow'
```
</CodeTabs>
### Media and Public Folders
@ -102,9 +117,9 @@ media_folder: 'images/uploads' # Media files will be stored in the repo under im
</CodeTabs>
If you're creating a new folder for uploaded media, you'll need to know where your static site generator expects static files. You can refer to the paths outlined above in [App File Structure](#app-file-structure), and put your media folder in the same location where you put the `admin` folder.
If you are creating a new folder for uploaded media, you will need to know where your static site generator expects static files. You can refer to the paths outlined above in [App File Structure](#app-file-structure), and put your media folder in the same location where you put the `admin` folder.
Note that the`media_folder` file path is relative to the project root, so the example above would work for Jekyll, GitBook, or any other generator that stores static files at the project root. However, it would not work for Hugo, Hexo, Middleman or others that store static files in a subfolder. Here's an example that could work for a Hugo site:
Note that the`media_folder` file path is relative to the project root, so the example above would work for Jekyll, GitBook, or any other generator that stores static files at the project root. However, it would not work for Hugo, Hexo, Middleman or others that store static files in a subfolder. Here is an example that could work for a Hugo site:
<CodeTabs>
```js
@ -120,7 +135,7 @@ public_folder: '/images/uploads' # The src attribute for uploaded media will beg
</CodeTabs>
The configuration above adds a new setting, `public_folder`. While `media_folder` specifies where uploaded files are saved in the repo, `public_folder` indicates where they are found in the published site. Image `src` attributes use this path, which is relative to the file where it's called. For this reason, we usually start the path at the site root, using the opening `/`.
The configuration above adds a new setting, `public_folder`. While `media_folder` specifies where uploaded files are saved in the repo, `public_folder` indicates where they are found in the published site. Image `src` attributes use this path, which is relative to the file where it is called. For this reason, we usually start the path at the site root, using the opening `/`.
_If `public_folder` is not set, Static CMS defaults to the same value as `media_folder`, adding an opening `/` if one is not included._
@ -128,7 +143,7 @@ _If `public_folder` is not set, Static CMS defaults to the same value as `media_
Collections define the structure for the different content types on your static site. Since every site is different, the `collections` settings differ greatly from one site to the next.
Let's say your site has a blog, with the posts stored in `_posts/blog`, and files saved in a date-title format, like `1999-12-31-lets-party.md`. Each post begins with settings in yaml-formatted front matter, like so:
Let us say your site has a blog, with the posts stored in `_posts/blog`, and files saved in a date-title format, like `1999-12-31-lets-party.md`. Each post begins with settings in yaml-formatted front matter, like so:
```yaml
---
@ -151,7 +166,7 @@ collections: [
label: 'Blog', // Used in the UI
folder: '_posts/blog', // The path to the folder where the documents are stored
create: true, // Allow users to create new documents in this collection
slug: '{{year}}-{{month}}-{{day}}-{{slug}}', // Filename template, e.g., YYYY-MM-DD-title.md
slug: '{{year}}-{{month}}-{{day}}-{{slug}}', // Filename template, e.g., yyyy-MM-dd-title.md
fields: [ // The fields for each document, usually in front matter
{ label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' },
{ label: 'Title', name: 'title', widget: 'string' },
@ -170,7 +185,7 @@ collections:
label: 'Blog' # Used in the UI
folder: '_posts/blog' # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
slug: '{{year}}-{{month}}-{{day}}-{{slug}}' # Filename template, e.g., YYYY-MM-DD-title.md
slug: '{{year}}-{{month}}-{{day}}-{{slug}}' # Filename template, e.g., yyyy-MM-dd-title.md
fields: # The fields for each document, usually in front matter
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' }
- { label: 'Title', name: 'title', widget: 'string' }
@ -182,7 +197,7 @@ collections:
</CodeTabs>
Let's break that down:
Let us break that down:
| Field | Description |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@ -239,7 +254,7 @@ collections:
## Authentication
Now that you have your Static CMS files in place and configured, all that's left is to enable authentication. We're using the [Netlify](https://www.netlify.com/) platform here because it's one of the quickest ways to get started, but you can learn about other authentication options in the [Backends](/docs/backends-overview) doc.
Now that you have your Static CMS files in place and configured, all that is left is to enable authentication. We are using the [Netlify](https://www.netlify.com/) platform here because it is one of the quickest ways to get started, but you can learn about other authentication options in the [Backends](/docs/backends-overview) doc.
### Setup on Netlify
@ -250,9 +265,9 @@ Netlify offers a built-in authentication service called Identity. In order to us
Netlify's Identity and Git Gateway services allow you to manage CMS admin users for your site without requiring them to have an account with your Git host or commit access on your repo. From your site dashboard on Netlify:
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you want only invited users to access your CMS, but if you're just experimenting, you can leave it open for convenience.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you want only invited users to access your CMS, but if you are just experimenting, you can leave it open for convenience.
3. If you'd like to allow one-click login with services like Google and GitHub, check the boxes next to the services you'd like to use, under **External providers**.
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This authenticates with your Git host and generates an API access token. In this case, we're leaving the **Roles** field blank, which means any logged in user may access Static CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This authenticates with your Git host and generates an API access token. In this case, we are leaving the **Roles** field blank, which means any logged in user may access Static CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
### Add the Netlify Identity Widget
@ -262,7 +277,7 @@ With the backend set to handle authentication, now you need a frontend interface
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
```
Add this to the `<head>` of your CMS index page at `/admin/index.html`, as well as the `<head>` of your site's main index page. Depending on how your site generator is set up, this may mean you need to add it to the default template, or to a "partial" or "include" template. If you can find where the site stylesheet is linked, that's probably the right place. Alternatively, you can include the script in your site using Netlify's [Script Injection](https://www.netlify.com/docs/inject-analytics-snippets/) feature.
Add this to the `<head>` of your CMS index page at `/admin/index.html`, as well as the `<head>` of your site's main index page. Depending on how your site generator is set up, this may mean you need to add it to the default template, or to a "partial" or "include" template. If you can find where the site stylesheet is linked, that is probably the right place. Alternatively, you can include the script in your site using Netlify's [Script Injection](https://www.netlify.com/docs/inject-analytics-snippets/) feature.
When a user logs in with the Netlify Identity widget, an access token directs to the site homepage. In order to complete the login and get back to Static CMS, redirect the user back to the `/admin/` path. To do this, add the following script before the closing `body` tag of your site's main index page:
@ -290,6 +305,6 @@ If you set your registration preference to "Invite only," invite yourself (and a
If you left your site registration open, or for return visits after confirming an email invitation, access your site's CMS at `yoursite.com/admin/`.
**Note:** No matter where you access Static CMS — whether running locally, in a staging environment, or in your published site — it always fetches and commits files in your hosted repository (for example, on GitHub), on the branch you configured in your Static CMS `config` file. This means that content fetched in the admin UI matches the content in the repository, which may be different from your locally running site. It also means that content saved using the admin UI saves directly to the hosted repository, even if you're running the UI locally or in staging.
**Note:** No matter where you access Static CMS — whether running locally, in a staging environment, or in your published site — it always fetches and commits files in your hosted repository (for example, on GitHub), on the branch you configured in your Static CMS `config` file. This means that content fetched in the admin UI matches the content in the repository, which may be different from your locally running site. It also means that content saved using the admin UI saves directly to the hosted repository, even if you are running the UI locally or in staging.
Happy posting!

View File

@ -4,11 +4,11 @@ title: CDN Hosting
weight: 4
---
This tutorial guides you through the steps for adding Static CMS via a public CDN to a site that's built with a common [static site generator](https://www.staticgen.com/), like Jekyll, Next, Hugo, Hexo, or Gatsby. Alternatively, you can [start from a template](/docs/start-with-a-template) or dive right into [configuration options](/docs/configuration-options).
This tutorial guides you through the steps for adding Static CMS via a public CDN to a site that is built with a common [static site generator](https://www.staticgen.com/), like Jekyll, Next, Hugo, Hexo, or Gatsby. Alternatively, you can [start from a template](/docs/start-with-a-template) or dive right into [configuration options](/docs/configuration-options).
## App File Structure
A static `admin` folder contains all Static CMS files, stored at the root of your published site. Where you store this folder in the source files depends on your static site generator. Here's the static file location for a few of the most popular static site generators:
A static `admin` folder contains all Static CMS files, stored at the root of your published site. Where you store this folder in the source files depends on your static site generator. Here is the static file location for a few of the most popular static site generators:
| These generators | store static files in |
| ------------------------------------------------------- | --------------------- |
@ -25,9 +25,9 @@ A static `admin` folder contains all Static CMS files, stored at the root of you
| preact-cli | `/src/static` |
| Docusaurus | `/static` |
If your generator isn't listed here, you can check its documentation, or as a shortcut, look in your project for a `css` or `images` folder. The contents of folders like that are usually processed as static files, so it's likely you can store your `admin` folder next to those. (When you've found the location, feel free to add it to these docs by [filing a pull request](https://github.com/StaticJsCMS/static-cms/blob/main/CONTRIBUTING.md#pull-requests)!)
If your generator is not listed here, you can check its documentation, or as a shortcut, look in your project for a `css` or `images` folder. The contents of folders like that are usually processed as static files, so it is likely you can store your `admin` folder next to those. (When you have found the location, feel free to add it to these docs by [filing a pull request](https://github.com/StaticJsCMS/static-cms/blob/main/CONTRIBUTING.md#pull-requests)!)
Inside the `admin` folder, you'll create two files:
Inside the `admin` folder, you will create two files:
```bash
admin
@ -35,7 +35,7 @@ admin
└ config.yml
```
The first file, `admin/index.html`, is the entry point for the Static CMS admin interface. This means that users navigate to `yoursite.com/admin/` to access it. On the code side, it's a basic HTML starter page that loads the Static CMS JavaScript file from a public CDN and initializes it. The second file, `admin/config.yml`, is the heart of your Static CMS installation, and a bit more complex. The [Configuration](#configuration) section covers the details.
The first file, `admin/index.html`, is the entry point for the Static CMS admin interface. This means that users navigate to `yoursite.com/admin/` to access it. On the code side, it is a basic HTML starter page that loads the Static CMS JavaScript file from a public CDN and initializes it. The second file, `admin/config.yml`, is the heart of your Static CMS installation, and a bit more complex. The [Configuration](#configuration) section covers the details.
In this example, we pull the `admin/index.html` file from a public CDN.
@ -62,11 +62,11 @@ In the code above the `script` is loaded from the `unpkg` CDN. Should there be a
## Configuration
Configuration is different for every site, so we'll break it down into parts. Add all the code snippets in this section to your `admin/config.yml` file. Alternatively, you can use a javascript file (`admin/config.js`) instead of a yaml file. Simply import the javascript config and pass it into your `CMS.init({ config })` call.
Configuration is different for every site, so we will break it down into parts. Add all the code snippets in this section to your `admin/config.yml` file. Alternatively, you can use a javascript file (`admin/config.js`) instead of a yaml file. Simply import the javascript config and pass it into your `CMS.init({ config })` call.
### Backend
We're using [Netlify](https://www.netlify.com) for our hosting and authentication in this tutorial, so backend configuration is fairly straightforward.
We are using [Netlify](https://www.netlify.com) for our hosting and authentication in this tutorial, so backend configuration is fairly straightforward.
For GitHub repositories, you can start your Static CMS `config` file with these lines:
@ -88,7 +88,22 @@ backend: {
_(For GitLab repositories, use [GitLab backend](/docs/gitlab-backend) and for Bitbucket repositories, use [Bitbucket backend](/docs/bitbucket-backend).)_
The configuration above specifies your backend protocol and your publication branch. Git Gateway is an open source API that acts as a proxy between authenticated users of your site and your site repo. (We'll get to the details of that in the [Authentication section](#authentication) below.) If you leave out the `branch` declaration, it defaults to `main`.
The configuration above specifies your backend protocol and your publication branch. Git Gateway is an open source API that acts as a proxy between authenticated users of your site and your site repo. (We will get to the details of that in the [Authentication section](#authentication) below.) If you leave out the `branch` declaration, it defaults to `main`.
### Editorial Workflow <BetaImage />
By default, saving a post in the CMS interface pushes a commit directly to the publication branch specified in `backend`. However, you also have the option to enable the [Editorial Workflow](/docs/editorial-workflow), which adds an interface for drafting, reviewing, and approving posts. To do this, add the following line to your `config.yml`:
<CodeTabs>
```yaml
publish_mode: editorial_workflow
```
```js
publish_mode: 'editorial_workflow'
```
</CodeTabs>
### Media and Public Folders
@ -106,9 +121,9 @@ media_folder: 'images/uploads', // Media files will be stored in the repo under
</CodeTabs>
If you're creating a new folder for uploaded media, you'll need to know where your static site generator expects static files. You can refer to the paths outlined above in [App File Structure](#app-file-structure), and put your media folder in the same location where you put the `admin` folder.
If you are creating a new folder for uploaded media, you will need to know where your static site generator expects static files. You can refer to the paths outlined above in [App File Structure](#app-file-structure), and put your media folder in the same location where you put the `admin` folder.
Note that the `media_folder` file path is relative to the project root, so the example above would work for Jekyll, GitBook, or any other generator that stores static files at the project root. However, it would not work for Hugo, Next, Hexo, Middleman or others that store static files in a subfolder. Here's an example that could work for a Hugo site:
Note that the `media_folder` file path is relative to the project root, so the example above would work for Jekyll, GitBook, or any other generator that stores static files at the project root. However, it would not work for Hugo, Next, Hexo, Middleman or others that store static files in a subfolder. Here is an example that could work for a Hugo site:
<CodeTabs>
```yaml
@ -124,7 +139,7 @@ public_folder: '/images/uploads', // The src attribute for uploaded media will b
</CodeTabs>
The configuration above adds a new setting, `public_folder`. While `media_folder` specifies where uploaded files are saved in the repo, `public_folder` indicates where they are found in the published site. Image `src` attributes use this path, which is relative to the file where it's called. For this reason, we usually start the path at the site root, using the opening `/`.
The configuration above adds a new setting, `public_folder`. While `media_folder` specifies where uploaded files are saved in the repo, `public_folder` indicates where they are found in the published site. Image `src` attributes use this path, which is relative to the file where it is called. For this reason, we usually start the path at the site root, using the opening `/`.
_If `public_folder` is not set, Static CMS defaults to the same value as `media_folder`, adding an opening `/` if one is not included._
@ -132,7 +147,7 @@ _If `public_folder` is not set, Static CMS defaults to the same value as `media_
Collections define the structure for the different content types on your static site. Since every site is different, the `collections` settings differ greatly from one site to the next.
Let's say your site has a blog, with the posts stored in `_posts/blog`, and files saved in a date-title format, like `1999-12-31-lets-party.md`. Each post begins with settings in yaml-formatted front matter, like so:
Let us say your site has a blog, with the posts stored in `_posts/blog`, and files saved in a date-title format, like `1999-12-31-lets-party.md`. Each post begins with settings in yaml-formatted front matter, like so:
```yaml
---
@ -154,7 +169,7 @@ collections:
label: 'Blog' # Used in the UI
folder: '_posts/blog' # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
slug: '{{year}}-{{month}}-{{day}}-{{slug}}' # Filename template, e.g., YYYY-MM-DD-title.md
slug: '{{year}}-{{month}}-{{day}}-{{slug}}' # Filename template, e.g., yyyy-MM-dd-title.md
fields: # The fields for each document, usually in front matter
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' }
- { label: 'Title', name: 'title', widget: 'string' }
@ -171,7 +186,7 @@ collections: [
label: 'Blog', // Used in the UI
folder: '_posts/blog', // The path to the folder where the documents are stored
create: true, // Allow users to create new documents in this collection
slug: '{{year}}-{{month}}-{{day}}-{{slug}}', // Filename template, e.g., YYYY-MM-DD-title.md
slug: '{{year}}-{{month}}-{{day}}-{{slug}}', // Filename template, e.g., yyyy-MM-dd-title.md
fields: [
// The fields for each document, usually in front matter
{ label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' },
@ -187,7 +202,7 @@ collections: [
</CodeTabs>
Let's break that down:
Let us break that down:
| Field | Description |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@ -244,7 +259,7 @@ collections: [
## Authentication
Now that you have your Static CMS files in place and configured, all that's left is to enable authentication. We're using the [Netlify](https://www.netlify.com/) platform here because it's one of the quickest ways to get started, but you can learn about other authentication options in the [Backends](/docs/backends-overview) doc.
Now that you have your Static CMS files in place and configured, all that is left is to enable authentication. We are using the [Netlify](https://www.netlify.com/) platform here because it is one of the quickest ways to get started, but you can learn about other authentication options in the [Backends](/docs/backends-overview) doc.
### Setup on Netlify
@ -255,9 +270,9 @@ Netlify offers a built-in authentication service called Identity. In order to us
Netlify's Identity and Git Gateway services allow you to manage CMS admin users for your site without requiring them to have an account with your Git host or commit access on your repo. From your site dashboard on Netlify:
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you want only invited users to access your CMS, but if you're just experimenting, you can leave it open for convenience.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you want only invited users to access your CMS, but if you are just experimenting, you can leave it open for convenience.
3. If you'd like to allow one-click login with services like Google and GitHub, check the boxes next to the services you'd like to use, under **External providers**.
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This authenticates with your Git host and generates an API access token. In this case, we're leaving the **Roles** field blank, which means any logged in user may access Static CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This authenticates with your Git host and generates an API access token. In this case, we are leaving the **Roles** field blank, which means any logged in user may access Static CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
### Add the Netlify Identity Widget
@ -267,7 +282,7 @@ With the backend set to handle authentication, now you need a frontend interface
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
```
Add this to the `<head>` of your CMS index page at `/admin/index.html`, as well as the `<head>` of your site's main index page. Depending on how your site generator is set up, this may mean you need to add it to the default template, or to a "partial" or "include" template. If you can find where the site stylesheet is linked, that's probably the right place. Alternatively, you can include the script in your site using Netlify's [Script Injection](https://www.netlify.com/docs/inject-analytics-snippets/) feature.
Add this to the `<head>` of your CMS index page at `/admin/index.html`, as well as the `<head>` of your site's main index page. Depending on how your site generator is set up, this may mean you need to add it to the default template, or to a "partial" or "include" template. If you can find where the site stylesheet is linked, that is probably the right place. Alternatively, you can include the script in your site using Netlify's [Script Injection](https://www.netlify.com/docs/inject-analytics-snippets/) feature.
When a user logs in with the Netlify Identity widget, an access token directs to the site homepage. In order to complete the login and get back to Static CMS, redirect the user back to the `/admin/` path. To do this, add the following script before the closing `body` tag of your site's main index page:
@ -295,6 +310,6 @@ If you set your registration preference to "Invite only," invite yourself (and a
If you left your site registration open, or for return visits after confirming an email invitation, access your site's CMS at `yoursite.com/admin/`.
**Note:** No matter where you access Static CMS — whether running locally, in a staging environment, or in your published site — it always fetches and commits files in your hosted repository (for example, on GitHub), on the branch you configured in your Static CMS `config` file. This means that content fetched in the admin UI matches the content in the repository, which may be different from your locally running site. It also means that content saved using the admin UI saves directly to the hosted repository, even if you're running the UI locally or in staging.
**Note:** No matter where you access Static CMS — whether running locally, in a staging environment, or in your published site — it always fetches and commits files in your hosted repository (for example, on GitHub), on the branch you configured in your Static CMS `config` file. This means that content fetched in the admin UI matches the content in the repository, which may be different from your locally running site. It also means that content saved using the admin UI saves directly to the hosted repository, even if you are running the UI locally or in staging.
Happy posting!

View File

@ -10,7 +10,7 @@ You can add Static CMS to your site in two different ways:
## CDN hosting
Adding Static CMS via a public CDN to a site that's built with a common static site generator, like Jekyll, Hugo, Hexo, or Gatsby. Alternatively, is a quick and easy way to get started. You can start from a [template](/docs/start-with-a-template) or use [this guide](/docs/add-to-your-site-cdn) to get started.
Adding Static CMS via a public CDN to a site that is built with a common static site generator, like Jekyll, Hugo, Hexo, or Gatsby. Alternatively, is a quick and easy way to get started. You can start from a [template](/docs/start-with-a-template) or use [this guide](/docs/add-to-your-site-cdn) to get started.
## Bundling

View File

@ -4,7 +4,7 @@ title: Custom Links & Pages
weight: 60
---
The Static CMS exposes a `window.CMS` global object that you can use to register external links or links custom pages, via `registerAdditionalLink`. The links are displayed at the bottom of the navigation menu in the order they are registered.
Static CMS exposes a `window.CMS` global object that you can use to register external links or links custom pages, via `registerAdditionalLink`. The links are displayed at the bottom of the navigation menu in the order they are registered.
### React Components Inline

View File

@ -10,15 +10,16 @@ A backend is JavaScript code that allows Static CMS to communicate with a servic
Individual backends provide their own configuration documentation, but there are some configuration options that are common to multiple backends. A full reference is below. Note that these are properties of the `backend` field, and should be nested under that field.
| Name | Type | Default | Description |
| ------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | 'git-gateway'<br />\| 'github'<br />\| 'gitlab'<br />\| 'bitbucket'<br />\| 'gitea'<br />\|'test-repo'<br />\| 'proxy' | | The backend git provider |
| repo | string | | Required for `github`, `gitlab`, `gitea` and `bitbucket` backends. Ignored by `git-gateway`. Follows the pattern `[org-or-username]/[repo-name]` |
| branch | string | `main` | _Optional_. The branch where published content is stored. All CMS commits and PRs are made to this branch |
| api_root | string | GitHub<br />`https://api.github.com`<br /><br />GitLab<br/>`https://gitlab.com/api/v4`<br /><br />Bitbucket<br />`https://api.bitbucket.org/2.0`<br /><br />Gitea<br />`https://try.gitea.io/api/v1` | _Optional_. The API endpoint. Only necessary in certain cases, like with GitHub Enterprise or self-hosted GitLab |
| site_domain | string | `location.hostname`<br /><br />On `localhost`<br />`cms.netlify.com` | _Optional_. Sets the `site_id` query param sent to the API endpoint. Non-Netlify auth setups will often need to set this for local development to work properly |
| base_url | string | GitHub or Bitbucket<br />`https://api.netlify.com`<br /><br />GitLab<br />`https://gitlab.com`<br /><br />Gitea<br />`https://try.gitea.io` | _Optional_. OAuth client hostname (just the base domain, no path). **Required** when using an external OAuth server or self-hosted GitLab/Gitea |
| auth_endpoint | string | GitHub or Bitbucket<br />`auth`<br /><br />GitLab<br />`oauth/authorize` | _Optional_. Path to append to `base_url` for authentication requests. |
| Name | Type | Default | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | 'git-gateway'<br />\| 'github'<br />\| 'gitlab'<br />\| 'bitbucket'<br />\| 'gitea'<br />\|'test-repo'<br />\| 'proxy' | | The backend git provider |
| repo | string | | Required for `github`, `gitlab`, `gitea` and `bitbucket` backends. Ignored by `git-gateway`. Follows the pattern `[org-or-username]/[repo-name]` |
| branch | string | `main` | _Optional_. The branch where published content is stored. All CMS commits and PRs are made to this branch |
| api_root | string | GitHub<br />`https://api.github.com`<br /><br />GitLab<br/>`https://gitlab.com/api/v4`<br /><br />Bitbucket<br />`https://api.bitbucket.org/2.0`<br /><br />Gitea<br />`https://try.gitea.io/api/v1` | _Optional_. The API endpoint. Only necessary in certain cases, like with GitHub Enterprise or self-hosted GitLab |
| site_domain | string | `location.hostname`<br /><br />On `localhost`<br />`cms.netlify.com` | _Optional_. Sets the `site_id` query param sent to the API endpoint. Non-Netlify auth setups will often need to set this for local development to work properly |
| base_url | string | GitHub or Bitbucket<br />`https://api.netlify.com`<br /><br />GitLab<br />`https://gitlab.com`<br /><br />Gitea<br />`https://try.gitea.io` | _Optional_. OAuth client hostname (just the base domain, no path). **Required** when using an external OAuth server or self-hosted GitLab/Gitea |
| auth_endpoint | string | GitHub or Bitbucket<br />`auth`<br /><br />GitLab<br />`oauth/authorize` | _Optional_. Path to append to `base_url` for authentication requests. |
| cms_label_prefix | string | `cms/` | Pull (or Merge) Requests label prefix when using editorial workflow. Optional. |
## Creating a New Backend

View File

@ -9,191 +9,22 @@ weight: 200
fix the features as well as update the docs. Use at your own risk.
</Alert>
Static CMS runs new functionality in an open beta format from time to time. That means that this functionality is totally available for use, an it might be ready for primetime, but it could break or change without notice.
Static CMS runs new functionality in an open beta format from time to time. That means that this functionality is fully available for use, and it might be ready for primetime, but it could break or change without notice.
**Use these features at your own risk.**
## Folder Collections Path
## Editorial Workflow
By default Static CMS stores folder collection content under the folder specified in the collection setting. You can now specify an additional `path` template (similar to the `slug` template) to control the content destination.
By default, all entries created or edited in Static CMS are committed directly into the main repository branch.
This allows saving content in subfolders, e.g. configuring `path: '{{year}}/{{slug}}'` will save the content under `posts/2019/post-title.md`.
The publish_mode option allows you to enable "Editorial Workflow" mode for more control over the content publishing phases. All unpublished entries will be arranged in a board according to their status, and they can be further reviewed and edited before going live.
See [Folder Collections Path](/docs/collection-types#folder-collections-path) for more information.
See [Editorial Workflow](/docs/editorial-workflow) for more information.
## Nested Collections
## Open Authoring
Nested collections is a beta feature that allows a folder collection to show a nested structure of entries and edit the locations of the entries. This feature is useful when you have a complex folder structure and may not want to create separate collections for every directory.
When using the [GitHub backend](/docs/github-backend), you can use Static CMS to accept contributions from GitHub users without giving them access to your repository. When they make changes in the CMS, the CMS forks your repository for them behind the scenes, and all the changes are made to the fork. When the contributor is ready to submit their changes, they can set their draft as ready for review in the CMS. This triggers a pull request to your repository, which you can merge using the GitHub UI.
See [Nested Collections](/docs/collection-types#nested-collections) for more information.
At the same time, any contributors who _do_ have write access to the repository can continue to use Static CMS normally.
## Image widget file size limit
You can set a limit to as what the maximum file size of a file is that users can upload directly into a image field.
See [Media Library](/docs/configuration-options#media-library) for more information.
## Media Library Folders
Adds support for viewing subfolders and creating new subfolders in the media library, under your configured `media_folder`.
See [Media Library](/docs/configuration-options#media-library) for more information.
## Summary string template transformations
You can apply transformations on fields in a summary string template using filter notation syntax.
Example config:
<CodeTabs>
```yaml
collections:
- name: 'posts'
label: 'Posts'
folder: '_posts'
summary: "{{title | upper}} - {{date | date('YYYY-MM-DD')}} - {{body | truncate(20, '***')}}"
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
```js
collections: [
{
name: 'posts',
label: 'Posts',
folder: '_posts',
summary: "{{title | upper}} - {{date | date('YYYY-MM-DD')}} - {{body | truncate(20, '***')}}",
fields: [
{ label: 'Title', name: 'title', widget: 'string' },
{ label: 'Publish Date', name: 'date', widget: 'datetime' },
{ label: 'Body', name: 'body', widget: 'markdown' },
],
},
],
```
</CodeTabs>
The above config will transform the title field to uppercase and format the date field using `YYYY-MM-DD` format.
Available transformations are `upper`, `lower`, `date('<format>')`, `default('defaultValue')`, `ternary('valueForTrue','valueForFalse')` and `truncate(<number>)`/`truncate(<number>, '<string>')`
## Registering to CMS Events
You can execute a function when a specific CMS event occurs. Supported events are `mounted`, `login`, `logout`, `change`, `preSave` and `postSave`.
### Mounted
The `mounted` event handler fires once the CMS is fully loaded.
```javascript
CMS.registerEventListener({
name: 'mounted',
handler: () => {
// your code here
},
});
```
### Login
The `login` event handler fires when a user logs into the CMS.
```javascript
CMS.registerEventListener({
name: 'login',
handler: ({ author: { login, name } }) => {
// your code here
},
});
```
### Logout
The `logout` event handler fires when a user logs out of the CMS.
```javascript
CMS.registerEventListener({
name: 'logout',
handler: () => {
// your code here
},
});
```
### Pre Save
The `preSave` event handler fires before the changes have been saved to your git backend, and can be used to modify the entry data like so:
```javascript
CMS.registerEventListener({
name: 'preSave',
collection: 'posts',
handler: ({ data: { entry } }) => {
return {
...entry.data,
title: 'new title',
};
},
});
```
### Post Save
The `postSave` event handler fires after the changes have been saved to your git backend.
```javascript
CMS.registerEventListener({
name: 'postSave',
collection: 'posts',
handler: ({ data: { entry } }) => {
// your code here
},
});
```
### Change
The `change` event handler must provide a field name, and can be used to modify the entry data like so:
```javascript
CMS.registerEventListener({
name: 'change',
collection: 'posts',
field: 'path.to.my.field',
handler: ({ data, collection, field }) => {
const currentValue = data.path.to.my.field;
return {
...data,
path: {
...data.path,
to: {
...data.path.to,
my: {
...data.path.to.my,
field: `new${currentValue}`
}
}
}
};
},
});
```
## i18n Support
Static CMS can provide a side by side interface for authoring content in multiple languages. Configuring Static CMS for i18n support requires top level configuration, collection level configuration and field level configuration.
## Gitea Backend
For repositories stored on Gitea, the gitea backend allows CMS users to log in directly with their Gitea account. Note that all users must have push access to your content repository for this to work.
See [Gitea Backend](/docs/gitea-backend) for more information.
## Large Media Support
Netlify Large Media allows you to store your media files outside of your git backend. This is helpful if you are trying to store large media files.
See [Netlify Large Media](/docs/netlify-large-media) for more information.
See [Open Authoring](/docs/open-authoring) for more information.

View File

@ -0,0 +1,288 @@
---
group: Customization
title: Events Hooks
weight: 110
---
You can execute a function when a specific event occurs within Static CMSD.
Supported events are:
| Name | Description |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| [mounted](#mounted-event) | Event fires once Static CMS is fully loaded |
| [login](#login-event) | Event fires when a user logs into Static CMS |
| [logout](#logout-event) | Event fires when a user logs out of Static CMS |
| [change](#change-event) | Event fires when a user changes the value of a field in the editor |
| [preSave](#pre-save-event) | Event fires before the changes have been saved to your git backend |
| [postSave](#post-save-event) | Event fires after the changes have been saved to your git backend |
| [prePublish](#pre-publish-event) | _**Editorial Workflow ONLY**_. Event fires before the entry is "published", before the PR is merged into default branch |
| [postPublish](#post-publish-event) | _**Editorial Workflow ONLY**_. Event fires after the entry is "published", after the PR is merged into default branch |
## Mounted Event
The `mounted` event handler fires once Static CMS is fully loaded.
```javascript
CMS.registerEventListener({
name: 'mounted',
handler: () => {
// your code here
},
});
```
## Login Event
The `login` event handler fires when a user logs into Static CMS.
```javascript
CMS.registerEventListener({
name: 'login',
handler: ({ author: { login, name } }) => {
// your code here
},
});
```
## Logout Event
The `logout` event handler fires when a user logs out of Static CMS.
```javascript
CMS.registerEventListener({
name: 'logout',
handler: () => {
// your code here
},
});
```
## Change Event
The `change` event handler fires when a user changes the value of a field in the editor. Event listeners for `change` can optionally provide collection, file and field names. They can also be used to modify the entry data.
```javascript
// Listen for ALL change events
CMS.registerEventListener({
name: 'change',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all change events in a specific collection
CMS.registerEventListener({
name: 'change',
collection: 'posts',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all change events in a specific file in a collection
CMS.registerEventListener({
name: 'change',
collection: 'settings',
file: 'global',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all change events in a specific field in a collection
CMS.registerEventListener({
name: 'change',
collection: 'posts',
// file: 'global', // You can specify a file if in a file collection
field: 'path.to.my.field',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Alter the entry data when a specific field changes
CMS.registerEventListener({
name: 'change',
collection: 'posts',
// file: 'global', // You can specify a file if in a file collection
field: 'path.to.my.field',
handler: ({ data, collection, field }) => {
const currentValue = data.path.to.my.field;
return {
...data,
path: {
...data.path,
to: {
...data.path.to,
my: {
...data.path.to.my,
field: `new${currentValue}`,
},
},
},
};
},
});
```
## Pre Save Event
The `preSave` event handler fires before the changes have been saved to your git backend, and can be used to modify the entry data.
```javascript
// Listen for ALL preSave events
CMS.registerEventListener({
name: 'preSave',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all preSave events in a specific collection
CMS.registerEventListener({
name: 'preSave',
collection: 'posts',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all preSave events in a specific file in a collection
CMS.registerEventListener({
name: 'preSave',
collection: 'settings',
file: 'global',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Alter the entry data
CMS.registerEventListener({
name: 'preSave',
collection: 'posts',
// file: 'global', // You can specify a file if in a file collection
handler: ({ data, collection, field }) => {
const currentValue = data.path.to.my.field;
return {
...data,
path: {
...data.path,
to: {
...data.path.to,
my: {
...data.path.to.my,
field: `new${currentValue}`,
},
},
},
};
},
});
```
## Post Save Event
The `postSave` event handler fires after the changes have been saved to your git backend.
```javascript
// Listen for ALL postSave events
CMS.registerEventListener({
name: 'postSave',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all postSave events in a specific collection
CMS.registerEventListener({
name: 'postSave',
collection: 'posts',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all postSave events in a specific file in a collection
CMS.registerEventListener({
name: 'postSave',
collection: 'settings',
file: 'global',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
```
## Pre Publish Event
<Alert severity="info">Editorial Workflow ONLY</Alert>
The `prePublish` event handler fires before the entry is "published", before the PR is merged into default branch.
```javascript
// Listen for ALL prePublish events
CMS.registerEventListener({
name: 'prePublish',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all prePublish events in a specific collection
CMS.registerEventListener({
name: 'prePublish',
collection: 'posts',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all prePublish events in a specific file in a collection
CMS.registerEventListener({
name: 'prePublish',
collection: 'settings',
file: 'global',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
```
## Post Publish Event
<Alert severity="info">Editorial Workflow ONLY</Alert>
The `postPublish` event handler fires after the entry is "published", after the PR is merged into default branch.
```javascript
// Listen for ALL postPublish events
CMS.registerEventListener({
name: 'postPublish',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all postPublish events in a specific collection
CMS.registerEventListener({
name: 'postPublish',
collection: 'posts',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
// Listen for all postPublish events in a specific file in a collection
CMS.registerEventListener({
name: 'postPublish',
collection: 'settings',
file: 'global',
handler: ({ data, collection, field }) => {
// Your handler code
},
});
```

View File

@ -17,6 +17,7 @@ weight: 9
| files or folder | [Collection Files](/docs/collection-types#file-collections)<br />\| [Collection Folder](/docs/collection-types#folder-collections) | | **Requires one of these**: Specifies the collection type and location; details in [collection types](/docs/collection-types) |
| filter | FilterRule<br />\| List of FilterRules | | _Optional_. Field and file filter for [Folder Collections](/docs/collection-types#folder-collections). See [filtered folder collections](/docs/collection-types#filtered-folder-collections) |
| create | boolean | `false` | _Optional_. **For [Folder Collections](/docs/collection-types#folder-collections) only**<br />`true` - Allows users to create new items in the collection |
| publish | boolean | `true` | _Optional_ For `publish_mode: editorial_workflow` only;<br />`false` hides UI publishing controls for a collection |
| hide | boolean | `false` | _Optional_. `true` hides a collection in the Static CMS UI. Useful when using the relation widget to hide referenced collections |
| delete | boolean | `true` | _Optional_. `false` prevents users from deleting items in a collection |
| extension | string | | _Optional_. See [extension and format](#extension-and-format) below |
@ -66,7 +67,7 @@ You may also specify a custom `extension` not included in the list above, as lon
- `yml` or `yaml`: parses and saves files as YAML-formatted data files; saves with `yml` extension by default
- `json`: parses and saves files as JSON-formatted data files; saves with `json` extension by default
- `toml`: parses and saves files as TOML-formatted data files; saves with `toml` extension by default
- `frontmatter`: parses files and saves files with data frontmatter followed by an unparsed body text (edited using a `body` field); saves with `md` extension by default; default for collections that can't be inferred. Collections with `frontmatter` format (either inferred or explicitly set) can parse files with frontmatter in YAML or JSON format. However, they will be saved with YAML frontmatter.
- `frontmatter`: parses files and saves files with data frontmatter followed by an unparsed body text (edited using a `body` field); saves with `md` extension by default; default for collections that cannot be inferred. Collections with `frontmatter` format (either inferred or explicitly set) can parse files with frontmatter in YAML or JSON format. However, they will be saved with YAML frontmatter.
- `yaml-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved only as YAML, followed by unparsed body text. The default delimiter for this option is `---`.
- `json-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved as JSON, followed by unparsed body text. The default delimiter for this option is `{` `}`.
- `toml-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved only as TOML, followed by unparsed body text. The default delimiter for this option is `+++`.
@ -219,6 +220,55 @@ summary: 'Version: {{version}} - {{title}}',
</CodeTabs>
### Template Transformations
You can apply transformations on fields in a summary string template using filter notation syntax.
Example config:
<CodeTabs>
```yaml
collections:
- name: 'posts'
label: 'Posts'
folder: '_posts'
summary: "{{title | upper}} - {{date | date('yyyy-MM-dd')}} - {{body | truncate(20, '***')}}"
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
```js
collections: [
{
name: 'posts',
label: 'Posts',
folder: '_posts',
summary: "{{title | upper}} - {{date | date('yyyy-MM-dd')}} - {{body | truncate(20, '***')}}",
fields: [
{ label: 'Title', name: 'title', widget: 'string' },
{ label: 'Publish Date', name: 'date', widget: 'datetime' },
{ label: 'Body', name: 'body', widget: 'markdown' },
],
},
],
```
</CodeTabs>
The above config will transform the title field to uppercase and format the date field using `yyyy-MM-dd` format.
Available transformations are:
| Name | Format | Description |
| -------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| upper | `upper` | Transforms the value to uppercase |
| lower | `lower` | Transforms the value to lowercase |
| date | `date('<format>')` | Formats a date string in the provided format. Accepts [date-fns tokens](https://date-fns.org/docs/format) |
| default | `default('defaultValue')` | Provides default value if no field value |
| ternary | `ternary('valueForTrue','valueForFalse')` | <ul><li>If field has value, show `valueForTrue`</li><li>If field does not have a value, show `valueForFalse`</li></ul> |
| truncate | `truncate(<number>)`<br />`truncate(<number>, '<string>')` | Truncates text to a specified length. An optional replacement string for the omitted text can be provided as a second parameter |
## Sortable Fields
The `sortable_fields` setting is an optional object with the following options:
@ -230,7 +280,7 @@ The `sortable_fields` setting is an optional object with the following options:
Defaults to inferring `title`, `date`, `author` and `description` fields and will also show `Update On` sort field in git based backends.
When `author` field can't be inferred commit author will be used.
When `author` field cannot be inferred commit author will be used.
<CodeTabs>
```yaml
@ -280,79 +330,122 @@ sortable_fields: {
## View Filters
The `view_filters` setting is an optional list of predefined view filters to show in the UI.
Defaults to an empty list.
The `view_filters` setting is an optional property which takes a list of predefined view filters to show in the UI and an optional default view filter.
<CodeTabs>
```yaml
view_filters:
- label: "Alice's and Bob's Posts"
field: author
pattern: 'Alice|Bob'
- label: 'Posts published in 2020'
field: date
pattern: '2020'
- label: Drafts
field: draft
pattern: true
default: drafts
filters:
- name: alice-and-bob
label: "Alice's and Bob's Posts"
field: author
pattern: 'Alice|Bob'
- name: posts-2020
label: 'Posts published in 2020'
field: date
pattern: '2020'
- name: drafts
label: Drafts
field: draft
pattern: true
```
```js
view_filters: [
{
label: "Alice's and Bob's Posts",
field: 'author',
pattern: 'Alice|Bob',
},
{
label: 'Posts published in 2020',
field: 'date',
pattern: '2020',
},
{
label: 'Drafts',
field: 'draft',
pattern: true,
},
],
view_filters: {
default: 'drafts',
filters: [
{
name: 'alice-and-bob',
label: "Alice's and Bob's Posts",
field: 'author',
pattern: 'Alice|Bob',
},
{
name: 'posts-2020',
label: 'Posts published in 2020',
field: 'date',
pattern: '2020',
},
{
name: 'drafts',
label: 'Drafts',
field: 'draft',
pattern: true,
},
],
},
```
</CodeTabs>
## View Groups
The `view_groups` setting is an optional list of predefined view groups to show in the UI.
Defaults to an empty list.
The `view_groups` setting is an optional property which takes a list of predefined view groups to show in the UI and an optional default view group.
<CodeTabs>
```yaml
view_groups:
- label: Year
field: date
# groups items based on the value matched by the pattern
pattern: \d{4}
- label: Drafts
field: draft
default: by-year
groups:
- name: by-year
label: Year
field: date
# groups items based on the value matched by the pattern
pattern: \d{4}
- name: drafts
label: Drafts
field: draft
```
```js
view_groups: [
{
label: 'Year',
field: 'date',
pattern: '\\d{4}',
},
{
label: 'Drafts',
field: 'draft',
},
],
view_groups: {
default: by-year
groups: [
{
name: 'by-year',
label: 'Year',
field: 'date',
pattern: '\\d{4}',
},
{
name: 'drafts',
label: 'Drafts',
field: 'draft',
},
],
},
```
</CodeTabs>
## Media Library
The `media_library` settings allows customization of the media library at the collection level. See [Media Library](/docs/configuration-options#media-library) for more details.
The `media_library` settings allows customization of the media library at the collection level.
### Options
| Name | Type | Default | Description |
| --------------------- | ------- | -------- | -------------------------------------------------------------------------------------- |
| max_file_size | number | `512000` | _Optional_. The max size, in bytes, of files that can be uploaded to the media library |
| folder_support | boolean | `false` | _Optional_. Enables directory navigation and folder creation in your media library |
### Example
<CodeTabs>
```yaml
media_library:
max_file_size: 512000
folder_support: true
```
```js
{
media_library: {
max_file_size: 512000,
folder_support: true
}
}
```
</CodeTabs>

View File

@ -471,7 +471,7 @@ collections: [
</CodeTabs>
### Folder Collections Path <BetaImage />
### Folder Collections Path
By default Static CMS stores folder collection content under the folder specified in the collection setting.
@ -577,9 +577,9 @@ Supports all of the [`slug` templates](/docs/configuration-options#slug) and:
- `{{media_folder}}` The global `media_folder`.
- `{{public_folder}}` The global `public_folder`.
### Nested Collections <BetaImage />
### Nested Collections
Nested collections is a beta feature that allows a folder collection to show a nested structure of entries and edit the locations of the entries. This feature is useful when you have a complex folder structure and may not want to create separate collections for every directory.
Nested collections allow a folder collection to show a nested structure of entries and edit the locations of the entries. This feature is useful when you have a complex folder structure and may not want to create separate collections for every directory.
Example configuration:

View File

@ -25,7 +25,7 @@ _This setting is required._
The `backend` option specifies how to access the content for your site, including authentication. Full details and code samples can be found in [Backends](/docs/backends-overview).
**Note**: no matter where you access Static CMS — whether running locally, in a staging environment, or in your published site — it will always fetch and commit files in your hosted repository (for example, on GitHub), on the branch you configured in your Static CMS config file. This means that content fetched in the admin UI will match the content in the repository, which may be different from your locally running site. It also means that content saved using the admin UI will save directly to the hosted repository, even if you're running the UI locally or in staging. If you want to have your local CMS write to a local repository, [try the local_backend setting](/docs/local-backend).
**Note**: no matter where you access Static CMS — whether running locally, in a staging environment, or in your published site — it will always fetch and commit files in your hosted repository (for example, on GitHub), on the branch you configured in your Static CMS config file. This means that content fetched in the admin UI will match the content in the repository, which may be different from your locally running site. It also means that content saved using the admin UI will save directly to the hosted repository, even if you are running the UI locally or in staging. If you want to have your local CMS write to a local repository, [try the local_backend setting](/docs/local-backend).
### Commit Message Templates
@ -44,6 +44,7 @@ backend:
delete: Delete {{collection}} "{{slug}}"
uploadMedia: Upload "{{path}}"
deleteMedia: Delete "{{path}}"
openAuthoring: "{{message}}"
```
```js
@ -54,6 +55,7 @@ backend: {
delete: 'Delete {{collection}} "{{slug}}"',
uploadMedia: 'Upload "{{path}}"',
deleteMedia: 'Delete "{{path}}"',
openAuthoring: '"{{message}}"'
},
},
```
@ -82,6 +84,14 @@ Template tags produce the following output:
- `{{author-login}}`: login/username of the author
- `{{author-name}}`: full name of the author (might be empty based on the user's profile)
## Publish Mode
By default, all entries created or edited in Static CMS are committed directly into the main repository branch.
The `publish_mode` option allows you to enable "Editorial Workflow" mode for more control over the content publishing phases. All unpublished entries will be arranged on a dashboard according to their status, and they can be further reviewed and edited before going live.
See [Editorial Workflow](/docs/editorial-workflow) for more information.
## Media and Public Folders
Static CMS users can upload files to your repository using the Media Gallery. The following settings specify where these files are saved, and where they can be accessed on your built site.
@ -122,16 +132,17 @@ Based on the settings above, if a user used an image widget field called `avatar
This setting can be set to an absolute URL e.g. `https://netlify.com/media` should you wish, however in general this is not advisable as content should have relative paths to other content.
## Media Library <BetaImage />
## Media Library
The `media_library` settings allows customization of the media library.
### Options
| Name | Type | Default | Description |
| -------------- | ------- | -------- | -------------------------------------------------------------------------------------- |
| max_file_size | number | `512000` | _Optional_. <BetaImage /> The max size, in bytes, of files that can be uploaded to the media library |
| folder_support | boolean | `false` | _Optional_. <BetaImage /> Enables directory navigation and folder creation in your media library |
| Name | Type | Default | Description |
| --------------------- | ------- | -------- | -------------------------------------------------------------------------------------- |
| max_file_size | number | `512000` | _Optional_. The max size, in bytes, of files that can be uploaded to the media library |
| folder_support | boolean | `false` | _Optional_. Enables directory navigation and folder creation in your media library |
| display_in_navigation | boolean | `true` | _Optional_. Displays the "Media" link in the main navigation |
### Example
@ -293,4 +304,34 @@ The `collections` setting is the heart of your Static CMS configuration, as it d
## Disable Local Backup
When the `disable_local_backup` setting is set to `true` local backups will no be taken for your entries and you will not be prompted to load local backups.
When the `disable_local_backup` setting is set to `true` local backups will no be taken for your entries.
## YAML Options
The YAML format parsing and stringifing can be customized via the `yaml` setting. Available options can be found in the [yaml docs](https://eemeli.org/yaml/#options),
| Setting | Docs |
| ----------------- | ------------------------------------------- |
| parseOptions | https://eemeli.org/yaml/#parse-options |
| documentOptions | https://eemeli.org/yaml/#document-options |
| schemaOptions | https://eemeli.org/yaml/#schema-options |
| createNodeOptions | https://eemeli.org/yaml/#createnode-options |
| toJsOptions | https://eemeli.org/yaml/#tojs-options |
| toStringOptions | https://eemeli.org/yaml/#tostring-options |
**Example**
<CodeTabs>
```yaml
yaml:
toStringOptions:
indentSeq: false
```
```js
yaml: {
toStringOptions: { indentSeq: false },
},
```
</CodeTabs>

View File

@ -4,7 +4,7 @@ title: Contributor Guide
weight: 20
---
We're hoping that Static CMS will do for the [Jamstack](https://www.jamstack.org) what WordPress did for dynamic sites back in the day. We know we can't do that without building a thriving community of contributors and users, and we'd love to have you join us.
We are hoping that Static CMS will do for the [Jamstack](https://www.jamstack.org) what WordPress did for dynamic sites back in the day. We know we cannot do that without building a thriving community of contributors and users, and we would love to have you join us.
## Getting started with contributing
Being a developer is not a requirement for contributing to Static CMS, you only need the desire, a web browser, and a [GitHub account](https://github.com/join). The GitHub repo has a step-by-step [guide](https://github.com/StaticJsCMS/static-cms/blob/main/CONTRIBUTING.md) to get started with the code.
@ -18,12 +18,12 @@ The GitHub website allows you to submit issues, work with files, search for cont
A [style guide](/docs/writing-style-guide/) is available to help provide context around grammar, code styling, syntax, etc.
## Filing issues
If you have a GitHub account, you can file an [issue](https://github.com/StaticJsCMS/static-cms/issues) (aka bug report) against the Static CMS docs. Even if you're not able to, or don't know how to, fix the issue (see [Improve existing content](#improve-existing-content)), it helps to start the conversation.
If you have a GitHub account, you can file an [issue](https://github.com/StaticJsCMS/static-cms/issues) (aka bug report) against the Static CMS docs. Even if you are not able to, or do not know how to, fix the issue (see [Improve existing content](#improve-existing-content)), it helps to start the conversation.
When filing an issue, it is important to remember the [Code of Conduct](https://github.com/StaticJsCMS/static-cms/blob/main/CODE_OF_CONDUCT.md).
## Improve existing content
If you are able to offer up a change to existing content, it is welcome. Once you've forked the repo, and changed the content, you would file a pull request (PR). The repo [Contributing file](https://github.com/StaticJsCMS/static-cms/blob/main/CONTRIBUTING.md) lays out the correct format for PRs.
If you are able to offer up a change to existing content, it is welcome. Once you have forked the repo, and changed the content, you would file a pull request (PR). The repo [Contributing file](https://github.com/StaticJsCMS/static-cms/blob/main/CONTRIBUTING.md) lays out the correct format for PRs.
## Other places to get involved
Here are some links with more information about getting involved:

View File

@ -4,7 +4,7 @@ title: Adding Custom Icons
weight: 100
---
The Static CMS exposes a `window.CMS` global object that you can use to register custom icons via `registerIcon`. The same object is also the default export if you import Static CMS as an npm module.
Static CMS exposes a `window.CMS` global object that you can use to register custom icons via `registerIcon`. The same object is also the default export if you import Static CMS as an npm module.
Custom icons can be used with [Collections](/docs/collection-overview) or [Custom Links & Pages](/docs/additional-links)

View File

@ -4,7 +4,7 @@ title: Creating Custom Previews
weight: 50
---
The Static CMS exposes a `window.CMS` global object that you can use to register custom previews for an entire collection (or file within a file collection) via `registerPreviewTemplate` (editor view) and `registerPreviewCard` / `registerFieldPreview` (collection view).
Static CMS exposes a `window.CMS` global object that you can use to register custom previews for an entire collection (or file within a file collection) via `registerPreviewTemplate` (editor view) and `registerPreviewCard` / `registerFieldPreview` (collection view).
### React Components Inline
@ -34,7 +34,6 @@ The following parameters will be passed to your `react_component` during render:
| window | Window | The window object the preview is within. If rendered with a frame, it will be the frame's window |
| widgetFor | Function | Given a field name, returns the rendered preview of that field's widget and value |
| widgetsFor | Function | Given a field name, returns the rendered previews of that field's nested child widgets and values |
| theme | 'light'<br />\| 'dark' | The current theme being used by the app |
#### Example
@ -352,7 +351,6 @@ The following parameters will be passed to your `react_component` during render:
| entry | object | Object with a `data` field that contains the current value of all widgets in the editor |
| widgetFor | Function | Given a field name, returns the rendered preview of that field's widget and value |
| widgetsFor | Function | Given a field name, returns the rendered previews of that field's nested child widgets and values |
| theme | 'light'<br />\| 'dark' | The current theme being used by the app |
| hasLocalBackup | boolean | Whether the current entry has a local backup |
#### Example
@ -478,7 +476,9 @@ import type { TemplatePreviewCardProps } from '@staticcms/core';
interface Post {
image: string;
title: string;
date: string;
body: string;
draft: boolean;
}
const PostPreviewCard = ({ entry, widgetFor }: TemplatePreviewCardProps<Post>) => {
@ -502,12 +502,12 @@ const PostPreviewCard = ({ entry, widgetFor }: TemplatePreviewCardProps<Post>) =
gap: '8px',
}}
>
<strong style={{ fontSize: '24px' }}>{entry.data.title}</strong>
<span style={{ fontSize: '16px' }}>{entry.data.date}</span>
<strong style={{ fontSize: '24px' }}>{entry.data?.title}</strong>
<span style={{ fontSize: '16px' }}>{entry.data?.date}</span>
</div>
<div
style={{
backgroundColor: entry.data.draft === true ? 'blue' : 'green',
backgroundColor: entry.data?.draft === true ? 'blue' : 'green',
color: 'white',
border: 'none',
padding: '4px 8px',
@ -518,7 +518,7 @@ const PostPreviewCard = ({ entry, widgetFor }: TemplatePreviewCardProps<Post>) =
borderRadius: '4px',
}}
>
{entry.data.draft === true ? 'Draft' : 'Published'}
{entry.data?.draft === true ? 'Draft' : 'Published'}
</div>
</div>
</div>
@ -554,7 +554,6 @@ The following parameters will be passed to your `component` during render:
| collection | object | Collection configuration |
| field | object | Field configuration |
| value | Function | The current value of the field for the given entry |
| theme | 'light'<br />\| 'dark' | The current theme being used by the app |
#### Example

View File

@ -0,0 +1,471 @@
---
group: Customization
title: Theming
weight: 30
---
Static CMS comes with two default themes (`light` and `dark`), and you can add your own custom themes as well.
Static CMS exposes a `window.CMS` global object that you can use to register custom themes via `registerTheme`. The same object is also the default export if you import Static CMS as an npm module.
## Register Theme
Register a custom theme.
<CodeTabs>
```js
const theme = {
name: 'Custom Dark',
text: {
primary: '#fff',
secondary: 'rgba(255, 255, 255, 0.7)',
disabled: 'rgba(255, 255, 255, 0.5)',
},
background: {
main: '#1e293b',
light: '#2c3b55',
dark: '#0f172a',
divider: '#2c3b55',
},
scrollbar: {
main: '#1e293b',
light: '#2c3b55',
},
button: {
disabled: '#334155',
},
primary: {
main: '#339ef4',
light: '#6bb9f7',
dark: '#0c82e0',
contrastColor: '#ffffff',
},
error: {
main: '#f44336',
light: '#e57373',
dark: '#d32f2f',
contrastColor: '#ffffff',
},
warning: {
main: '#ffa726',
light: '#ffb74d',
dark: '#f57c00',
contrastColor: '#ffffff',
},
info: {
main: '#29b6f6',
light: '#4fc3f7',
dark: '#0288d1',
contrastColor: '#ffffff',
},
success: {
main: '#66bb6a',
light: '#81c784',
dark: '#388e3c',
contrastColor: '#ffffff',
},
codemirror: {
theme: 'dark',
},
};
// Using global window object
CMS.registerTheme(theme);
// Using npm module import
import CMS from '@staticcms/core';
CMS.registerTheme(theme);
```
```yaml
theme:
themes:
- name: Custom Dark
text:
primary: '#fff'
secondary: 'rgba(255, 255, 255, 0.7)'
disabled: 'rgba(255, 255, 255, 0.5)'
background:
main: '#1e293b'
light: '#2c3b55'
dark: '#0f172a'
divider: '#2c3b55'
scrollbar:
main: '#1e293b'
light: '#2c3b55'
button:
disabled: '#334155'
primary:
main: '#339ef4'
light: '#6bb9f7'
dark: '#0c82e0'
contrastColor: '#ffffff'
error:
main: '#f44336'
light: '#e57373'
dark: '#d32f2f'
contrastColor: '#ffffff'
warning:
main: '#ffa726'
light: '#ffb74d'
dark: '#f57c00'
contrastColor: '#ffffff'
info:
main: '#29b6f6'
light: '#4fc3f7'
dark: '#0288d1'
contrastColor: '#ffffff'
success:
main: '#66bb6a'
light: '#81c784'
dark: '#388e3c'
contrastColor: '#ffffff'
codemirror:
theme: dark
```
</CodeTabs>
## Extend Built-in Themes
Extend either the `light` or `dark` themes.
<CodeTabs>
```js
// Using global window object
CMS.registerTheme({
name: 'Red Orange',
extends: 'dark',
primary: {
main: '#ff4500',
},
});
// Using npm module import
import CMS from '@staticcms/core';
CMS.registerTheme({
name: 'Red Orange',
extends: 'dark',
primary: {
main: '#ff4500',
},
});
```
```yaml
theme:
themes:
- name: Red Orange
extends: dark
primary:
main: '#ff4500'
```
</CodeTabs>
## Set Default Theme
By default `light` is the main theme (or `dark` if the user's system is set to dark mode). `default_theme` allows you to change that.
```yaml
theme:
default_theme: false
themes:
# Can also be registered via javascript
- name: Red Orange
extends: dark
primary:
main: '#ff4500'
```
## Hide Built-in themes
By default both a `light` and `dark` them are available. However, if you provide at least one custom theme, `include_built_in_themes` allows you to disable the built-in themes.
If `default_theme` is not provided, then the first custom theme is used (when `include_built_in_themes` is `false`).
```yaml
theme:
include_built_in_themes: false
themes:
# Can also be registered via javascript
- name: Red Orange
extends: dark
primary:
main: '#ff4500'
```
## useTheme Hook
The `useTheme` hook can be utilized in customize widgets and previews to utilize values from the theme.
### Example Preview Card
<CodeTabs>
```js
const PostPreviewCard = ({ entry, widgetFor }) => {
const theme = useTheme();
return h(
'div',
{ style: { width: '100%' } },
widgetFor('image'),
h(
'div',
{ style: { padding: '16px', width: '100%' } },
h(
'div',
{
style: {
display: 'flex',
width: '100%',
justifyContent: 'space-between',
alignItems: 'start',
},
},
h(
'div',
{
style: {
display: 'flex',
flexDirection: 'column',
alignItems: 'baseline',
gap: '8px',
},
},
h('strong', { style: { fontSize: '24px' } }, entry.data.title),
h('span', { style: { fontSize: '16px' } }, entry.data.date),
),
h(
'div',
{
style: {
backgroundColor: entry.data.draft === true
? theme.info.main
: theme.success.main,
color: 'white',
border: 'none',
padding: '4px 8px',
textAlign: 'center',
textDecoration: 'none',
display: 'inline-block',
cursor: 'pointer',
borderRadius: '4px',
},
},
entry.data.draft === true ? 'Draft' : 'Published',
),
),
),
);
};
CMS.registerPreviewCard('posts', PostPreviewCard, () => 240);
```
```jsx
import CMS from '@staticcms/core';
const PostPreviewCard = ({ entry, widgetFor }) => {
const theme = useTheme();
return (
<div style={{ width: '100%' }}>
{widgetFor('image')}
<div style={{ padding: '16px', width: '100%' }}>
<div
style={{
display: 'flex',
width: '100%',
justifyContent: 'space-between',
alignItems: 'start',
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'baseline',
gap: '8px',
}}
>
<strong style={{ fontSize: '24px' }}>{entry.data.title}</strong>
<span style={{ fontSize: '16px' }}>{entry.data.date}</span>
</div>
<div
style={{
backgroundColor: entry.data.draft === true
? theme.info.main
: theme.success.main,
color: 'white',
border: 'none',
padding: '4px 8px',
textAlign: 'center',
textDecoration: 'none',
display: 'inline-block',
cursor: 'pointer',
borderRadius: '4px',
}}
>
{entry.data.draft === true ? 'Draft' : 'Published'}
</div>
</div>
</div>
</div>
);
};
CMS.registerPreviewCard('posts', PostPreviewCard, () => 240);
```
```tsx
import CMS, { useTheme } from '@staticcms/core';
import type { TemplatePreviewCardProps } from '@staticcms/core';
/**
* The type for 'entry.data'
*/
interface Post {
image: string;
title: string;
date: string;
body: string;
draft: boolean;
}
const PostPreviewCard = ({ entry, widgetFor }: TemplatePreviewCardProps<Post>) => {
const theme = useTheme();
return (
<div style={{ width: '100%' }}>
{widgetFor('image')}
<div style={{ padding: '16px', width: '100%' }}>
<div
style={{
display: 'flex',
width: '100%',
justifyContent: 'space-between',
alignItems: 'start',
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'baseline',
gap: '8px',
}}
>
<strong style={{ fontSize: '24px' }}>{entry.data?.title}</strong>
<span style={{ fontSize: '16px' }}>{entry.data?.date}</span>
</div>
<div
style={{
backgroundColor: entry.data?.draft === true
? theme.info.main
: theme.success.main,
color: 'white',
border: 'none',
padding: '4px 8px',
textAlign: 'center',
textDecoration: 'none',
display: 'inline-block',
cursor: 'pointer',
borderRadius: '4px',
}}
>
{entry.data?.draft === true ? 'Draft' : 'Published'}
</div>
</div>
</div>
</div>
);
};
CMS.registerPreviewCard('posts', PostPreviewCard, () => 240);
```
</CodeTabs>
## Theme
The react component that renders the control. It receives the following props:
| Param | Type | Description |
| ---------- | ------ | ------------------------------------------------------------------------------------ |
| name | string | The name of the theme |
| extends | string | _Optional if all other theme options are provided._<br />The default theme to extend |
| common | object | _Optional if `extends` is provided._ See [Common Colors](#common-colors) |
| text | object | _Optional if `extends` is provided._ See [Text Colors](#text-colors) |
| background | object | _Optional if `extends` is provided._ See [Background Colors](#background-colors) |
| scrollbar | object | _Optional if `extends` is provided._ See [Scrollbar Colors](#scrollbar-colors) |
| primary | object | _Optional if `extends` is provided._ See [Theme Color](#theme-color) |
| error | object | _Optional if `extends` is provided._ See [Theme Color](#theme-color) |
| warning | object | _Optional if `extends` is provided._ See [Theme Color](#theme-color) |
| info | object | _Optional if `extends` is provided._ See [Theme Color](#theme-color) |
| success | object | _Optional if `extends` is provided._ See [Theme Color](#theme-color) |
| codemirror | object | _Optional if `extends` is provided._ See [Codemirror](#codemirror) |
### Common Colors
`common` allows you to change the common colors.
| Param | Type | Description |
| ----- | ------ | ------------------------------------ |
| gray | string | _Optional if `extends` is provided._ |
### Text Colors
`text` allows you to change the text colors.
| Param | Type | Description |
| --------- | ------ | ------------------------------------ |
| primary | string | _Optional if `extends` is provided._ |
| secondary | string | _Optional if `extends` is provided._ |
| disabled | string | _Optional if `extends` is provided._ |
### Background Colors
`background` allows you to change the background colors.
| Param | Type | Description |
| ------- | ------ | ----------------------------------------------------------------------------------------- |
| main | string | _Optional if `extends` is provided._ |
| light | string | _Optional if `extends` is provided._<br />Will be calculated from `main` if not provided. |
| dark | string | _Optional if `extends` is provided._<br />Will be calculated from `main` if not provided. |
| divider | string | _Optional if `extends` is provided._ |
### Scrollbar Colors
`scrollbar` allows you to change the scrollbar colors.
| Param | Type | Description |
| ----- | ------ | ----------------------------------------------------------------------------------------- |
| main | string | _Optional if `extends` is provided._ |
| light | string | _Optional if `extends` is provided._<br />Will be calculated from `main` if not provided. |
### Theme Color
`primary`, `error`, `warning`, `info` and `success` are theme colors and share the same options.
| Param | Type | Description |
| ------------- | ------ | ----------------------------------------------------------------------------------------- |
| main | string | _Optional if `extends` is provided._ |
| light | string | _Optional if `extends` is provided._<br />Will be calculated from `main` if not provided. |
| dark | string | _Optional if `extends` is provided._<br />Will be calculated from `main` if not provided. |
| contrastColor | string | _Optional if `extends` is provided._ |
### Codemirror
`codemirror` allows you to change the theme settings for Codemirror instances (used by the [code](/docs/widget-code) and [markdown](/docs/widget-markdown) widgets).
| Param | Type | Description |
| ----- | ------ | ----------------------------------------------------------------------------------------- |
| main | string | _Optional if `extends` is provided._ |
| light | string | _Optional if `extends` is provided._<br />Will be calculated from `main` if not provided. |

View File

@ -4,7 +4,7 @@ title: Creating Custom Widgets
weight: 40
---
The Static CMS exposes a `window.CMS` global object that you can use to register custom widgets via `registerWidget`. The same object is also the default export if you import Static CMS as an npm module.
Static CMS exposes a `window.CMS` global object that you can use to register custom widgets via `registerWidget`. The same object is also the default export if you import Static CMS as an npm module.
### React Components Inline
@ -64,7 +64,6 @@ The react component that renders the control. It receives the following props:
| query | function | Runs a search on another collection. See [Query](#query) |
| i18n | object | The current i18n settings |
| t | function | Translates a given key to the current locale |
| theme | 'light'<br />\| 'dark' | The current theme being used by the app |
#### Query
@ -90,7 +89,6 @@ The react component that renders the preview. It receives the following props:
| collection | object | The collection configuration for the current widget. See [Collections](/docs/collection-overview) |
| config | object | The current Static CMS config. See [configuration options](/docs/configuration-options) |
| entry | object | Object with a `data` field that contains the current value of all widgets in the editor |
| theme | 'light'<br />\| 'dark' | The current theme being used by the app |
### Options

View File

@ -4,7 +4,7 @@ title: Overview
weight: 1
---
The Static CMS exposes a `window.CMS` global object that you can use to customize your CMS. The same object is also the default export if you import Static CMS as an npm module. Available options are:
Static CMS exposes a `window.CMS` global object that you can use to customize your CMS. The same object is also the default export if you import Static CMS as an npm module. Available options are:
- Register [custom widgets](/docs/custom-widgets)
- Register [custom previews](/docs/custom-previews)

View File

@ -1,35 +1,45 @@
---
group: Migration
title: Decap CMS Migration Guide
title: Decap / Netlify Migration Guide
weight: 190
---
Static CMS is a fork of [Decap](https://github.com/decaporg/decap-cms) (previously Netlify CMS) . Many changes have been made, some big, some small.
Static CMS is a fork of [Decap](https://github.com/decaporg/decap-cms) (previously Netlify CMS). Many changes have been made, some big, some small.
In this guide, we will walk you through the steps of migrating from Decap to Static CMS.
In this guide, we will walk you through the steps of migrating from Decap or Netlify to Static CMS.
## How to Migrate
Start by replacing Decap with Static CMS, then address the changes below.
Start by replacing Decap / Netlify with Static CMS, then address the changes below.
### CDN
Decap:
Decap (_remove_):
```html
<script src="https://unpkg.com/netlify-cms@^3.0.0/dist/decap-cms.js"></script>
```
Netlify (_remove_):
```html
<script src="https://unpkg.com/netlify-cms@^3.0.0/dist/netlify-cms.js"></script>
```
Static CMS:
Static CMS (_add_):
```html
<script src="https://unpkg.com/@staticcms/app@^3.0.0/dist/static-cms-app.js"></script>
<script src="https://unpkg.com/@staticcms/app@^4.0.0/dist/static-cms-app.js"></script>
```
### Bundling
```bash
# Uninstall Decap
npm uninstall decap-cms-app
npm uninstall decap-cms-core
# Uninstall Netlify
npm uninstall netlify-cms-app
npm uninstall netlify-cms-core
@ -39,13 +49,19 @@ npm install @staticcms/core
#### Change your imports
Decap:
Decap (_remove_):
```js
import CMS from 'decap-cms-app';
```
Netlify (_remove_):
```js
import CMS from 'netlify-cms-app';
```
Static CMS:
Static CMS (_add_):
```js
import CMS from '@staticcms/core';
@ -81,7 +97,7 @@ However, the Gitlab, Client-Side Implicit Grant has been removed as a method of
### Dates
[Moment](https://momentjs.com/) has been dropped as the date library used. Instead we are now using [date-fns](https://date-fns.org/). Date formats in your configuration will need to be updated. See [format docs](https://date-fns.org/v2.29.3/docs/format).
[Moment](https://momentjs.com/) has been dropped as the date library used. Instead we are now using [date-fns](https://date-fns.org/). Date formats in your configuration will need to be updated. See [format docs](https://date-fns.org/docs/format).
### Initializing Static CMS
@ -93,20 +109,152 @@ A [new markdown editor](/docs/widget-markdown) has been added. It comes with a n
### Sortable Fields
The `sortable_fields` configuration option has been slightly changed, as we now allow a [default sorting option](/docs/collection-overview#sortable_fields).
The `sortable_fields` configuration option has been slightly changed, as we now allow a [default sorting option](/docs/collection-overview#default-sort).
**Decap**:
**Decap / Netlify**:
```yaml
sortable_fields: - field1 - field2
sortable_fields:
- field1
- field2
```
**Static CMS**:
<CodeTabs>
```yaml
sortable_fields: fields: - field1 - field2
sortable_fields:
fields:
- field1
- field2
```
```js
sortable_fields: {
fields: ['field1', 'field2'];
}
```
</CodeTabs>
### View Filters
The `view_filters` configuration option has been slightly changed, as we now allow a [default filter option](/docs/collection-overview#view-filters). Also each filter now requires a unique name.
**Decap / Netlify**:
```yaml
view_filters:
- label: "Alice's and Bob's Posts"
field: author
pattern: 'Alice|Bob'
- label: 'Posts published in 2020'
field: date
pattern: '2020'
- label: Drafts
field: draft
pattern: true
```
**Static CMS**:
<CodeTabs>
```yaml
view_filters:
fields:
- name: alice-and-bob
label: "Alice's and Bob's Posts"
field: author
pattern: 'Alice|Bob'
- name: posts-2020
label: 'Posts published in 2020'
field: date
pattern: '2020'
- name: drafts
label: Drafts
field: draft
pattern: true
```
```js
view_filters: {
fields: [
{
name: 'alice-and-bob',
label: "Alice's and Bob's Posts",
field: 'author',
pattern: 'Alice|Bob',
},
{
name: 'posts-2020',
label: 'Posts published in 2020',
field: 'date',
pattern: '2020',
},
{
name: 'drafts',
label: 'Drafts',
field: 'draft',
pattern: true,
},
];
}
```
</CodeTabs>
### View Groups
The `view_groups` configuration option has been slightly changed, as we now allow a [default grouping option](/docs/collection-overview#view-groups). Also each group now requires a unique name.
**Decap / Netlify**:
```yaml
view_groups:
- label: Year
field: date
# groups items based on the value matched by the pattern
pattern: \d{4}
- label: Drafts
field: draft
```
**Static CMS**:
<CodeTabs>
```yaml
view_groups:
groups:
- name: by-year
label: Year
field: date
# groups items based on the value matched by the pattern
pattern: \d{4}
- name: drafts
label: Drafts
field: draft
```
```js
view_groups: {
groups: [
{
name: "by-year",
label: "Year",
field: "date",
pattern: "\\d{4}
},
{
name: "drafts",
label: "Drafts",
field: "draft"
}
]
}
```
</CodeTabs>
### List Widget
Support in the List Widget for the `field` property has been dropped. A single field in the `fields` property [achieves the same behavior](/docs/widget-list).
@ -146,7 +294,7 @@ The `getAsset` method has been removed, the new `useMediaAsset` hook should be u
### Beta Features
The following beta features from Decap have been dropped:
The following beta features from Decap / Netlify have been dropped:
- GraphQL support for GitHub and GitLab
- Remark plugins (new markdown editor has its own plugin system)
@ -226,7 +374,7 @@ collections:
If you are using Gatsby you will need to change out your CMS plugin.
```bash
# Uninstall Decap plugin
# Uninstall Decap / Netlify plugin
npm uninstall gatsby-plugin-netlify-cms
# Install Static CMS plugin
@ -237,13 +385,19 @@ npm install gatsby-plugin-static-cms
If you are using the local backend you will need to switch the proxy server package you are using.
Decap:
Decap (_remove_):
```bash
npx decap-server
```
Netlify (_remove_):
```bash
npx netlify-cms-proxy-server
```
Static CMS:
Static CMS (_add_):
```bash
npx @staticcms/proxy-server

View File

@ -0,0 +1,35 @@
---
group: Workflow
weight: 10
title: Editorial Workflow
beta: true
---
<Alert severity="warning">
Editorial Workflow is not available for the Gitea backend.
</Alert>
By default, all entries created or edited in Static CMS are committed directly into the main repository branch.
The `publish_mode` option allows you to enable "Editorial Workflow" mode for more control over the content publishing phases. The unpublished entries will be arranged on a dashboard, in Static CMS, according to their status (Draft, Ready for Review, Ready To Publish). This allows for quick access to unpublished entries, allowing them to be reviewed and edited before going live.
You can enable the Editorial Workflow with the following line in your `config.yml` file:
<CodeTabs>
```yaml
publish_mode: editorial_workflow
```
```js
publish_mode: 'editorial_workflow';
```
</CodeTabs>
From a technical perspective, the workflow translates editor UI actions into common Git commands:
| Actions in Netlify UI | Perform these Git actions |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Save draft | Commits to a new branch (named according to the pattern `cms/collectionName/entrySlug`), and opens a pull/merge request |
| Edit draft | Pushes another commit to the draft branch and pull/merge request |
| Approve and publish draft | Merges pull/merge request and deletes branch |

View File

@ -2,9 +2,12 @@
title: Gitea
group: Backends
weight: 45
beta: true
---
<Alert severity="warning">
Gitea backend cannot be used with the Editorial Workflow.
</Alert>
- **Name**: `gitea`
For repositories stored on Gitea, the `gitea` backend allows CMS users to log in directly with their Gitea account. Note that all users must have push access to your content repository for this to work.

View File

@ -1,7 +1,6 @@
---
group: Collections
title: i18n Support
beta: true
weight: 30
---
@ -25,7 +24,7 @@ i18n:
# Optional, defaults to the first item in locales.
# The locale to be used for fields validation and as a baseline for the entry.
defaultLocale: en
default_locale: en
```
```js
@ -45,7 +44,7 @@ i18n: {
* Optional, defaults to the first item in locales.
* The locale to be used for fields validation and as a baseline for the entry.
*/
defaultLocale: 'en'
default_locale: 'en'
},
```

View File

@ -37,7 +37,7 @@ backend: {
## Usage
1. Run `npx @staticcms/proxy-server` from the root directory of the above repository.
- If the default port (8081) is in use, the proxy server won't start and you will see an error message. In this case, follow [these steps](#configure-the-@staticcms/proxy-server-port-number) before proceeding.
- If the default port (8081) is in use, the proxy server will not start and you will see an error message. In this case, follow [these steps](#configure-the-@staticcms/proxy-server-port-number) before proceeding.
2. Start your local development server (e.g. run `gatsby develop`).
3. Open `http://localhost:<port>/admin` to verify that your can administer your content locally. Replace `<port>` with the port of your local development server. For example Gatsby's default port is `8000`

View File

@ -1,89 +0,0 @@
---
group: Migration
title: How to Upgrade to v3
weight: 101
---
Static CMS v3 introduces:
- Mobile support
- Depedent fields (see [Field Conditions](/docs/widgets#field-conditions) for more information)
In this guide, we will walk you through the steps for upgrading to Static CMS v3.
Please [report any issues](https://github.com/StaticJsCMS/static-cms/issues/new) you encounter while upgrading to Static CMS v3.
## Installing
To install the latest version of Static CMS:
```bash
npm install @staticcms/core@^3.0.0
```
Or if you're using yarn:
```bash
yarn add @staticcms/core@^3.0.0
```
If you are using a CDN to load Static CMS, simply change your URLs:
```html
<link rel="stylesheet" href="https://unpkg.com/@staticcms/app@^3.0.0/dist/main.css" />
```
```html
<script src="https://unpkg.com/@staticcms/app@^3.0.0/dist/static-cms-app.js"></script>
```
## Gitea Backend Update <BetaImage />
While still remaining in beta, the Gitea backend has been evolving. This update switches the authentication mechanism to PKCE auth and improves performance when dealing with multiple file commits.
To use Gitea with Static CMS v3, you need to update your Gitea instance to at least `v1.20`. You will also need to update your config to match the setup for PKCE authentication. See [Gitea authentication](/docs/gitea-backend#authentication).
## CMS Events <BetaImage />
CMS Events have undergone a significant refactor in this update, including adding a new `change` event. You may need to update your config as follows to continue to use them. The `preSave` and `postSave` events along with the new `change` event, now require a `collection` be provided during registration, with an optional `file` if you are targeting a [file collection](/docs/collection-types#file-collections). All events now can handle async handlers as well.
**Old setup**
```js
CMS.registerEventListener({
name: 'preSave',
handler: ({ entry }) => {
return {
...entry,
data: {
...entry.data,
title: 'new title',
},
};
},
});
```
**New Setup**
```js
CMS.registerEventListener({
name: 'preSave',
collection: 'posts',
handler: ({ data: { entry } }) => {
return {
...entry.data,
title: 'new title',
};
},
});
```
See [CMS Events](/docs/beta-features#registering-to-cms-events) for more details.
## Other Breaking Changes
- The following Widget Control component properties have been removed:
- `hidden`
- `mediaPaths` - Use [useMediaInsert](/docs/custom-widgets#interacting-with-the-media-library) instead.
- `openMediaLibrary` - Use [useMediaInsert](/docs/custom-widgets#interacting-with-the-media-library) instead.
- `removeInsertedMedia` - Use [useMediaInsert](/docs/custom-widgets#interacting-with-the-media-library) instead.

View File

@ -0,0 +1,263 @@
---
group: Migration
title: How to Upgrade to v4
weight: 101
---
Static CMS v4 introduces:
- [Custom themes](/docs/custom-theme)
- [Editorial Workflow](/docs/editorial-workflow) <BetaImage />
- [Open Authoring](/docs/open-authoring) (Github backend only) <BetaImage />
In this guide, we will walk you through the steps for upgrading to Static CMS v4.
Please [report any issues](https://github.com/StaticJsCMS/static-cms/issues/new) you encounter while upgrading to Static CMS v4.
## Installing
To install the latest version of Static CMS:
```bash
npm install @staticcms/core@^4.0.0
```
Or if you are using yarn:
```bash
yarn add @staticcms/core@^4.0.0
```
If you are using a CDN to load Static CMS, simply change your URLs:
```html
<link rel="stylesheet" href="https://unpkg.com/@staticcms/app@^4.0.0/dist/main.css" />
```
```html
<script src="https://unpkg.com/@staticcms/app@^4.0.0/dist/static-cms-app.js"></script>
```
## View Filters
The `view_filters` configuration option has been slightly changed, as we now allow a [default filter option](/docs/collection-overview#view-filters). Also each filter now requires a unique name.
**Old setup**
<CodeTabs>
```yaml
view_filters:
- label: "Alice's and Bob's Posts"
field: author
pattern: 'Alice|Bob'
- label: 'Posts published in 2020'
field: date
pattern: '2020'
- label: Drafts
field: draft
pattern: true
```
```js
view_filters: [
{
label: "Alice's and Bob's Posts",
field: 'author',
pattern: 'Alice|Bob',
},
{
label: 'Posts published in 2020',
field: 'date',
pattern: '2020',
},
{
label: 'Drafts',
field: 'draft',
pattern: true,
},
];
```
</CodeTabs>
**New setup**
<CodeTabs>
```yaml
view_filters:
fields:
- name: alice-and-bob
label: "Alice's and Bob's Posts"
field: author
pattern: 'Alice|Bob'
- name: posts-2020
label: 'Posts published in 2020'
field: date
pattern: '2020'
- name: drafts
label: Drafts
field: draft
pattern: true
```
```js
view_filters: {
fields: [
{
name: 'alice-and-bob',
label: "Alice's and Bob's Posts",
field: 'author',
pattern: 'Alice|Bob',
},
{
name: 'posts-2020',
label: 'Posts published in 2020',
field: 'date',
pattern: '2020',
},
{
name: 'drafts',
label: 'Drafts',
field: 'draft',
pattern: true,
},
];
}
```
</CodeTabs>
## View Groups
The `view_groups` configuration option has been slightly changed, as we now allow a [default grouping option](/docs/collection-overview#view-groups). Also each group now requires a unique name.
**Old setup**
<CodeTabs>
```yaml
view_groups:
- label: Year
field: date
# groups items based on the value matched by the pattern
pattern: \d{4}
- label: Drafts
field: draft
```
```js
view_groups: [
{
label: "Year",
field: "date",
pattern: "\\d{4}
},
{
label: "Drafts",
field: "draft"
}
]
```
</CodeTabs>
**New setup**
<CodeTabs>
```yaml
view_groups:
groups:
- name: by-year
label: Year
field: date
# groups items based on the value matched by the pattern
pattern: \d{4}
- name: drafts
label: Drafts
field: draft
```
```js
view_groups: {
groups: [
{
name: "by-year",
label: "Year",
field: "date",
pattern: "\\d{4}
},
{
name: "drafts",
label: "Drafts",
field: "draft"
}
]
}
```
</CodeTabs>
## Theme
The `theme` prop has been removed from:
- Custom widget [control components](/docs/custom-widgets#control-component) and [preview components](/docs/custom-widgets#preview-component)
- [Custom previews](/docs/custom-previews#editor-preview)
- [Custom collection card previews](/docs/custom-previews#collection-card-preview)
- [Custom collection field previews](/docs/custom-previews#collection-field-preview)
- [Shortcode control components](/docs/widget-markdown#shortcodes)
The new [useTheme hook](/docs/custom-theme#usetheme-hook) should be instead to get the colors of the current theme.
## Date Template Transformation
The date template transformation now uses [date-fns tokens](https://date-fns.org/docs/format) instead of momentjs.
## List / Object Filter Rules
Previously, when using [Filtered Folder Collections](/docs/collection-types#filtered-folder-collections), specifying a `list` field, Static CMS would search the values of the list to find a match. Now the default behavior is to match the JSON formatted version of the list's value. To match values inside the list, simply add `.*` to the end of your filter field.
Object fields are also now matched against the JSON formatted version of their values.
**Old setup**
<CodeTabs>
```yaml
filter:
field: list_field
value: some_value
```
```js
filter: {
field: 'list_field',
value: 'some_value'
}
```
</CodeTabs>
**New setup**
<CodeTabs>
```yaml
filter:
field: list_field.*
value: some_value
```
```js
filter: {
field: 'list_field.*',
value: 'some_value'
}
```
</CodeTabs>
## i18n Config
For i18n, the setting `defaultLocale` has been renamed to `default_locale`.
## Type Changes (TypeScript)
The `StringOrTextField` type has been split into `StringField` and `TextField`.

View File

@ -0,0 +1,107 @@
---
group: Workflow
weight: 20
title: Open Authoring
beta: true
---
<Alert severity="warning">
Open Authoring is only available with the [GitHub backend](/docs/github-backend) and must be used with the [Editorial Workflow](/docs/editorial-workflow).
</Alert>
When using the [GitHub backend](/docs/github-backend), you can use Static CMS to accept contributions from GitHub users without giving them access to your repository. When they make changes in the CMS, the CMS forks your repository for them behind the scenes, and all the changes are made to the fork. When the contributor is ready to submit their changes, they can set their draft as ready for review in the CMS. This triggers a pull request to your repository, which makes it appear in the Dashboard for maintainers.
At the same time, any contributors who _do_ have write access to the repository can continue to use Static CMS normally.
## Requirements
- You must use the [GitHub backend](/docs/github-backend).
**Note that the [Git Gateway backend](/docs/git-gateway-backend) does _not_ support Open Authoring, even when the underlying repo is on GitHub.**
- For private GitHub repos the user must have `read` access on the repo, and you must explicitly set the auth_scope to `repo`, for example:
<CodeTabs>
```yaml
backend:
name: github
repo: owner-name/private-repo-name # path to private repo
auth_scope: repo # this is needed to fork the private repo
open_authoring: true
```
```js
backend: {
name: "github",
repo: "owner-name/private-repo-name", // path to private repo
auth_scope: "repo", // this is needed to fork the private repo
open_authoring: true
}
```
</CodeTabs>
## Enabling Open Authoring
1. [Enable the editorial workflow](/docs/editorial-workflow) by setting `publish_mode` to `editorial_workflow` in your `config.yml`.
2. Set `open_authoring` to `true` in the `backend` section of your `config.yml`, as follows:
<CodeTabs>
```yaml
backend:
name: github
repo: owner-name/repo-name # Path to your GitHub repository
open_authoring: true
```
```js
backend: {
name: "github",
repo: "owner-name/repo-name", // Path to your GitHub repository
open_authoring: true
}
```
</CodeTabs>
## Usage
When a user logs into Static CMS who does not have write access to your repo, the CMS asks for permission to create a fork of your repo (or uses their existing fork, if they already have one). They are then presented with the normal CMS interface. The published content shown is from the original repo, so it stays up-to-date as changes are made.
On the editorial workflow screen, the normal three columns are replaced by two columns instead — `Draft` and `Ready to Review`.
When they make changes to content in the CMS, the changes are made to a branch on their fork. In the editorial workflow screen, they see only their own pending changes. Once they are ready to submit their changes, they can move the card into the "Ready To Review" column to create a pull request. When the entry is published (by a repository maintainer via their Static CMS UI), Static CMS deletes the branch, closes the PR and removes the card from the user's editorial workflow screen. Open Authoring users cannot publish entries through the CMS.
Users who _do_ have write access to the original repository continue to use the CMS normally.
## Alternative for external contributors with Git Gateway
[As noted above](#requirements), Open Authoring does not work with the Git Gateway backend. However, you can use Git Gateway on a site with Netlify Identity that has [open registration](https://www.netlify.com/docs/identity/#adding-identity-users). This lets users create accounts on your site and log into the CMS. There are a few differences, including the following:
- Users do not need to know about GitHub or create a GitHub account. Instead, they use Netlify Identity accounts that are created on your site and managed by you.
- The CMS applies users' changes directly to your repo, not to a fork. (If you use the editorial workflow, you can use features like [GitHub's protected branches](https://help.github.com/en/articles/about-protected-branches) or [Netlify's locked deploys](https://www.netlify.com/docs/locked-deploys/) to prevent users from publishing directly to your site from the CMS.)
- There is no distinction between users with write access to the repo and users without — all editorial workflow entries are visible from within the CMS and can be published with the CMS.
## Linking to specific entries in the CMS
Open authoring often includes some sort of "Edit this page" link on the live site. Static CMS supports this via the **edit** path:
```js
/#/edit/{collectionName}/{entryName}
```
For the entry named "general" in the "settings" file collection
```html
https://www.example.com/path-to-cms/#/edit/settings/general
```
For blog post "test.md" in the "posts" folder collection
```html
https://www.example.com/path-to-cms/#/edit/posts/test
```
- **`collectionName`**: the name of the collection as entered in the CMS config.
- **`entryName`** _(for [file collections](/docs/collection-types/#file-collections)_: the `name` of the entry from the CMS config.
- **`entryName`** _(for [folder collections](/docs/collection-types/#folder-collections)_: the filename, sans extension (the slug).

View File

@ -41,15 +41,15 @@ You can add Static CMS [to an existing site](/docs/add-to-your-site/), but the q
After clicking one of those buttons, authenticate with GitHub and choose a repository name. Netlify then automatically creates a clone of the repository in your GitHub account. Next, it builds and deploys the new site on Netlify, bringing you to the site dashboard after completing the build.
**Note for GitLab and Bitbucket users:** Static CMS supports GitLab and Bitbucket repositories, but won't work with the Deploy to Netlify buttons above without additional configuration (See [GitLab](/docs/gitlab-backend) or [Bitbucket](/docs/bitbucket-backend) respectively).
**Note for GitLab and Bitbucket users:** Static CMS supports GitLab and Bitbucket repositories, but will not work with the Deploy to Netlify buttons above without additional configuration (See [GitLab](/docs/gitlab-backend) or [Bitbucket](/docs/bitbucket-backend) respectively).
## Access Static CMS
1. The template deploy process sends you an invitation to your new site, sent from `no-reply@netlify.com`.
![Sample email subject line: You've been invited to join radiologist-amanda-53841.netlify.com](/img/email-subject.webp)
![Sample email subject line: You have been invited to join radiologist-amanda-53841.netlify.com](/img/email-subject.webp)
2. Wait for the deployment to complete, then click the link to accept the invite. Your site will open with a prompt to create a password.
!["Complete your signup" modal on the Kaldi coffee site](/img/create-password.webp)
3. Enter a password, sign in, and you'll go to Static CMS. (For future visits, you can go straight to `<yoursiteaddress.com>/admin/`.)
3. Enter a password, sign in, and you will go to Static CMS. (For future visits, you can go straight to `<yoursiteaddress.com>/admin/`.)
Try adding and editing posts, or changing the content of the Products page. When you save, the changes are pushed immediately to your Git repository, triggering a build on Netlify, and updating the content on your site. Check out the configuration code by visiting your site repo.

View File

@ -8,7 +8,7 @@ weight: 60
You can use the `test-repo` backend to try out Static CMS without connecting to a Git repo. With this backend, you can write and publish content normally, but any changes will disappear when you reload the page. This backend powers the Static CMS [demo site](https://demo.staticcms.org/).
**Note:** The `test-repo` backend can't access your local file system, nor does it connect to a Git repo, thus you won't see any existing files while using it.
**Note:** The `test-repo` backend cannot access your local file system, nor does it connect to a Git repo, thus you will not see any existing files while using it.
To enable this backend, set your backend name to `test-repo` in your Static CMS `config` file.

View File

@ -14,12 +14,12 @@ If you are using a package manager like Yarn or NPM, use their standard procedur
If you are using Static CMS through a CDN like Unpkg, then that depends on the version tag you are using. You can find the version tag in the `/admin/index.html` file of your site.
- (Recommended) If you use `^3.0.0`, Static CMS does all updates except major versions automatically.
- It upgrades to `3.0.1`, `3.1.0`, `3.1.1`.
- It does not upgrade to `3.0.0` or higher.
- (Recommended) If you use `^4.0.0`, Static CMS does all updates except major versions automatically.
- It upgrades to `4.0.1`, `4.1.0`, `4.1.1`.
- It does not upgrade to `4.0.0` or higher.
- It does not upgrade to beta versions.
- If you use `~3.0.0`, Static CMS will do only patch updates automatically.
- It upgrades `3.0.1`, `3.0.2`.
- It does not upgrade to `3.1.0` or higher.
- If you use `~4.0.0`, Static CMS will do only patch updates automatically.
- It upgrades `4.0.1`, `4.0.2`.
- It does not upgrade to `4.1.0` or higher.
- It does not upgrade beta versions.

View File

@ -14,9 +14,11 @@ The boolean widget translates a toggle switch input to a `true` or `false` value
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ------- | ------- | ------- | ------------------------------------------- |
| default | boolean | `false` | _Optional_. The default value for the field |
| Name | Type | Default | Description |
| ------- | ------- | ------- | --------------------------------------------- |
| default | boolean | `false` | _Optional_. The default value for the field |
| prefix | string | `''` | _Optional_. Text to show before toggle switch |
| suffix | string | `''` | _Optional_. Text to show after toggle switch |
## Example

View File

@ -17,9 +17,9 @@ For common options, see [Common widget options](/docs/widgets#common-widget-opti
| Name | Type | Default | Description |
| ----------- | ---------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| default | string | `Current Date and Time` | _Optional_. The default value for the field. Accepts a datetime string, or an empty string to accept blank input. |
| format | string | `yyyy-MM-dd'T'HH:mm:ss.SSSXXX` | _Optional_. Sets storage format. Accepts [date-fns tokens](https://date-fns.org/v2.29.3/docs/format) |
| date_format | string<br />\| boolean | `true` | _Optional_. Sets date display format in UI.<ul><li>`string` - Accepts [date-fns tokens](https://date-fns.org/v2.29.3/docs/format)</li><li>`true` - Uses default locale format</li><li>`false` - If `time_format` is `true` or a string, then date picker is hidden</li></ul> |
| time_format | string<br />\| boolean | `true` | _Optional_. Sets time display format in UI.<ul><li>`string` - Accepts [date-fns tokens](https://date-fns.org/v2.29.3/docs/format)</li><li>`true` - Uses default locale format</li><li>`false` - Hides the time picker</li></ul> |
| format | string | `yyyy-MM-dd'T'HH:mm:ss.SSSXXX` | _Optional_. Sets storage format. Accepts [date-fns tokens](https://date-fns.org/docs/format) |
| date_format | string<br />\| boolean | `true` | _Optional_. Sets date display format in UI.<ul><li>`string` - Accepts [date-fns tokens](https://date-fns.org/docs/format)</li><li>`true` - Uses default locale format</li><li>`false` - If `time_format` is `true` or a string, then date picker is hidden</li></ul> |
| time_format | string<br />\| boolean | `true` | _Optional_. Sets time display format in UI.<ul><li>`string` - Accepts [date-fns tokens](https://date-fns.org/docs/format)</li><li>`true` - Uses default locale format</li><li>`false` - Hides the time picker</li></ul> |
| picker_utc | boolean | `false` | _Optional_. <ul><li>`true` - The datetime picker will display times in UTC</li><li>`false` - The datetime picker will display times in the user's local timezone</li></ul> When using date-only formats, it can be helpful to set this to `true` so users in all timezones will see the same date in the datetime picker |
## Examples

View File

@ -19,7 +19,7 @@ For common options, see [Common widget options](/docs/widgets#common-widget-opti
| default | string | `[ <default from the child fields> ]` | _Optional_. The default values for fields. Also accepts an array of items |
| allow_add | boolean | `true` | _Optional_. `false` - Hides the button to add additional items. Ignored if both `fields` and `types` are not defined |
| collapsed | boolean | `true` | _Optional_. `true` - The list and entries collapse by default. Ignored if both `fields` and `types` are not defined |
| summary | string | | _Optional_. The label displayed on collapsed entries. _Ignored for single field lists._ |
| summary | string | | _Optional_. The label displayed on collapsed entries. Can use [Template Transformations](/docs/collection-overview#template-transformations). _Ignored for single field lists._ |
| label_singular | string | `label` | _Optional_. The text to show on the add button |
| fields | list of widgets | [] | _Optional_. A nested list of multiple widget fields to be included in each repeatable iteration |
| min | number | | _Optional_. Minimum number of items in the list |

View File

@ -10,7 +10,7 @@ weight: 19
The markdown widget provides a full fledged text editor allowing users to format text with features such as headings and blockquotes. Users can change their editing view with a handy toggle button.
_Please note:_ If you want to use your markdown editor to fill a markdown file contents after its frontmatter, you'll have to name the field `body` so Static CMS recognizes it and saves the file accordingly.
_Please note:_ If you want to use your markdown editor to fill a markdown file contents after its frontmatter, you will have to name the field `body` so Static CMS recognizes it and saves the file accordingly.
## Widget Options

View File

@ -21,6 +21,8 @@ For common options, see [Common widget options](/docs/widgets#common-widget-opti
| min | number | | _Optional_. Minimum value accepted. If a pattern is provided (see [Common widget options](/docs/widgets#common-widget-options)), min is ignored during validation, but is still applied to the input |
| max | number | | _Optional_. Maximum value accepted. If a pattern is provided (see [Common widget options](/docs/widgets#common-widget-options)), max is ignored during validation, but is still applied to the input |
| step | number | `1` | _Optional_. Size of steps when stepping up or down in input |
| prefix | string | `''` | _Optional_. Text to show before number input |
| suffix | string | `''` | _Optional_. Text to show after number input |
## Example

View File

@ -14,11 +14,11 @@ The object widget allows you to group multiple widgets together, nested under a
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| --------- | ------- | ------- | ------------------------------------------------------------ |
| fields | boolean | `false` | A nested list of widget fields to include in your widget |
| collapsed | boolean | `false` | _Optional_. Collapse the widget's content by default |
| summary | string | `value` | _Optional_. The label displayed when the object is collapsed |
| Name | Type | Default | Description |
| --------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| fields | boolean | `false` | A nested list of widget fields to include in your widget |
| collapsed | boolean | `false` | _Optional_. Collapse the widget's content by default |
| summary | string | `value` | _Optional_. The label displayed when the object is collapsed. Can use [Template Transformations](/docs/collection-overview#template-transformations) |
_Please note:_ A default value cannot be set directly on an object widget. Instead you can set defaults within each sub-field's configuration
@ -42,7 +42,7 @@ fields:
label: 'Birthdate'
widget: 'date'
default: ''
format: 'MM/DD/YYYY'
format: 'MM/dd/yyyy'
- name: 'address'
label: 'Address'
widget: 'object'
@ -81,7 +81,7 @@ fields: [
label: 'Birthdate',
widget: 'date',
default: '',
format: 'MM/DD/YYYY'
format: 'MM/dd/yyyy'
},
{
name: 'address',

View File

@ -8,7 +8,7 @@ weight: 22
- **UI:** Text input with search result dropdown
- **Data type:** Data type of the value pulled from the related collection item
The relation widget allows you to reference items from another collection. It provides a search input with a list of entries from the collection you're referencing, and the list automatically updates with matched entries based on what you've typed.
The relation widget allows you to reference items from another collection. It provides a search input with a list of entries from the collection you are referencing, and the list automatically updates with matched entries based on what you have typed.
## Widget Options

View File

@ -17,6 +17,8 @@ For common options, see [Common widget options](/docs/widgets#common-widget-opti
| Name | Type | Default | Description |
| ------- | ------ | ------- | ------------------------------------------------------------- |
| default | string | `''` | _Optional_. The default value for the field. Accepts a string |
| prefix | string | `''` | _Optional_. Text to show before string input |
| suffix | string | `''` | _Optional_. Text to show after string input |
## Example

View File

@ -45,7 +45,7 @@ The following options are available on all fields:
| required | boolean | `true` | _Optional_. Specify as `false` to make a field optional |
| hint | string | | _Optional_. Adds helper text directly below a widget. Useful for including instructions. Accepts markdown for bold, italic, strikethrough, and links. |
| pattern | list of strings | | _Optional_. Adds field validation by specifying a list with a [regex pattern](https://regexr.com/) and an error message; more extensive validation can be achieved with [custom widgets](/docs/custom-widgets/#advanced-field-validation) |
| i18n | boolean<br />\| 'translate'<br />\| 'duplicate'<br />\| 'none' | | _Optional_. <BetaImage /><ul><li>`translate` - Allows translation of the field</li><li>`duplicate` - Duplicates the value from the default locale</li><li>`true` - Accept parent values as default</li><li>`none` or `false` - Exclude field from translations</li></ul> |
| i18n | boolean<br />\| 'translate'<br />\| 'duplicate'<br />\| 'none' | | _Optional_. <ul><li>`translate` - Allows translation of the field</li><li>`duplicate` - Duplicates the value from the default locale</li><li>`true` - Accept parent values as default</li><li>`none` or `false` - Exclude field from translations</li></ul> |
| condition | FilterRule<br />\| List of FilterRules | | _Optional_. See [Field Conditions](#field-conditions) |
## Example Widget

View File

@ -168,13 +168,13 @@ _____
> Do: View the fields.
> Don't: With this next command, we'll view the fields.
> Don't: With this next command, we will view the fields.
### Address the reader as "you"
> Do: You can create a Deployment by …
> Don't: We'll create a Deployment by …
> Don't: We will create a Deployment by …
_____
> Do: In the preceding output, you can see…
@ -201,7 +201,7 @@ Exception: Use "etc." for et cetera.
### Avoid using "we"
Using "we" in a sentence can be confusing, because the reader might not know whether they're part of the "we" you're describing.
Using "we" in a sentence can be confusing, because the reader might not know whether they are part of the "we" you are describing.
> Do: Version 1.4 includes …

View File

@ -21,7 +21,7 @@
],
"call_to_action": {
"title": "Getting started is simple and free.",
"subtitle": "Choose a template that's pre-configured with a static site generator and deploys to a global CDN in one click.",
"subtitle": "Choose a template that is pre-configured with a static site generator and deploys to a global CDN in one click.",
"button_text": "Get Started",
"url": "/docs/start-with-a-template/"
},
@ -37,14 +37,14 @@
"description": "The web-based app includes rich-text editing, real-time previews, and drag-and-drop media uploads."
},
{
"image": "/img/your-content-your-way.webp",
"title": "Your content, your way",
"description": "Static CMS can integrate with most major static site generators and git repository providers."
"image": "/img/intuitive-workflow-for-content-teams.svg",
"title": "Intuitive workflow for content teams",
"description": "Writers and editors can easily manage content from draft to review to publish across any number of custom content types."
},
{
"image": "/img/instant-access-without-github-account.svg",
"title": "Instant access without GitHub account",
"description": "With Git Gateway, you can add CMS access for any team member — even if they don't have a GitHub account."
"description": "With Git Gateway, you can add CMS access for any team member — even if they do not have a GitHub account."
}
]
}

View File

@ -13,6 +13,10 @@
"name": "Backends",
"title": "Backends"
},
{
"name": "Workflow",
"title": "Workflow"
},
{
"name": "Collections",
"title": "Collections"

View File

@ -1,10 +1,41 @@
{
"releases": [
{
"date": "2024-01-03T10:00:00.000Z",
"version": "v4.0.0",
"type": "major",
"description": "Editorial workflow, open authoring and theming"
},
{
"date": "2024-01-02T11:00:00.000Z",
"version": "v4.0.0-beta.17",
"type": "pre"
},
{
"date": "2024-01-02T10:00:00.000Z",
"version": "v3.4.8",
"type": "patch"
},
{
"date": "2023-12-21T10:00:00.000Z",
"version": "v4.0.0-beta.16",
"type": "pre"
},
{
"date": "2023-12-13T10:00:00.000Z",
"version": "v4.0.0-beta.15",
"type": "pre"
},
{
"date": "2023-12-11T11:00:00.000Z",
"version": "v4.0.0-beta.13",
"type": "pre"
},
{
"date": "2023-12-11T10:00:00.000Z",
"version": "v4.0.0-beta.12",
"type": "pre"
},
{
"date": "2023-12-11T10:00:00.000Z",
"version": "v3.4.7",
@ -15,11 +46,71 @@
"version": "v3.4.6",
"type": "patch"
},
{
"date": "2023-11-17T10:00:00.000Z",
"version": "v4.0.0-beta.11",
"type": "pre"
},
{
"date": "2023-11-16T15:00:00.000Z",
"version": "v4.0.0-beta.10",
"type": "pre"
},
{
"date": "2023-11-16T14:00:00.000Z",
"version": "v4.0.0-beta.9",
"type": "pre"
},
{
"date": "2023-11-16T13:00:00.000Z",
"version": "v4.0.0-beta.8",
"type": "pre"
},
{
"date": "2023-11-16T12:00:00.000Z",
"version": "v4.0.0-beta.7",
"type": "pre"
},
{
"date": "2023-11-16T11:00:00.000Z",
"version": "v4.0.0-beta.6",
"type": "pre"
},
{
"date": "2023-11-16T10:00:00.000Z",
"version": "v4.0.0-beta.5",
"type": "pre"
},
{
"date": "2023-11-08T11:00:00.000Z",
"version": "v4.0.0-beta.4",
"type": "pre"
},
{
"date": "2023-11-08T10:00:00.000Z",
"version": "v4.0.0-beta.3",
"type": "pre"
},
{
"date": "2023-11-07T10:00:00.000Z",
"version": "v4.0.0-beta.2",
"type": "pre"
},
{
"date": "2023-11-07T11:00:00.000Z",
"version": "v3.4.5",
"type": "patch"
},
{
"date": "2023-11-02T10:00:00.000Z",
"version": "v4.0.0-beta.1",
"type": "pre"
},
{
"date": "2023-11-01T10:00:00.000Z",
"version": "v4.0.0-beta.0",
"type": "pre"
},
{
"date": "2023-10-31T11:00:00.000Z",
"version": "v3.4.4",

View File

@ -1,13 +1,14 @@
const withPWA = require('next-pwa')({
publicExcludes: ['!bulletins/**/*'],
dest: 'public'
dest: 'public',
});
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true'
enabled: process.env.ANALYZE === 'true',
});
const redirects = [
{ source: '/docs', destination: '/docs/intro', permanent: true },
{ source: '/chat', destination: 'https://discord.gg/ZWJM9pBMjj', permanent: true },
];
@ -28,7 +29,7 @@ let config = {
},
],
},
}
};
if (process.env.NODE_ENV === 'production') {
config = withPWA(config);
@ -36,4 +37,4 @@ if (process.env.NODE_ENV === 'production') {
config = withBundleAnalyzer(config);
}
module.exports = config
module.exports = config;

View File

@ -14,14 +14,14 @@
"lint": "run-p -c --aggregate-output \"lint:*\""
},
"dependencies": {
"@emotion/react": "11.10.6",
"@emotion/styled": "11.10.6",
"@mui/icons-material": "5.11.16",
"@mui/material": "5.11.16",
"date-fns": "2.29.3",
"@emotion/react": "11.11.1",
"@emotion/styled": "11.11.0",
"@mui/icons-material": "5.14.12",
"@mui/material": "5.14.12",
"date-fns": "2.30.0",
"gray-matter": "4.0.3",
"js-yaml": "4.1.0",
"next": "13.2.4",
"next": "14.0.1",
"next-mdx-remote": "4.4.1",
"next-pwa": "5.6.0",
"prismjs": "1.29.0",
@ -30,31 +30,30 @@
"react-schemaorg": "2.0.0",
"remark-gfm": "3.0.1",
"schema-dts": "1.1.2",
"yaml": "2.2.2"
"yaml": "2.3.2"
},
"devDependencies": {
"@babel/core": "7.21.4",
"@emotion/eslint-plugin": "11.10.0",
"@next/bundle-analyzer": "13.2.4",
"@next/eslint-plugin-next": "13.2.4",
"@types/js-yaml": "4.0.5",
"@types/node": "18.16.14",
"@types/prettier": "2.7.2",
"@types/prismjs": "1.26.0",
"@types/react": "18.2.0",
"@types/react-dom": "18.2.1",
"@typescript-eslint/eslint-plugin": "5.59.1",
"@typescript-eslint/parser": "5.59.1",
"@babel/eslint-parser": "7.21.3",
"eslint": "8.39.0",
"eslint-config-next": "13.2.4",
"eslint-config-prettier": "8.8.0",
"@babel/core": "7.23.0",
"@babel/eslint-parser": "7.22.15",
"@emotion/eslint-plugin": "11.11.0",
"@next/bundle-analyzer": "13.5.4",
"@next/eslint-plugin-next": "13.5.4",
"@types/js-yaml": "4.0.6",
"@types/node": "18.17.19",
"@types/prismjs": "1.26.1",
"@types/react": "18.2.25",
"@types/react-dom": "18.2.10",
"@typescript-eslint/eslint-plugin": "6.7.4",
"@typescript-eslint/parser": "6.7.4",
"eslint": "8.50.0",
"eslint-config-next": "14.0.1",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-babel": "5.3.1",
"eslint-plugin-unicorn": "46.0.1",
"eslint-plugin-unicorn": "48.0.1",
"npm-run-all": "4.1.5",
"prettier": "2.8.8",
"typescript": "5.0.4",
"webpack": "5.80.0"
"prettier": "3.0.3",
"typescript": "5.2.2",
"webpack": "5.88.2"
},
"lint-staged": {
"*.ts": "prettier --write",

View File

@ -1 +1 @@
<svg width="318" height="198" viewBox="0 0 318 198" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>editor</title><defs><rect id="b" width="300" height="180" rx="6"/><filter x="-6%" y="-7.8%" width="112%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.147843071 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feOffset dy="3" in="SourceAlpha" result="shadowOffsetOuter2"/><feGaussianBlur stdDeviation="4.5" in="shadowOffsetOuter2" result="shadowBlurOuter2"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0974298007 0" in="shadowBlurOuter2" result="shadowMatrixOuter2"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="shadowMatrixOuter2"/></feMerge></filter><linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="c"><stop stop-color="#4779DD" offset="0%"/><stop stop-color="#3A69C7" offset="100%"/></linearGradient></defs><g transform="translate(9 6)" fill="none" fill-rule="evenodd"><use fill="#000" filter="url(#a)" xlink:href="#b"/><use fill="#FFF" xlink:href="#b"/><path d="M0 19h149v161H6c-3.3137 0-6-2.6863-6-6V19z" fill="#EFF0F4"/><path fill="#AEB1BD" d="M10 31h18v4H10z"/><path fill="#FFF" d="M10 39h129v12H10z"/><path fill="#D4D6DD" d="M16 43h44v4H16z"/><path fill="#AEB1BD" d="M10 59h10v4H10z"/><path fill="#FFF" d="M10 67h129v12H10z"/><path fill="#D4D6DD" d="M16 71h26v4H16z"/><path fill="#AEB1BD" d="M10 87h35v4H10z"/><path fill="#FFF9E5" d="M10 95h42v27.7686H10z"/><circle fill="#FFC500" cx="31" cy="110.4463" r="7.8099"/><path d="M10 110.1683c4.6101 2.4917 9.3585 3.7375 14.245 3.7375 7.33 0 10.2112-5.0507 15.7083-5.0507 3.6648 0 7.6803 1.5082 12.0467 4.5246v9.3889H10v-12.6003z" fill="#D4D6DD"/><path d="M10 115.257c4.6503-2.702 8.896-4.0531 12.7373-4.0531 5.7618 0 11.322 4.9766 15.525 4.9766 2.8018 0 7.3811-2.196 13.7377-6.588v13.1761H10v-7.5116z" fill="#9AA1AE"/><path fill="#AEB1BD" d="M10 131h10v4H10z"/><path fill="#FFF" d="M10 139h129v41H10z"/><path fill="#D4D6DD" d="M16 145h117v4H16zm0 6h117v4H16zm0 6h83v4H16zm0 10h117v4H16zm0 6h43v4H16z"/><path fill="#9AA1AE" d="M61 172h2v6h-2z"/><path fill="#AEB1BD" d="M179 31h91v8h-91z"/><path fill="#D4D6DD" d="M213 42h24v4h-24z"/><path fill="#FFF9E5" d="M164 56h121v80H164z"/><circle fill="#FFC500" cx="224.5" cy="100.5" r="22.5"/><path d="M164 99.6992c13.2815 7.1784 26.9613 10.7676 41.0393 10.7676 21.117 0 29.4178-14.5508 45.2548-14.5508 10.558 0 22.1266 4.345 34.7059 13.0352V136H164V99.6992z" fill="#D4D6DD"/><path d="M164 114.3594c13.3973-7.7845 25.6291-11.6768 36.6955-11.6768 16.5995 0 32.6183 14.3374 44.7266 14.3374 8.0722 0 21.2648-6.3266 39.5779-18.98V136H164v-21.6406z" fill="#9AA1AE"/><path fill="#D4D6DD" d="M164 145h121v4H164zm0 6h121v4H164zm0 6h85.8376v4H164zm0 10h121v4H164zm0 6h44.4701v4H164z"/><path fill="#9AA1AE" d="M210 172h2v6h-2z"/><path d="M6 0h288c3.3137 0 6 2.6863 6 6v13H0V6c0-3.3137 2.6863-6 6-6z" fill="url(#c)"/></g></svg>
<svg width="318" height="198" viewBox="0 0 318 198" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>editor</title><defs><rect id="b" width="300" height="180" rx="6"/><filter x="-6%" y="-7.8%" width="112%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.147843071 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feOffset dy="3" in="SourceAlpha" result="shadowOffsetOuter2"/><feGaussianBlur stdDeviation="4.5" in="shadowOffsetOuter2" result="shadowBlurOuter2"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0974298007 0" in="shadowBlurOuter2" result="shadowMatrixOuter2"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="shadowMatrixOuter2"/></feMerge></filter><linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="c"><stop stop-color="#4779DD" offset="0%"/><stop stop-color="#3A69C7" offset="100%"/></linearGradient></defs><g transform="translate(9 6)" fill="none" fill-rule="evenodd"><use fill="#000" filter="url(#a)" xlink:href="#b"/><use fill="#FFF" xlink:href="#b"/><path d="M0 19h149v161H6c-3.3137 0-6-2.6863-6-6V19z" fill="#EFF0F4"/><path fill="#AEB1BD" d="M10 31h18v4H10z"/><path fill="#FFF" d="M10 39h129v12H10z"/><path fill="#D4D6DD" d="M16 43h44v4H16z"/><path fill="#AEB1BD" d="M10 59h10v4H10z"/><path fill="#FFF" d="M10 67h129v12H10z"/><path fill="#D4D6DD" d="M16 71h26v4H16z"/><path fill="#AEB1BD" d="M10 87h35v4H10z"/><path fill="#FFF9E5" d="M10 95h42v27.7686H10z"/><circle fill="#FFC500" cx="31" cy="110.4463" r="7.8099"/><path d="M10 110.1683c4.6101 2.4917 9.3585 3.7375 14.245 3.7375 7.33 0 10.2112-5.0507 15.7083-5.0507 3.6648 0 7.6803 1.5082 12.0467 4.5246v9.3889H10v-12.6003z" fill="#D4D6DD"/><path d="M10 115.257c4.6503-2.702 8.896-4.0531 12.7373-4.0531 5.7618 0 11.322 4.9766 15.525 4.9766 2.8018 0 7.3811-2.196 13.7377-6.588v13.1761H10v-7.5116z" fill="#9AA1AE"/><path fill="#AEB1BD" d="M10 131h10v4H10z"/><path fill="#FFF" d="M10 139h129v41H10z"/><path fill="#D4D6DD" d="M16 145h117v4H16zm0 6h117v4H16zm0 6h83v4H16zm0 10h117v4H16zm0 6h43v4H16z"/><path fill="#9AA1AE" d="M61 172h2v6h-2z"/><path fill="#AEB1BD" d="M179 31h91v8h-91z"/><path fill="#D4D6DD" d="M213 42h24v4h-24z"/><path fill="#FFF9E5" d="M164 56h121v80H164z"/><circle fill="#FFC500" cx="224.5" cy="100.5" r="22.5"/><path d="M164 99.6992c13.2815 7.1784 26.9613 10.7676 41.0393 10.7676 21.117 0 29.4178-14.5508 45.2548-14.5508 10.558 0 22.1266 4.345 34.7059 13.0352V136H164V99.6992z" fill="#D4D6DD"/><path d="M164 114.3594c13.3973-7.7845 25.6291-11.6768 36.6955-11.6768 16.5995 0 32.6183 14.3374 44.7266 14.3374 8.0722 0 21.2648-6.3266 39.5779-18.98V136H164v-21.6406z" fill="#9AA1AE"/><path fill="#D4D6DD" d="M164 145h121v4H164zm0 6h121v4H164zm0 6h85.8376v4H164zm0 10h121v4H164zm0 6h44.4701v4H164z"/><path fill="#9AA1AE" d="M210 172h2v6h-2z"/><path d="M6 0h288c3.3137 0 6 2.6863 6 6v13H0V6c0-3.3137 2.6863-6 6-6z" fill="url(#c)"/></g></svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -92,10 +92,13 @@ const useIntersectionObserver = (setActiveId: (activeId: string) => void) => {
}
const callback: IntersectionObserverCallback = headings => {
headingElementsRef.current = headings.reduce((map, headingElement) => {
map[headingElement.target.id] = headingElement;
return map;
}, headingElementsRef.current as Record<string, IntersectionObserverEntry>);
headingElementsRef.current = headings.reduce(
(map, headingElement) => {
map[headingElement.target.id] = headingElement;
return map;
},
headingElementsRef.current as Record<string, IntersectionObserverEntry>,
);
// Get all headings that are currently visible on the page
const visibleHeadings: IntersectionObserverEntry[] = [];
@ -148,6 +151,11 @@ const StyledNav = styled('nav')(
overflow-y: auto;
top: 16px;
&:hover {
overflow-y: auto;
padding-right: 0;
}
${theme.breakpoints.between('md', 'lg')} {
top: 24px;
}

View File

@ -8,7 +8,7 @@ import IconButton from '@mui/material/IconButton';
import { styled, useTheme } from '@mui/material/styles';
import Toolbar from '@mui/material/Toolbar';
import Link from 'next/link';
import { useCallback, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import releases from '../../lib/releases';
import Logo from './Logo';
@ -19,7 +19,7 @@ import SponsorButton from './SponsorButton';
import type { PaletteMode } from '@mui/material';
import type { ButtonTypeMap } from '@mui/material/Button';
import type { ExtendButtonBase } from '@mui/material/ButtonBase';
import type { DocsGroup, MenuItem, SearchablePage } from '../../interface';
import type { DocsGroup, MenuItem, MenuLink, SearchablePage } from '../../interface';
const StyledAppBar = styled(AppBar)(
({ theme }) => `
@ -55,7 +55,7 @@ const StyledGithubLink = styled('a')(
display: flex;
align-items: center;
${theme.breakpoints.down('lg')} {
${theme.breakpoints.down(1300)} {
display: none;
}
`,
@ -104,6 +104,14 @@ const StyledDesktopLink = styled(Button)(
`,
) as ExtendButtonBase<ButtonTypeMap<{}, 'a'>>;
const STATIC_CMS_DOMAIN = 'staticcms.org';
const DEFAULT_DEMO_SITE = 'demo.staticcms.org';
const STATIC_CMS_DOMAIN_REGEX = /staticcms\.org$/g;
function createDemoUrl(subdomain?: string): string {
return `https://${subdomain ? subdomain : ''}${DEFAULT_DEMO_SITE}/`;
}
interface HeaderProps {
mode: PaletteMode;
docsGroups: DocsGroup[];
@ -115,6 +123,18 @@ const Header = ({ mode, docsGroups, searchablePages, toggleColorMode }: HeaderPr
const theme = useTheme();
const [mobileOpen, setMobileOpen] = useState(false);
const [demoUrl, setDemoUrl] = useState(createDemoUrl());
useEffect(() => {
if (
typeof window === 'undefined' ||
!window.location.host.endsWith(STATIC_CMS_DOMAIN) ||
window.location.host === `www.${STATIC_CMS_DOMAIN}`
) {
return;
}
setDemoUrl(createDemoUrl(window.location.host.replace(STATIC_CMS_DOMAIN_REGEX, '')));
}, []);
const handleDrawerToggle = useCallback(() => {
setMobileOpen(!mobileOpen);
}, [mobileOpen]);
@ -142,12 +162,17 @@ const Header = ({ mode, docsGroups, searchablePages, toggleColorMode }: HeaderPr
title: 'Examples',
url: '/docs/examples',
},
{
title: 'Demo',
url: demoUrl,
target: '_blank',
},
{
title: 'Community',
url: '/community',
},
],
[docsGroups],
[demoUrl, docsGroups],
);
return (
@ -198,14 +223,21 @@ const Header = ({ mode, docsGroups, searchablePages, toggleColorMode }: HeaderPr
</StyledIconsWrapper>
{items.map(item => {
let url = '#';
let target: MenuLink['target'];
if ('url' in item) {
url = item.url;
target = item.target;
} else if (item.groups.length > 0 && item.groups[0].links.length > 0) {
url = item.groups[0].links[0].url;
}
return (
<StyledDesktopLink key={`desktop-${item.title}-${url}`} component={Link} href={url}>
<StyledDesktopLink
key={`desktop-${item.title}-${url}`}
component={Link}
href={url}
target={target}
>
{item.title}
</StyledDesktopLink>
);

View File

@ -66,7 +66,7 @@ export interface HomepageData {
export interface Release {
readonly date: string;
readonly version: string;
readonly type: 'major' | 'minor' | 'patch';
readonly type: 'major' | 'minor' | 'patch' | 'pre';
readonly description?: string;
}
@ -136,6 +136,7 @@ export interface MenuLink {
readonly url: string;
readonly beta?: boolean;
readonly deprecated?: boolean;
readonly target?: '_blank';
}
export interface MenuLinkSubGroup {

View File

@ -129,18 +129,21 @@ export function fetchDocsContent(): [DocsPage[], DocsGroup[]] {
},
);
const pagesByGroup: Record<string, DocsGroupLink[]> = allDocsData.reduce((acc, doc) => {
if (!(doc.data.group in acc)) {
acc[doc.data.group] = [];
}
acc[doc.data.group].push({
title: doc.data.title,
slug: doc.data.slug,
beta: doc.data.beta ?? false,
deprecated: doc.data.deprecated ?? false,
});
return acc;
}, {} as Record<string, DocsGroupLink[]>);
const pagesByGroup: Record<string, DocsGroupLink[]> = allDocsData.reduce(
(acc, doc) => {
if (!(doc.data.group in acc)) {
acc[doc.data.group] = [];
}
acc[doc.data.group].push({
title: doc.data.title,
slug: doc.data.slug,
beta: doc.data.beta ?? false,
deprecated: doc.data.deprecated ?? false,
});
return acc;
},
{} as Record<string, DocsGroupLink[]>,
);
const docsGroups: DocsGroup[] = menu.docs.map(group => ({
...group,

View File

@ -251,7 +251,10 @@ const StyledFeatureText = styled('div')`
const Home = ({ docsGroups, searchablePages }: DocsMenuProps) => {
const theme = useTheme();
const majorMinorThemes = useMemo(() => releases.filter(r => r.type !== 'patch'), []);
const majorMinorReleases = useMemo(
() => releases.filter(r => ['major', 'minor'].includes(r.type)),
[],
);
return (
<Page url="/" docsGroups={docsGroups} searchablePages={searchablePages} fullWidth>
@ -328,11 +331,11 @@ const Home = ({ docsGroups, searchablePages }: DocsMenuProps) => {
<Container>
<StyledReleasesSectionContent>
{[...Array(3)].map((_, index) => {
if (index >= majorMinorThemes.length) {
if (index >= majorMinorReleases.length) {
return null;
}
const release = majorMinorThemes[index];
const release = majorMinorReleases[index];
return (
<CardActionArea
key={release.version}

View File

@ -6,6 +6,7 @@ import { styled } from '@mui/material/styles';
import format from 'date-fns/format';
import parseISO from 'date-fns/parseISO';
import Link from 'next/link';
import { useMemo } from 'react';
import Container from '../components/layout/Container';
import Page from '../components/layout/Page';
@ -94,7 +95,33 @@ const StyledLink = styled(Link)(
`,
);
function getVersionNumber(version: string): number {
return +version.replace('v', '');
}
function isNextVersion(latestMajorVersionNumber: number, version: string): boolean {
if (getVersionNumber(version) > latestMajorVersionNumber) {
return true;
}
return false;
}
function getMajorVersion(version: string): string {
return version.split('.')[0];
}
const Releases = ({ docsGroups, searchablePages }: DocsMenuProps) => {
const latestMajorVersion = useMemo(
() => getMajorVersion((releaseData.find(r => r.type === 'major') ?? releaseData[0]).version),
[],
);
const latestMajorVersionNumber = useMemo(
() => getVersionNumber(latestMajorVersion),
[latestMajorVersion],
);
return (
<Page url="/releases" docsGroups={docsGroups} searchablePages={searchablePages} fullWidth>
<StyledReleaseContent>
@ -125,31 +152,50 @@ const Releases = ({ docsGroups, searchablePages }: DocsMenuProps) => {
</Container>
<Container>
<StyledReleaseLinksContent>
{releaseData.map(release => (
<StyledReleaseSection key={release.version}>
<Typography variant="h3" color="primary.main">
<strong>{release.version}</strong>
&nbsp;&nbsp;
<Box component="small" sx={{ fontSize: '16px', opacity: 0.75 }}>
{format(parseISO(release.date), 'MMM dd, yyyy')}
</Box>
</Typography>
<Typography
variant="body1"
component="div"
color="inherit"
sx={{ display: 'flex', flexDirection: 'column' }}
>
{isNotEmpty(release.description) ? release.description : null}
<StyledLink
href={`${config.repo_url}/releases/tag/${release.version}`}
target="_blank"
{releaseData.map(release => {
const majorVersion = getMajorVersion(release.version);
const isNext = isNextVersion(latestMajorVersionNumber, majorVersion);
return (
<StyledReleaseSection key={release.version}>
<Typography variant="h3" color="primary.main">
<strong>{release.version}</strong>
&nbsp;&nbsp;
<Box component="small" sx={{ fontSize: '16px', opacity: 0.75 }}>
{format(parseISO(release.date), 'MMM dd, yyyy')}
</Box>
</Typography>
<Typography
variant="body1"
component="div"
color="inherit"
sx={{ display: 'flex', flexDirection: 'column' }}
>
Changelog
</StyledLink>
</Typography>
</StyledReleaseSection>
))}
{isNotEmpty(release.description) ? release.description : null}
<Box sx={{ display: 'flex', gap: '8px' }}>
<StyledLink
href={`${config.repo_url}/releases/tag/${release.version}`}
target="_blank"
>
Changelog
</StyledLink>
<StyledLink
href={`https://${
isNext
? 'next'
: majorVersion !== latestMajorVersion
? majorVersion
: 'www'
}.staticcms.org/docs`}
target={majorVersion !== latestMajorVersion ? '_blank' : undefined}
>
Docs
</StyledLink>
</Box>
</Typography>
</StyledReleaseSection>
);
})}
</StyledReleaseLinksContent>
</Container>
</StyledReleaseLinks>