Improve documentation, better typing on media folder selection
This commit is contained in:
parent
590681d64e
commit
fdd51aefa3
@ -520,8 +520,6 @@ export interface BaseField {
|
|||||||
hint?: string;
|
hint?: string;
|
||||||
pattern?: [string, string];
|
pattern?: [string, string];
|
||||||
i18n?: boolean | 'translate' | 'duplicate' | 'none';
|
i18n?: boolean | 'translate' | 'duplicate' | 'none';
|
||||||
media_folder?: string;
|
|
||||||
public_folder?: string;
|
|
||||||
comment?: string;
|
comment?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,7 +557,7 @@ export interface DateTimeField extends BaseField {
|
|||||||
format?: string;
|
format?: string;
|
||||||
date_format?: boolean | string;
|
date_format?: boolean | string;
|
||||||
time_format?: boolean | string;
|
time_format?: boolean | string;
|
||||||
picker_utc?: boolean; // TODO Reimplement
|
picker_utc?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileOrImageField extends BaseField {
|
export interface FileOrImageField extends BaseField {
|
||||||
@ -567,6 +565,8 @@ export interface FileOrImageField extends BaseField {
|
|||||||
default?: string;
|
default?: string;
|
||||||
|
|
||||||
media_library?: MediaLibrary;
|
media_library?: MediaLibrary;
|
||||||
|
media_folder?: string;
|
||||||
|
public_folder?: string;
|
||||||
private?: boolean;
|
private?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,17 @@ import { folderFormatter } from '../formatters';
|
|||||||
import { joinUrlPath } from '../urlHelper';
|
import { joinUrlPath } from '../urlHelper';
|
||||||
import { basename, isAbsolutePath } from '.';
|
import { basename, isAbsolutePath } from '.';
|
||||||
|
|
||||||
import type { Config, Field, Collection, CollectionFile, Entry } from '../../interface';
|
import type {
|
||||||
|
Config,
|
||||||
|
Field,
|
||||||
|
Collection,
|
||||||
|
CollectionFile,
|
||||||
|
Entry,
|
||||||
|
FileOrImageField,
|
||||||
|
MarkdownField,
|
||||||
|
ListField,
|
||||||
|
ObjectField,
|
||||||
|
} from '../../interface';
|
||||||
|
|
||||||
export const DRAFT_MEDIA_FILES = 'DRAFT_MEDIA_FILES';
|
export const DRAFT_MEDIA_FILES = 'DRAFT_MEDIA_FILES';
|
||||||
|
|
||||||
@ -14,17 +24,28 @@ function getFileField(collectionFiles: CollectionFile[], slug: string | undefine
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMediaField(
|
||||||
|
folderKey: 'media_folder' | 'public_folder',
|
||||||
|
field: Field | undefined,
|
||||||
|
): field is FileOrImageField | MarkdownField {
|
||||||
|
return Boolean(field && folderKey in field);
|
||||||
|
}
|
||||||
|
|
||||||
function hasCustomFolder(
|
function hasCustomFolder(
|
||||||
folderKey: 'media_folder' | 'public_folder',
|
folderKey: 'media_folder' | 'public_folder',
|
||||||
collection: Collection | undefined | null,
|
collection: Collection | undefined | null,
|
||||||
slug: string | undefined,
|
slug: string | undefined,
|
||||||
field: Field | undefined,
|
field: Field | undefined,
|
||||||
) {
|
): field is FileOrImageField | MarkdownField {
|
||||||
if (!collection) {
|
if (!collection) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field && field[folderKey]) {
|
if (!isMediaField(folderKey, field)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field[folderKey]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +68,7 @@ function evaluateFolder(
|
|||||||
config: Config,
|
config: Config,
|
||||||
c: Collection,
|
c: Collection,
|
||||||
entryMap: Entry | undefined,
|
entryMap: Entry | undefined,
|
||||||
field: Field | undefined,
|
field: FileOrImageField | MarkdownField,
|
||||||
) {
|
) {
|
||||||
let currentFolder = config[folderKey]!;
|
let currentFolder = config[folderKey]!;
|
||||||
|
|
||||||
@ -140,12 +161,17 @@ function traverseFields(
|
|||||||
config: Config,
|
config: Config,
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
entryMap: Entry | undefined,
|
entryMap: Entry | undefined,
|
||||||
field: Field,
|
field: FileOrImageField | MarkdownField | ListField | ObjectField,
|
||||||
fields: Field[],
|
fields: Field[],
|
||||||
currentFolder: string,
|
currentFolder: string,
|
||||||
): string | null {
|
): string | null {
|
||||||
const matchedField = fields.filter(f => f === field)[0];
|
const matchedField = fields.filter(f => f === field)[0] as
|
||||||
if (matchedField) {
|
| FileOrImageField
|
||||||
|
| MarkdownField
|
||||||
|
| ListField
|
||||||
|
| ObjectField
|
||||||
|
| undefined;
|
||||||
|
if (matchedField && isMediaField(folderKey, matchedField)) {
|
||||||
return folderFormatter(
|
return folderFormatter(
|
||||||
matchedField[folderKey] ? matchedField[folderKey]! : `{{${folderKey}}}`,
|
matchedField[folderKey] ? matchedField[folderKey]! : `{{${folderKey}}}`,
|
||||||
entryMap,
|
entryMap,
|
||||||
@ -157,13 +183,13 @@ function traverseFields(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const f of fields) {
|
for (const f of fields) {
|
||||||
const field = { ...f };
|
const childField: Field = { ...f };
|
||||||
if (!field[folderKey]) {
|
if (isMediaField(folderKey, childField) && !childField[folderKey]) {
|
||||||
// add identity template if doesn't exist
|
// add identity template if doesn't exist
|
||||||
field[folderKey] = `{{${folderKey}}}`;
|
childField[folderKey] = `{{${folderKey}}}`;
|
||||||
}
|
}
|
||||||
const folder = folderFormatter(
|
const folder = folderFormatter(
|
||||||
field[folderKey]!,
|
isMediaField(folderKey, childField) ? childField[folderKey] ?? '' : '',
|
||||||
entryMap,
|
entryMap,
|
||||||
collection,
|
collection,
|
||||||
currentFolder,
|
currentFolder,
|
||||||
@ -171,24 +197,24 @@ function traverseFields(
|
|||||||
config.slug,
|
config.slug,
|
||||||
);
|
);
|
||||||
let fieldFolder = null;
|
let fieldFolder = null;
|
||||||
if ('fields' in field && field.fields) {
|
if ('fields' in childField && childField.fields) {
|
||||||
fieldFolder = traverseFields(
|
fieldFolder = traverseFields(
|
||||||
folderKey,
|
folderKey,
|
||||||
config,
|
config,
|
||||||
collection,
|
collection,
|
||||||
entryMap,
|
entryMap,
|
||||||
field,
|
childField,
|
||||||
field.fields,
|
childField.fields,
|
||||||
folder,
|
folder,
|
||||||
);
|
);
|
||||||
} else if ('types' in field && field.types) {
|
} else if ('types' in childField && childField.types) {
|
||||||
fieldFolder = traverseFields(
|
fieldFolder = traverseFields(
|
||||||
folderKey,
|
folderKey,
|
||||||
config,
|
config,
|
||||||
collection,
|
collection,
|
||||||
entryMap,
|
entryMap,
|
||||||
field,
|
childField,
|
||||||
field.types,
|
childField.types,
|
||||||
folder,
|
folder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -209,9 +235,7 @@ export function selectMediaFolder(
|
|||||||
const name = 'media_folder';
|
const name = 'media_folder';
|
||||||
let mediaFolder = config[name];
|
let mediaFolder = config[name];
|
||||||
|
|
||||||
const customFolder = hasCustomFolder(name, collection, entryMap?.slug, field);
|
if (hasCustomFolder(name, collection, entryMap?.slug, field)) {
|
||||||
|
|
||||||
if (customFolder) {
|
|
||||||
const folder = evaluateFolder(name, config, collection!, entryMap, field);
|
const folder = evaluateFolder(name, config, collection!, entryMap, field);
|
||||||
if (folder.startsWith('/')) {
|
if (folder.startsWith('/')) {
|
||||||
// return absolute paths as is
|
// return absolute paths as is
|
||||||
|
@ -64,12 +64,9 @@ const DateTimeControl = ({
|
|||||||
const format = field.format;
|
const format = field.format;
|
||||||
|
|
||||||
// dateFormat and timeFormat are strictly for modifying input field with the date/time pickers
|
// dateFormat and timeFormat are strictly for modifying input field with the date/time pickers
|
||||||
const dateFormat: string | boolean = field.date_format ?? false;
|
const dateFormat: string | boolean = field.date_format ?? true;
|
||||||
// show time-picker? false hides it, true shows it using default format
|
// show time-picker? false hides it, true shows it using default format
|
||||||
let timeFormat: string | boolean = field.time_format ?? false;
|
const timeFormat: string | boolean = field.time_format ?? true;
|
||||||
if (typeof timeFormat === 'undefined') {
|
|
||||||
timeFormat = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
format,
|
format,
|
||||||
|
@ -3,9 +3,10 @@ group: Accounts
|
|||||||
title: Azure
|
title: Azure
|
||||||
weight: 20
|
weight: 20
|
||||||
---
|
---
|
||||||
|
|
||||||
For repositories stored on Azure, the `azure` backend allows CMS users to log in directly with their Azure account. Note that all users must have write access to your content repository for this to work.
|
For repositories stored on Azure, the `azure` backend allows CMS users to log in directly with their Azure account. Note that all users must have write access to your content repository for this to work.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
In order to get Static CMS working with Azure DevOps, you need a Tenant Id and an Application Id.
|
In order to get Static CMS working with Azure DevOps, you need a Tenant Id and an Application Id.
|
||||||
|
|
||||||
1. If you do not have an Azure account, [create one here](https://azure.microsoft.com/en-us/free/?WT.mc_id=A261C142F) and make sure to have a credit card linked to the account.
|
1. If you do not have an Azure account, [create one here](https://azure.microsoft.com/en-us/free/?WT.mc_id=A261C142F) and make sure to have a credit card linked to the account.
|
||||||
|
@ -5,7 +5,9 @@ weight: 20
|
|||||||
---
|
---
|
||||||
For repositories stored on Bitbucket, the `bitbucket` backend allows CMS users to log in directly with their Bitbucket account. Note that all users must have write access to your content repository for this to work.
|
For repositories stored on Bitbucket, the `bitbucket` backend allows CMS users to log in directly with their Bitbucket account. Note that all users must have write access to your content repository for this to work.
|
||||||
|
|
||||||
To enable it:
|
## Authentication
|
||||||
|
|
||||||
|
To enable Bitbucket authentication it:
|
||||||
|
|
||||||
1. Follow the authentication provider setup steps in the [Netlify docs](https://www.netlify.com/docs/authentication-providers/#using-an-authentication-provider).
|
1. Follow the authentication provider setup steps in the [Netlify docs](https://www.netlify.com/docs/authentication-providers/#using-an-authentication-provider).
|
||||||
2. Add the following lines to your Static CMS `config.yml` file:
|
2. Add the following lines to your Static CMS `config.yml` file:
|
||||||
|
@ -5,6 +5,8 @@ weight: 30
|
|||||||
---
|
---
|
||||||
For repositories stored on GitHub, the `github` backend allows CMS users to log in directly with their GitHub account. Note that all users must have push access to your content repository for this to work.
|
For repositories stored on GitHub, the `github` backend allows CMS users to log in directly with their GitHub account. Note that all users must have push access to your content repository for this to work.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
Because Github requires a server for authentication, Netlify facilitates basic GitHub authentication.
|
Because Github requires a server for authentication, Netlify facilitates basic GitHub authentication.
|
||||||
|
|
||||||
To enable basic GitHub authentication:
|
To enable basic GitHub authentication:
|
||||||
|
@ -7,7 +7,7 @@ For repositories stored on GitLab, the `gitlab` backend allows CMS users to log
|
|||||||
|
|
||||||
**Note:** GitLab default branch is protected by default, thus typically requires `maintainer` permissions in order for users to have push access.
|
**Note:** GitLab default branch is protected by default, thus typically requires `maintainer` permissions in order for users to have push access.
|
||||||
|
|
||||||
## Authorization
|
## Authentication
|
||||||
|
|
||||||
With GitLab's PKCE authorization, users can authenticate with GitLab directly from the client. To do this:
|
With GitLab's PKCE authorization, users can authenticate with GitLab directly from the client. To do this:
|
||||||
|
|
||||||
|
@ -6,12 +6,19 @@ weight: 10
|
|||||||
|
|
||||||
The boolean widget translates a toggle switch input to a true/false value.
|
The boolean widget translates a toggle switch input to a true/false value.
|
||||||
|
|
||||||
- **Name:** `boolean`
|
## Widget options
|
||||||
- **UI:** toggle switch
|
|
||||||
- **Data type:** boolean
|
For common options, see [Common widget options](/docs/widgets#common-widget-options).
|
||||||
- **Options:**
|
|
||||||
- `default`: accepts `true` or `false`; defaults to `false` when `required` is set to `false`
|
|Name|Type|Default|Description|
|
||||||
- **Example:**
|
|----|----|-------|-----------|
|
||||||
```yaml
|
|default|boolean|`false`|_Optional_. The default value for the field|
|
||||||
- {label: "Draft", title: "draft", widget: "boolean", default: true}
|
|
||||||
```
|
## Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: draft
|
||||||
|
label: Draft
|
||||||
|
widget: boolean
|
||||||
|
default: true
|
||||||
|
```
|
||||||
|
@ -6,18 +6,21 @@ weight: 11
|
|||||||
|
|
||||||
The code widget provides a code editor (powered by [Codemirror](https://codemirror.net)) with optional syntax awareness. Can output the raw code value or an object with the selected language and the raw code value.
|
The code widget provides a code editor (powered by [Codemirror](https://codemirror.net)) with optional syntax awareness. Can output the raw code value or an object with the selected language and the raw code value.
|
||||||
|
|
||||||
- **Name:** `code`
|
## Widget options
|
||||||
- **UI:** code editor
|
|
||||||
- **Data type:** string
|
|
||||||
- **Options:**
|
|
||||||
- `default_language`: optional; default language to use
|
|
||||||
- `allow_language_selection`: optional; defaults to `false`: allows syntax to be changed
|
|
||||||
- `keys`: optional; sets key names for code and lang if outputting an object; defaults to `{ code: 'code', lang: 'lang' }`
|
|
||||||
- `output_code_only`: set to `true` to output the string value only, defaults to `false`
|
|
||||||
|
|
||||||
- **Example:**
|
For common options, see [Common widget options](/docs/widgets#common-widget-options).
|
||||||
```yaml
|
|
||||||
- label: 'Code'
|
| Name | Type | Default | Description |
|
||||||
title: 'code'
|
| ------------------------ | ------- | -------------------------------- | -------------------------------------------------------------------- |
|
||||||
widget: 'code'
|
| default_language | string | | _Optional_. Default language to use |
|
||||||
```
|
| allow_language_selection | boolean | `false` | _Optional_. Allows language syntax to be changed |
|
||||||
|
| keys | boolean | `{ code: 'code', lang: 'lang' }` | _Optional_. Sets key names for code and lang if outputting an object |
|
||||||
|
| output_code_only | string | `true` | _Optional_. Set to `true` to output the string value only |
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: code
|
||||||
|
label: Code
|
||||||
|
widget: code
|
||||||
|
```
|
||||||
|
@ -6,18 +6,31 @@ weight: 12
|
|||||||
|
|
||||||
The color widget translates a color picker to a color string.
|
The color widget translates a color picker to a color string.
|
||||||
|
|
||||||
- **Name:** `color`
|
## Widget options
|
||||||
- **UI:** color picker
|
|
||||||
- **Data type:** string
|
For common options, see [Common widget options](/docs/widgets#common-widget-options).
|
||||||
- **Options:**
|
|
||||||
- `default`: accepts a string; defaults to an empty string. Sets the default value
|
| Name | Type | Default | Description |
|
||||||
- `allow_input`: accepts a boolean, defaults to `false`. Allows manual editing of the color input value
|
| ------------ | ------- | ------- | ---------------------------------------------------------- |
|
||||||
- `enable_alpha`: accepts a boolean, defaults to `false`. Enables Alpha editing
|
| default | string | `''` | _Optional_. The default value for the field |
|
||||||
- **Example:**
|
| allow_input | boolean | `false` | _Optional_. Allows manual editing of the color input value |
|
||||||
```yaml
|
| enable_alpha | boolean | `false` | _Optional_. Enables Alpha editing |
|
||||||
- { label: 'Color', title: 'color', widget: 'color' }
|
|
||||||
```
|
## Examples
|
||||||
- **Example:**
|
|
||||||
```yaml
|
### Basic
|
||||||
- { label: 'Color', title: 'color', widget: 'color', enable_alpha: true, allow_input: true }
|
```yaml
|
||||||
```
|
name: color
|
||||||
|
label: Color
|
||||||
|
widget: color
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kitchen Sink
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: color
|
||||||
|
label: Color
|
||||||
|
widget: color
|
||||||
|
enable_alpha: true
|
||||||
|
allow_input: true
|
||||||
|
```
|
||||||
|
@ -6,23 +6,49 @@ weight: 13
|
|||||||
|
|
||||||
The datetime widget translates a datetime picker to a datetime string.
|
The datetime widget translates a datetime picker to a datetime string.
|
||||||
|
|
||||||
- **Name:** `datetime`
|
## Widget options
|
||||||
- **UI:** datetime picker
|
|
||||||
- **Data type:** Moment.js-formatted datetime string
|
For common options, see [Common widget options](/docs/widgets#common-widget-options).
|
||||||
- **Options:**
|
|
||||||
- `default`: accepts a datetime string, or an empty string to accept blank input; otherwise defaults to current datetime
|
| Name | Type | Default | Description |
|
||||||
- `format`: sets storage format; accepts Moment.js [tokens](https://momentjs.com/docs/#/parsing/string-format/); defaults to raw Date object (if supported by output format)
|
| ----------- | ---------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
- `date_format`: sets date display format in UI; boolean or Moment.js [tokens](https://momentjs.com/docs/#/parsing/string-format/). If `true` use default locale format.
|
| 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. |
|
||||||
- `time_format`: sets time display format in UI; boolean or Moment.js [tokens](https://momentjs.com/docs/#/parsing/string-format/). If `true` use default locale format, `false` hides time-picker.
|
| 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) |
|
||||||
- `picker_utc`: _(default: `false`)_ when set to `true`, the datetime picker will display times in UTC. When `false`, the datetime picker will display times in the user's local timezone. 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.
|
| 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> |
|
||||||
- **Example:**
|
| 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> |
|
||||||
```yaml
|
| 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 |
|
||||||
- label: "Start time"
|
|
||||||
title: "start"
|
## Examples
|
||||||
widget: "datetime"
|
|
||||||
default: ""
|
### Date Time Picker
|
||||||
date_format: "DD.MM.YYYY" # e.g. 24.12.2021
|
|
||||||
time_format: "HH:mm" # e.g. 21:07
|
```yaml
|
||||||
format: "LLL"
|
name: 'datetime'
|
||||||
picker_utc: false
|
label: 'Datetime'
|
||||||
```
|
widget: 'datetime'
|
||||||
|
date_format: 'dd.MM.yyyy' # e.g. 24.12.2022
|
||||||
|
time_format: 'HH:mm' # e.g. 21:07
|
||||||
|
format: 'yyyy-MM-dd HH:mm' # e.g. 2022-12-24 21:07
|
||||||
|
```
|
||||||
|
|
||||||
|
### Date Picker
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: 'date'
|
||||||
|
label: 'Date'
|
||||||
|
widget: 'datetime'
|
||||||
|
date_format: 'dd.MM.yyyy' # e.g. 24.12.2022
|
||||||
|
time_format: false
|
||||||
|
format: 'yyyy-MM-dd' # e.g. 2022-12-24
|
||||||
|
```
|
||||||
|
|
||||||
|
### Time Picker
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: 'date'
|
||||||
|
label: 'Date'
|
||||||
|
widget: 'datetime'
|
||||||
|
date_format: false
|
||||||
|
time_format: 'HH:mm' # e.g. 21:07
|
||||||
|
format: 'HH:mm' # e.g. 21:07
|
||||||
|
```
|
||||||
|
@ -4,23 +4,34 @@ title: Overview
|
|||||||
weight: 0
|
weight: 0
|
||||||
---
|
---
|
||||||
|
|
||||||
Widgets define the data type and interface for entry fields. Static CMS comes with several built-in widgets. Click the widget names in the sidebar to jump to specific widget details. We're always adding new widgets, and you can also [create your own](/docs/custom-widgets)!
|
Widgets define the data type and interface for entry fields. Static CMS comes with several built-in widgets. Click the widget names in the sidebar to jump to specific widget details. You can also [create your own](/docs/custom-widgets)!
|
||||||
|
|
||||||
Widgets are specified as collection fields in the Static CMS `config.yml` file. Note that [YAML syntax](https://en.wikipedia.org/wiki/YAML#Basic_components) allows lists and objects to be written in block or inline style, and the code samples below include a mix of both.
|
Widgets are specified as collection fields in the Static CMS `config.yml` file. Note that [YAML syntax](https://en.wikipedia.org/wiki/YAML#Basic_components) allows lists and objects to be written in block or inline style, and the code samples below include a mix of both.
|
||||||
|
|
||||||
To see working examples of all of the built-in widgets, try making a 'Kitchen Sink' collection item on the [CMS demo site](https://cms-demo.netlify.com). (No login required: click the login button and the CMS will open.) You can refer to the demo [configuration code](https://github.com/StaticJsCMS/static-cms/blob/main/dev-test/config.yml) to see how each field was configured.
|
To see working examples of all of the built-in widgets, try making a 'Kitchen Sink' collection item on the [CMS demo site](https://static-cms-demo.netlify.com). (No login required: click the login button and the CMS will open.) You can refer to the demo [configuration code](https://github.com/StaticJsCMS/static-cms/blob/main/dev-test/config.yml) to see how each field was configured.
|
||||||
|
|
||||||
## Common widget options
|
## Common widget options
|
||||||
|
|
||||||
The following options are available on all fields:
|
The following options are available on all fields:
|
||||||
|
|
||||||
- `required`: specify as `false` to make a field optional; defaults to `true`
|
| Name | Type | Default | Description |
|
||||||
- `hint`: optionally add helper text directly below a widget. Useful for including instructions. Accepts markdown for bold, italic, strikethrough, and links.
|
| ------------- | ----------------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
- `pattern`: add 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)
|
| name | string | | The name of the field |
|
||||||
- **Example:**
|
| widget | string | `'string'` | _Optional_. The type of widget to render for the field |
|
||||||
```yaml
|
| label | string | `name` | _Optional_. The display name of the field |
|
||||||
label: "Title"
|
| required | boolean | `true` | _Optional_. Specify as `false` to make a field optional |
|
||||||
title: "title"
|
| hint | string | | _Optional_. Adds helper text directly below a widget. Useful for including instructions. Accepts markdown for bold, italic, strikethrough, and links. |
|
||||||
widget: "string"
|
| pattern | string | | _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) |
|
||||||
pattern: ['.{12,}', "Must have at least 12 characters"]
|
| i18n | boolean<br />\|'translate'<br />\|'duplicate'<br />\|'none' | | _Optional_. <img src="https://img.shields.io/badge/-Beta%20Feature-blue" alt="Beta Feature" /><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> |
|
||||||
```
|
| media_folder | string | | _Optional_. Specifies the folder path where uploaded files should be saved, relative to the base of the repo |
|
||||||
|
| public_folder | string | | _Optional_. Specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site |
|
||||||
|
| comment | string | | _Optional_. Adds comment before the field (only supported for yaml) |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: title
|
||||||
|
label: Title
|
||||||
|
widget: string
|
||||||
|
pattern: ['.{12,}', 'Must have at least 12 characters']
|
||||||
|
```
|
||||||
|
@ -155,6 +155,19 @@ const DocsContent = styled('div')(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& table thead tr th,
|
||||||
|
& table thead tr td {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
& table tbody tr td {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
& table tbody tr td:last-child {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
& pre {
|
& pre {
|
||||||
display: block;
|
display: block;
|
||||||
line-height: 1.25rem;
|
line-height: 1.25rem;
|
||||||
@ -197,8 +210,9 @@ const DocsContent = styled('div')(
|
|||||||
padding: 0.2em 0.4em;
|
padding: 0.2em 0.4em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
color: ${theme.palette.text.primary};
|
||||||
background-color: ${
|
background-color: ${
|
||||||
theme.palette.mode === 'light' ? 'rgba(175,184,193,0.2)' : 'rgba(110,118,129,0.4)'
|
theme.palette.mode === 'light' ? 'rgba(175,184,193,0.2)' : 'rgba(110,118,129,0.75)'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,59 +236,6 @@ const DocsContent = styled('div')(
|
|||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
& table {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
border-spacing: 0;
|
|
||||||
margin: 24px 0;
|
|
||||||
border: 1px solid ${theme.palette.text.secondary};
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.editor table {
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
& table thead {
|
|
||||||
background: ${theme.palette.background.paper};
|
|
||||||
}
|
|
||||||
|
|
||||||
& table th,
|
|
||||||
& table thead td {
|
|
||||||
font-weight: 700;
|
|
||||||
height: 56px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
& table th,
|
|
||||||
& table thead td,
|
|
||||||
& table td {
|
|
||||||
padding: 8px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
& table tr:not(:first-of-type) th,
|
|
||||||
& table tr:not(:first-of-type) thead td,
|
|
||||||
& table tbody tr td {
|
|
||||||
border-top: 1px solid ${theme.palette.text.secondary};
|
|
||||||
}
|
|
||||||
|
|
||||||
& table tr td {
|
|
||||||
background-color: ${theme.palette.background.paper};
|
|
||||||
}
|
|
||||||
|
|
||||||
& table tbody tr td {
|
|
||||||
height: 52px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
& table tbody tr:hover td {
|
|
||||||
background-color: ${theme.palette.background.paper};
|
|
||||||
}
|
|
||||||
|
|
||||||
& ol,
|
& ol,
|
||||||
& ul {
|
& ul {
|
||||||
padding: 0 0 0 1.5rem;
|
padding: 0 0 0 1.5rem;
|
||||||
|
34
website/src/components/docs/components/table/Table.tsx
Normal file
34
website/src/components/docs/components/table/Table.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import Table from '@mui/material/Table';
|
||||||
|
import TableContainer from '@mui/material/TableContainer';
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
const StyledTableContainer = styled(TableContainer)(
|
||||||
|
({ theme }) => `
|
||||||
|
& td {
|
||||||
|
color: ${theme.palette.text.secondary};
|
||||||
|
}
|
||||||
|
|
||||||
|
& td:nth-of-type(2) {
|
||||||
|
color:
|
||||||
|
${theme.palette.mode === 'light' ? '#751365' : '#ffb6ec'};
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
|
interface DocsTableProps {
|
||||||
|
children?: ReactNode | ReactNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const DocsTable = ({ children = [] }: DocsTableProps) => {
|
||||||
|
return (
|
||||||
|
<StyledTableContainer>
|
||||||
|
<Table sx={{ width: '100%' }} aria-label="doc table">
|
||||||
|
{children}
|
||||||
|
</Table>
|
||||||
|
</StyledTableContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DocsTable;
|
13
website/src/components/docs/components/table/TableBody.tsx
Normal file
13
website/src/components/docs/components/table/TableBody.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import MuiTableBody from '@mui/material/TableBody';
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
interface TableBodyProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TableBody = ({ children }: TableBodyProps) => {
|
||||||
|
return <MuiTableBody>{children}</MuiTableBody>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableBody;
|
@ -0,0 +1,37 @@
|
|||||||
|
import TableCell from '@mui/material/TableCell';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
interface TableBodyCellProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TableBodyCell = ({ children }: TableBodyCellProps) => {
|
||||||
|
return (
|
||||||
|
<TableCell
|
||||||
|
scope="row"
|
||||||
|
sx={{
|
||||||
|
padding: '16px 12px',
|
||||||
|
'&:first-child, &:first-child': {
|
||||||
|
paddingLeft: 0,
|
||||||
|
},
|
||||||
|
'&:last-child, &:last-child': {
|
||||||
|
paddingRight: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
component="span"
|
||||||
|
sx={{
|
||||||
|
fontSize: '13px',
|
||||||
|
fontFamily: 'Consolas, Menlo, Monaco, Andale Mono, Ubuntu Mono, monospace',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableBodyCell;
|
13
website/src/components/docs/components/table/TableHead.tsx
Normal file
13
website/src/components/docs/components/table/TableHead.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import MuiTableHead from '@mui/material/TableHead';
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
interface TableHeadProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TableHead = ({ children }: TableHeadProps) => {
|
||||||
|
return <MuiTableHead>{children}</MuiTableHead>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableHead;
|
@ -0,0 +1,28 @@
|
|||||||
|
import TableCell from '@mui/material/TableCell';
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
interface TableHeaderCellProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TableHeaderCell = ({ children }: TableHeaderCellProps) => {
|
||||||
|
return (
|
||||||
|
<TableCell
|
||||||
|
sx={{
|
||||||
|
fontWeight: 600,
|
||||||
|
padding: '16px 12px',
|
||||||
|
'&:first-child, &:first-child': {
|
||||||
|
paddingLeft: 0,
|
||||||
|
},
|
||||||
|
'&:last-child, &:last-child': {
|
||||||
|
paddingRight: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</TableCell>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableHeaderCell;
|
15
website/src/components/docs/components/table/TableRow.tsx
Normal file
15
website/src/components/docs/components/table/TableRow.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import MuiTableRow from '@mui/material/TableRow';
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
interface TableRowProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TableRow = ({ children }: TableRowProps) => {
|
||||||
|
return (
|
||||||
|
<MuiTableRow sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>{children}</MuiTableRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableRow;
|
@ -7,6 +7,11 @@ import remarkGfm from 'remark-gfm';
|
|||||||
import Blockquote from '../../components/docs/components/Blockquote';
|
import Blockquote from '../../components/docs/components/Blockquote';
|
||||||
import Header2 from '../../components/docs/components/Header2';
|
import Header2 from '../../components/docs/components/Header2';
|
||||||
import Header3 from '../../components/docs/components/Header3';
|
import Header3 from '../../components/docs/components/Header3';
|
||||||
|
import DocsTable from '../../components/docs/components/table/Table';
|
||||||
|
import TableBody from '../../components/docs/components/table/TableBody';
|
||||||
|
import TableBodyCell from '../../components/docs/components/table/TableBodyCell';
|
||||||
|
import TableHead from '../../components/docs/components/table/TableHead';
|
||||||
|
import TableHeaderCell from '../../components/docs/components/table/TableHeaderCell';
|
||||||
import DocsContent from '../../components/docs/DocsContent';
|
import DocsContent from '../../components/docs/DocsContent';
|
||||||
import DocsLeftNav from '../../components/docs/DocsLeftNav';
|
import DocsLeftNav from '../../components/docs/DocsLeftNav';
|
||||||
import DocsRightNav from '../../components/docs/DocsRightNav';
|
import DocsRightNav from '../../components/docs/DocsRightNav';
|
||||||
@ -87,7 +92,16 @@ const Docs = ({ docsGroups, title, slug, description = '', source }: DocsProps)
|
|||||||
<Typography variant="h1">{title}</Typography>
|
<Typography variant="h1">{title}</Typography>
|
||||||
<MDXRemote
|
<MDXRemote
|
||||||
{...source}
|
{...source}
|
||||||
components={{ h2: Header2, h3: Header3, blockquote: Blockquote }}
|
components={{
|
||||||
|
h2: Header2,
|
||||||
|
h3: Header3,
|
||||||
|
blockquote: Blockquote,
|
||||||
|
table: DocsTable,
|
||||||
|
thead: TableHead,
|
||||||
|
tbody: TableBody,
|
||||||
|
th: TableHeaderCell,
|
||||||
|
td: TableBodyCell,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</DocsContent>
|
</DocsContent>
|
||||||
</StyledDocsContentWrapper>
|
</StyledDocsContentWrapper>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user