Co-authored-by: Denys Konovalov <kontakt@denyskon.de> Co-authored-by: Mathieu COSYNS <64072917+Mathieu-COSYNS@users.noreply.github.com>
539 lines
14 KiB
Plaintext
539 lines
14 KiB
Plaintext
---
|
|
group: Widgets
|
|
title: Markdown
|
|
weight: 19
|
|
---
|
|
|
|
- **Name:** `markdown`
|
|
- **UI:** [Toast UI Editor](https://ui.toast.com/tui-editor) ([Docs](https://nhn.github.io/tui.editor/latest/))
|
|
- **Data type:** `markdown string`
|
|
|
|
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 will have to name the field `body` so Static CMS recognizes it and saves the file accordingly.
|
|
|
|
## Widget Options
|
|
|
|
For common options, see [Common widget options](/docs/widgets#common-widget-options).
|
|
|
|
| Name | Type | Default | Description |
|
|
| --------------- | --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
| default | string | `''` | _Optional_. The default value for the field. Accepts markdown content |
|
|
| 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 |
|
|
| media_library | Media Library Options | `{}` | _Optional_. Media library settings to apply when the media library is opened by the current widget. See [Media Library](/docs/configuration-options#media-library) |
|
|
| choose_url | boolean | `true` | _Optional_. When set to `false`, the "Insert from URL" button will be hidden |
|
|
| toolbar_buttons | object | [] | _Optional_. Specifies which toolbar items to show for the markdown widget. See [Toolbar Customization](#toolbar-customization) |
|
|
| show_raw | boolean | `false` | _Optional_. Specifies if the raw markdown editor should be shown |
|
|
|
|
## Example
|
|
|
|
<CodeTabs>
|
|
```yaml
|
|
name: body
|
|
label: Body
|
|
widget: markdown
|
|
```
|
|
|
|
```js
|
|
name: 'body',
|
|
label: 'Body',
|
|
widget: 'markdown',
|
|
```
|
|
|
|
</CodeTabs>
|
|
|
|
This would render as:
|
|
|
|

|
|
|
|
_Please note:_ The markdown widget outputs a raw markdown string. Your static site generator may or may not render the markdown to HTML automatically. Consult with your static site generator's documentation for more information about rendering markdown.
|
|
|
|
## Toolbar Customization
|
|
|
|
| Name | Type | Default | Description |
|
|
| --------------- | ------ | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
| main | object | See [Main Toolbar](#main-toolbar) | _Optional_. A list of buttons and menus for the top level toolbar |
|
|
| empty | object | See [Empty Toolbar](#empty-toolbar) | _Optional_. A list of buttons and menus for the popup toolbar when in an empty paragraph |
|
|
| selection | object | See [Selection Toolbar](#selection-toolbar) | _Optional_. A list of buttons and menus for the popup toolbar when selecting text outside of a table cell |
|
|
| table_empty | object | See [Empty Table Cell Toolbar](#empty-table-cell-toolbar) | _Optional_. A list of buttons and menus for the popup toolbar when in an empty table cell |
|
|
| table_selection | object | See [Table Selection Toolbar](#table-selection-toolbar) | _Optional_. A list of buttons and menus for the popup toolbar when selecting text in a table cell |
|
|
|
|
### Options
|
|
|
|
All toolbars can be customized with a list consisting of buttons and dropdown menus.
|
|
|
|
#### Buttons
|
|
|
|
Buttons can be configured simply with their name. The following options are available:
|
|
|
|
- `blockquote`
|
|
- `bold`
|
|
- `code`
|
|
- `code-block`
|
|
- `decrease-indent`
|
|
- `delete-column`
|
|
- `delete-row`
|
|
- `delete-table`
|
|
- `file-link`
|
|
- `font`
|
|
- `image`
|
|
- `increase-indent`
|
|
- `insert-column`
|
|
- `insert-row`
|
|
- `insert-table`
|
|
- `italic`
|
|
- `ordered-list`
|
|
- `shortcode`
|
|
- `strikethrough`
|
|
- `unordered-list`
|
|
|
|
#### Menus
|
|
|
|
The following options are available for menus:
|
|
|
|
| Name | Type | Description |
|
|
| ------ | -------------- | ----------------------------------------------------------------------------------------- |
|
|
| label | string | The name and tooltip label for the menu |
|
|
| icon | string | _Optional_. The icon to use for the menu. If not supplied a default add icon will be used |
|
|
| groups | list of groups | A list groups of menu items. Each group is separated by a divider |
|
|
|
|
##### Menu Groups
|
|
|
|
The following options are available for menu groups:
|
|
|
|
| Name | Type | Description |
|
|
| ----- | --------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
| items | list of strings | The name of the toolbar buttons in the group. All [buttons](#buttons) are available in menus except `font` and `shortcode` |
|
|
|
|
### Default Values
|
|
|
|
Below are the default values for the various toolbars:
|
|
|
|
#### Main Toolbar
|
|
|
|
<CodeTabs>
|
|
|
|
```yaml
|
|
main:
|
|
- bold
|
|
- italic
|
|
- strikethrough
|
|
- code
|
|
- font
|
|
- unordered-list
|
|
- ordered-list
|
|
- decrease-indent
|
|
- increase-indent
|
|
- shortcode
|
|
- label: Insert
|
|
groups:
|
|
- items:
|
|
- blockquote
|
|
- code-block
|
|
- items:
|
|
- insert-table
|
|
- items:
|
|
- image
|
|
- file-link
|
|
```
|
|
|
|
```js
|
|
main: [
|
|
'bold',
|
|
'italic',
|
|
'strikethrough',
|
|
'code',
|
|
'font',
|
|
'unordered-list',
|
|
'ordered-list',
|
|
'decrease-indent',
|
|
'increase-indent',
|
|
'shortcode',
|
|
{
|
|
label: 'Insert',
|
|
groups: [
|
|
{
|
|
items: ['blockquote', 'code-block'],
|
|
},
|
|
{
|
|
items: ['insert-table'],
|
|
},
|
|
{
|
|
items: ['image', 'file-link'],
|
|
},
|
|
],
|
|
},
|
|
];
|
|
```
|
|
|
|
</CodeTabs>
|
|
|
|
#### Empty Toolbar
|
|
|
|
<CodeTabs>
|
|
|
|
```yaml
|
|
empty: []
|
|
```
|
|
|
|
```js
|
|
empty: [];
|
|
```
|
|
|
|
</CodeTabs>
|
|
|
|
#### Selection Toolbar
|
|
|
|
<CodeTabs>
|
|
|
|
```yaml
|
|
selection:
|
|
- bold
|
|
- italic
|
|
- strikethrough
|
|
- code
|
|
- font
|
|
- file-link
|
|
```
|
|
|
|
```js
|
|
selection: ['bold', 'italic', 'strikethrough', 'code', 'font', 'file-link'];
|
|
```
|
|
|
|
</CodeTabs>
|
|
|
|
#### Empty Table Cell Toolbar
|
|
|
|
<CodeTabs>
|
|
|
|
```yaml
|
|
table_empty:
|
|
- bold
|
|
- italic
|
|
- strikethrough
|
|
- code
|
|
- insert-row
|
|
- delete-row
|
|
- insert-column
|
|
- delete-column
|
|
- delete-table
|
|
- file-link
|
|
- image
|
|
- shortcode
|
|
```
|
|
|
|
```js
|
|
table_empty: [
|
|
'bold',
|
|
'italic',
|
|
'strikethrough',
|
|
'code',
|
|
'insert-row',
|
|
'delete-row',
|
|
'insert-column',
|
|
'delete-column',
|
|
'delete-table',
|
|
'file-link',
|
|
'image',
|
|
'shortcode',
|
|
];
|
|
```
|
|
|
|
</CodeTabs>
|
|
|
|
#### Table Selection Toolbar
|
|
|
|
<CodeTabs>
|
|
|
|
```yaml
|
|
table_selection:
|
|
- bold
|
|
- italic
|
|
- strikethrough
|
|
- code
|
|
- insert-row
|
|
- delete-row
|
|
- insert-column
|
|
- delete-column
|
|
- delete-table
|
|
- file-link
|
|
```
|
|
|
|
```js
|
|
table_selection: [
|
|
'bold',
|
|
'italic',
|
|
'strikethrough',
|
|
'code',
|
|
'insert-row',
|
|
'delete-row',
|
|
'insert-column',
|
|
'delete-column',
|
|
'delete-table',
|
|
'file-link',
|
|
];
|
|
```
|
|
|
|
</CodeTabs>
|
|
|
|
## Shortcodes
|
|
|
|
Shortcodes can be added to customize the Markdown editor via `registerShortcode`.
|
|
|
|
### Usage
|
|
|
|
```markdown
|
|
[youtube|p6h-rYSVX90]
|
|
```
|
|
|
|
<CodeTabs>
|
|
|
|
```js
|
|
CMS.registerShortcode('youtube', {
|
|
label: 'YouTube',
|
|
openTag: '[',
|
|
closeTag: ']',
|
|
separator: '|',
|
|
toProps: args => {
|
|
if (args.length > 0) {
|
|
return { src: args[0] };
|
|
}
|
|
|
|
return { src: '' };
|
|
},
|
|
toArgs: ({ src }) => {
|
|
return [src];
|
|
},
|
|
control: ({ src, onChange }) => {
|
|
return h('span', {}, [
|
|
h('input', {
|
|
key: 'control-input',
|
|
value: src,
|
|
onChange: event => {
|
|
onChange({ src: event.target.value });
|
|
},
|
|
}),
|
|
h(
|
|
'iframe',
|
|
{
|
|
key: 'control-preview',
|
|
width: '420',
|
|
height: '315',
|
|
src: `https://www.youtube.com/embed/${src}`,
|
|
},
|
|
'',
|
|
),
|
|
]);
|
|
},
|
|
preview: ({ src }) => {
|
|
return h(
|
|
'span',
|
|
{},
|
|
h(
|
|
'iframe',
|
|
{
|
|
width: '420',
|
|
height: '315',
|
|
src: `https://www.youtube.com/embed/${src}`,
|
|
},
|
|
'',
|
|
),
|
|
);
|
|
},
|
|
});
|
|
```
|
|
|
|
```jsx
|
|
import CMS from '@staticcms/core';
|
|
|
|
CMS.registerShortcode('youtube', {
|
|
label: 'YouTube',
|
|
openTag: '[',
|
|
closeTag: ']',
|
|
separator: '|',
|
|
toProps: args => {
|
|
if (args.length > 0) {
|
|
return { src: args[0] };
|
|
}
|
|
|
|
return { src: '' };
|
|
},
|
|
toArgs: ({ src }) => {
|
|
return [src];
|
|
},
|
|
control: ({ src, onChange }) => {
|
|
return (
|
|
<span>
|
|
<input
|
|
key="control-input"
|
|
value={src}
|
|
onChange={event => {
|
|
onChange({ src: event.target.value });
|
|
}}
|
|
/>
|
|
<iframe
|
|
key="control-preview"
|
|
width="420"
|
|
height="315"
|
|
src={`https://www.youtube.com/embed/${src}`}
|
|
/>
|
|
</span>
|
|
);
|
|
},
|
|
preview: ({ src }) => {
|
|
return (
|
|
<span>
|
|
<iframe width="420" height="315" src={`https://www.youtube.com/embed/${src}`} />
|
|
</span>
|
|
);
|
|
},
|
|
});
|
|
```
|
|
|
|
```tsx
|
|
import CMS from '@staticcms/core';
|
|
|
|
interface YouTubeShortcodeProps {
|
|
src: string;
|
|
}
|
|
|
|
CMS.registerShortcode<YouTubeShortcodeProps>('youtube', {
|
|
label: 'YouTube',
|
|
openTag: '[',
|
|
closeTag: ']',
|
|
separator: '|',
|
|
toProps: args => {
|
|
if (args.length > 0) {
|
|
return { src: args[0] };
|
|
}
|
|
|
|
return { src: '' };
|
|
},
|
|
toArgs: ({ src }) => {
|
|
return [src];
|
|
},
|
|
control: ({ src, onChange }) => {
|
|
return (
|
|
<span>
|
|
<input
|
|
key="control-input"
|
|
value={src}
|
|
onChange={event => {
|
|
onChange({ src: event.target.value });
|
|
}}
|
|
/>
|
|
<iframe
|
|
key="control-preview"
|
|
width="420"
|
|
height="315"
|
|
src={`https://www.youtube.com/embed/${src}`}
|
|
/>
|
|
</span>
|
|
);
|
|
},
|
|
preview: ({ src }) => {
|
|
return (
|
|
<span>
|
|
<iframe width="420" height="315" src={`https://www.youtube.com/embed/${src}`} />
|
|
</span>
|
|
);
|
|
},
|
|
});
|
|
```
|
|
|
|
</CodeTabs>
|
|
|
|
### Interacting With The Media Library
|
|
|
|
If you want to use the media library in your shortcode you will need to use the `useMediaInsert` and `useMediaAsset` hooks.
|
|
|
|
- `useMediaInsert` - Takes the current url to your media, details about your field and a callback method for when new media is uploaded. If you want to select folders instead of files, set the `forFolder`variable in options.
|
|
- `useMediaAsset` - Transforms your stored url into a usable url for displaying as a preview.
|
|
|
|
<CodeTabs>
|
|
|
|
```js
|
|
const ImageControl = ({ src, onChange, controlProps }) => {
|
|
const { collection, field, entry } = controlProps;
|
|
|
|
const handleChange = ({ path }) => {
|
|
onChange({ src: path });
|
|
};
|
|
|
|
const handleOpenMediaLibrary = useMediaInsert(
|
|
src,
|
|
{ collection: collection, field },
|
|
handleChange,
|
|
);
|
|
|
|
const assetSource = useMediaAsset(src, collection, field, entry);
|
|
|
|
return [
|
|
h('button', { type: 'button', onClick: handleOpenMediaLibrary }, 'Upload'),
|
|
h('img', { role: 'presentation', src: assetSource }),
|
|
];
|
|
};
|
|
```
|
|
|
|
```jsx
|
|
import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset';
|
|
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';
|
|
|
|
const ImageControl = ({ src, onChange, controlProps }) => {
|
|
const { collection, field, entry } = controlProps;
|
|
|
|
const handleChange = ({ path }) => {
|
|
onChange({ src: path });
|
|
};
|
|
|
|
const handleOpenMediaLibrary = useMediaInsert(
|
|
src,
|
|
{ collection: collection, field },
|
|
handleChange,
|
|
);
|
|
|
|
const assetSource = useMediaAsset(src, collection, field, entry);
|
|
|
|
return (
|
|
<>
|
|
<button type="button" onClick={handleOpenMediaLibrary}>
|
|
Upload
|
|
</button>
|
|
<img role="presentation" src={assetSource} />
|
|
</>
|
|
);
|
|
};
|
|
```
|
|
|
|
```tsx
|
|
import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset';
|
|
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';
|
|
|
|
import type { WidgetControlProps, MediaPath } from '@staticcms/core/interface';
|
|
import type { FC } from 'react';
|
|
|
|
const FileControl: FC<WidgetControlProps<string, MyField>> = ({ src, onChange, controlProps }) => {
|
|
const { collection, field, entry } = controlProps;
|
|
|
|
const handleChange = ({ path }: MediaPath) => {
|
|
onChange({ src: path });
|
|
};
|
|
|
|
const handleOpenMediaLibrary = useMediaInsert(src, { collection, field }, handleChange);
|
|
|
|
const assetSource = useMediaAsset(value, collection, field, entry);
|
|
|
|
return (
|
|
<>
|
|
<button type="button" onClick={handleOpenMediaLibrary}>
|
|
Upload
|
|
</button>
|
|
<img role="presentation" src={assetSource} />
|
|
</>
|
|
);
|
|
};
|
|
```
|
|
|
|
</CodeTabs>
|