docs: update examples (#411)
This commit is contained in:
parent
fcfbd6de13
commit
cab7166eb7
@ -374,12 +374,12 @@ export function getAdditionalLink(id: string): AdditionalLink | undefined {
|
||||
/**
|
||||
* Markdown editor shortcodes
|
||||
*/
|
||||
export function registerShortcode(name: string, config: ShortcodeConfig) {
|
||||
export function registerShortcode<P = {}>(name: string, config: ShortcodeConfig<P>) {
|
||||
if (registry.backends[name]) {
|
||||
console.error(`Shortcode [${name}] already registered. Please choose a different name.`);
|
||||
return;
|
||||
}
|
||||
registry.shortcodes[name] = config;
|
||||
registry.shortcodes[name] = config as unknown as ShortcodeConfig;
|
||||
}
|
||||
|
||||
export function getShortcode(name: string): ShortcodeConfig {
|
||||
|
@ -50,6 +50,8 @@ CMS.registerAdditionalLink({
|
||||
|
||||
### Custom Page
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```js
|
||||
const CustomPage = () => {
|
||||
return h('div', {}, 'I am a custom page!');
|
||||
@ -64,3 +66,37 @@ CMS.registerAdditionalLink({
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```jsx
|
||||
const CustomPage = () => {
|
||||
return <div>I am a custom page!</div>;
|
||||
};
|
||||
|
||||
CMS.registerAdditionalLink({
|
||||
id: 'custom-page',
|
||||
title: 'Custom Page',
|
||||
data: CustomPage,
|
||||
options: {
|
||||
icon: 'page',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```tsx
|
||||
import type { FC } from 'react';
|
||||
|
||||
const CustomPage: FC = () => {
|
||||
return <div>I am a custom page!</div>;
|
||||
};
|
||||
|
||||
CMS.registerAdditionalLink({
|
||||
id: 'custom-page',
|
||||
title: 'Custom Page',
|
||||
data: CustomPage,
|
||||
options: {
|
||||
icon: 'page',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
@ -19,6 +19,8 @@ Custom icons can be used with [Collections](/docs/collection-overview) or [Custo
|
||||
|
||||
This example uses Font Awesome to supply the icon.
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```js
|
||||
import { faHouse } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
@ -27,6 +29,24 @@ import CMS from '@staticcms/core';
|
||||
CMS.registerIcon('house', () => h(FontAwesomeIcon, {icon=faHouse, size="lg"}));
|
||||
```
|
||||
|
||||
```jsx
|
||||
import { faHouse } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import CMS from '@staticcms/core';
|
||||
|
||||
CMS.registerIcon('house', () => <FontAwesomeIcon icon={faHouse} size="lg" />);
|
||||
```
|
||||
|
||||
```tsx
|
||||
import { faHouse } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import CMS from '@staticcms/core';
|
||||
|
||||
CMS.registerIcon('house', () => <FontAwesomeIcon icon={faHouse} size="lg" />);
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
## Usage
|
||||
|
||||
### Collection
|
||||
|
@ -34,124 +34,278 @@ The following parameters will be passed to your `react_component` during render:
|
||||
|
||||
### Example
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/@staticcms/app@^1.0.0/dist/static-cms-app.js"></script>
|
||||
<script>
|
||||
const PostPreview = ({ widgetFor, getAsset, entry }) => {
|
||||
const [imageUrl, setImageUrl] = useState('');
|
||||
const image = useMemo(() => entry.data.image, [entry.data.image]);
|
||||
<CodeTabs>
|
||||
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
```js
|
||||
const PostPreview = ({ widgetFor, getAsset, entry, collection, field }) => {
|
||||
const imageUrl = useMediaAsset(entry.data.image, collection, field, entry);
|
||||
|
||||
const loadImage = async () => {
|
||||
const imageAsset = await getAsset(image);
|
||||
if (alive) {
|
||||
setImageUrl(imageAsset.toString());
|
||||
}
|
||||
};
|
||||
return h(
|
||||
'div',
|
||||
{},
|
||||
h('h1', {}, entry.data.title),
|
||||
h('img', { src: imageUrl }),
|
||||
h('div', { className: 'text' }, widgetFor('body')),
|
||||
);
|
||||
};
|
||||
|
||||
loadImage();
|
||||
|
||||
return () => {
|
||||
alive = false;
|
||||
};
|
||||
}, [image]);
|
||||
|
||||
return h(
|
||||
'div',
|
||||
{},
|
||||
h('h1', {}, entry.data.title),
|
||||
h('img', { src: imageUrl }),
|
||||
h('div', { classtitle: 'text' }, widgetFor('body')),
|
||||
);
|
||||
});
|
||||
|
||||
CMS.registerPreviewTemplate('posts', PostPreview);
|
||||
</script>
|
||||
CMS.registerPreviewTemplate('posts', PostPreview);
|
||||
```
|
||||
|
||||
```jsx
|
||||
import CMS, { useMediaAsset } from '@staticcms/core';
|
||||
|
||||
const PostPreview = ({ widgetFor, getAsset, entry, collection, field }) => {
|
||||
const imageUrl = useMediaAsset(entry.data.image, collection, field, entry);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{entry.data.title}</h1>
|
||||
<img src={imageUrl} />
|
||||
<div className='text'>{widgetFor('body')}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CMS.registerPreviewTemplate('posts', PostPreview);
|
||||
```
|
||||
|
||||
```tsx
|
||||
import CMS, { useMediaAsset } from '@staticcms/core';
|
||||
|
||||
import type { TemplatePreviewProps } from '@staticcms/core';
|
||||
|
||||
/**
|
||||
* The type for 'entry.data'
|
||||
*/
|
||||
interface Post {
|
||||
image: string;
|
||||
title: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
const PostPreview = ({ widgetFor, getAsset, entry, collection, field }: TemplatePreviewProps<Post>) => {
|
||||
const imageUrl = useMediaAsset(entry.data.image, collection, field, entry);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{entry.data.title}</h1>
|
||||
<img src={imageUrl} />
|
||||
<div className='text'>{widgetFor('body')}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CMS.registerPreviewTemplate('posts', PostPreview);
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
### Lists and Objects
|
||||
|
||||
The API for accessing the individual fields of list- and object-type entries is similar to the API for accessing fields in standard entries, but there are a few key differences. Access to these nested fields is facilitated through the `widgetsFor` function, which is passed to the preview template component during render.
|
||||
|
||||
**List Example:**
|
||||
#### List Template Example
|
||||
|
||||
```html
|
||||
<script>
|
||||
// For list fields, the widgetFor function returns an array of objects
|
||||
// that you can map over in your template. If our field is a list of
|
||||
// authors containing two entries, with fields `name` and `description`,
|
||||
// the return value of `widgetsFor` would look like this:
|
||||
//
|
||||
// [{
|
||||
// data: { title: 'Mathias', description: 'Co-Founder'},
|
||||
// widgets: { title: (<WidgetComponent>), description: (WidgetComponent>)}
|
||||
// },
|
||||
// {
|
||||
// data: { title: 'Chris', description: 'Co-Founder'},
|
||||
// widgets: { title: (<WidgetComponent>), description: (WidgetComponent>)}
|
||||
// }]
|
||||
//
|
||||
// Templating would look something like this:
|
||||
const AuthorsPreview = ({ widgetsFor }) => {
|
||||
return h(
|
||||
'div',
|
||||
{},
|
||||
For list fields, the widgetFor function returns an array of objects that you can map over in your template. If your field is a list of authors containing two entries, with fields `name` and `description`, the return value of `widgetsFor` would look like this:
|
||||
|
||||
// This is a static header that would only be rendered once for the entire list
|
||||
h('h1', {}, 'Authors'),
|
||||
|
||||
// Here we provide a simple mapping function that will be applied to each
|
||||
// object in the array of authors
|
||||
widgetsFor('authors').map(function (author, index) {
|
||||
return h(
|
||||
'div',
|
||||
{ key: index },
|
||||
h('hr', {}),
|
||||
h('strong', {}, author.data.name),
|
||||
author.widgets.description,
|
||||
);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
CMS.registerPreviewTemplate('authors', AuthorsPreview);
|
||||
</script>
|
||||
```js
|
||||
[{
|
||||
data: { title: 'Mathias', description: 'Co-Founder'},
|
||||
widgets: { title: <WidgetComponent>, description: <WidgetComponent>}
|
||||
},
|
||||
{
|
||||
data: { title: 'Chris', description: 'Co-Founder'},
|
||||
widgets: { title: <WidgetComponent>, description: <WidgetComponent>}
|
||||
}]
|
||||
```
|
||||
|
||||
**Object Example:**
|
||||
<CodeTabs>
|
||||
|
||||
```html
|
||||
<script>
|
||||
// Object fields are simpler than lists - instead of `widgetsFor` returning
|
||||
// an array of objects, it returns a single object. Accessing the shape of
|
||||
// that object is the same as the shape of objects returned for list fields:
|
||||
//
|
||||
// {
|
||||
// data: { front_limit: 0, author: 'Chris' },
|
||||
// widgets: { front_limit: (<WidgetComponent>), author: (WidgetComponent>)}
|
||||
// }
|
||||
const GeneralPreview = ({ entry, widgetsFor }) => {
|
||||
const title = entry.data.site_title;
|
||||
const posts = entry.data.posts;
|
||||
```js
|
||||
const AuthorsPreview = ({ widgetsFor }) => {
|
||||
return h(
|
||||
'div',
|
||||
{},
|
||||
|
||||
return h(
|
||||
'div',
|
||||
{},
|
||||
h('h1', {}, title),
|
||||
h(
|
||||
'dl',
|
||||
{},
|
||||
h('dt', {}, 'Posts on Frontpage'),
|
||||
h('dd', {}, widgetsFor('posts').widgets.front_limit || 0),
|
||||
// This is a static header that would only be rendered once for the entire list
|
||||
h('h1', {}, 'Authors'),
|
||||
|
||||
h('dt', {}, 'Default Author'),
|
||||
h('dd', {}, widgetsFor('posts').data.author || 'None'),
|
||||
),
|
||||
);
|
||||
};
|
||||
// Here we provide a simple mapping function that will be applied to each
|
||||
// object in the array of authors
|
||||
widgetsFor('authors').map(function (author, index) {
|
||||
return h(
|
||||
'div',
|
||||
{ key: index },
|
||||
h('hr', {}),
|
||||
h('strong', {}, author.data.name),
|
||||
author.widgets.description,
|
||||
);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
CMS.registerPreviewTemplate('general', GeneralPreview);
|
||||
</script>
|
||||
CMS.registerPreviewTemplate('authors', AuthorsPreview);
|
||||
```
|
||||
|
||||
```jsx
|
||||
import CMS from '@staticcms/core';
|
||||
|
||||
const AuthorsPreview = ({ widgetsFor }) => {
|
||||
return (
|
||||
<div>
|
||||
{/* This is a static header that would only be rendered once for the entire list */}
|
||||
<h1>Authors</h1>
|
||||
{/* Here we provide a simple mapping function that will be applied to each object in the array of authors */}
|
||||
{widgetsFor('authors').map((author, index) => (
|
||||
<div key={index}>
|
||||
<hr />
|
||||
<strong>{author.data.name}</strong>
|
||||
{author.widgets.description}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CMS.registerPreviewTemplate('authors', AuthorsPreview);
|
||||
```
|
||||
|
||||
```tsx
|
||||
import CMS from '@staticcms/core';
|
||||
|
||||
import type { TemplatePreviewProps } from '@staticcms/core';
|
||||
|
||||
interface Author {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type for 'entry.data'
|
||||
*/
|
||||
interface Authors {
|
||||
authors: Author[];
|
||||
}
|
||||
|
||||
const AuthorsPreview = ({ widgetsFor }: TemplatePreviewProps<Authors>) => {
|
||||
return (
|
||||
<div>
|
||||
{/* This is a static header that would only be rendered once for the entire list */}
|
||||
<h1>Authors</h1>
|
||||
{/* Here we provide a simple mapping function that will be applied to each object in the array of authors */}
|
||||
{widgetsFor('authors').map((author, index) => (
|
||||
<div key={index}>
|
||||
<hr />
|
||||
<strong>{author.data.name}</strong>
|
||||
{author.widgets.description}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CMS.registerPreviewTemplate('authors', AuthorsPreview);
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
#### Object Example
|
||||
|
||||
Object fields are simpler than lists - instead of `widgetsFor` returning an array of objects, it returns a single object. Accessing the shape of that object is the same as the shape of objects returned for list fields:
|
||||
|
||||
```js
|
||||
{
|
||||
data: { front_limit: 0, author: 'Chris' },
|
||||
widgets: { front_limit: <WidgetComponent>, author: <WidgetComponent>}
|
||||
}
|
||||
```
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```js
|
||||
const GeneralPreview = ({ entry, widgetsFor }) => {
|
||||
const title = entry.data.site_title;
|
||||
const posts = entry.data.posts;
|
||||
|
||||
return h(
|
||||
'div',
|
||||
{},
|
||||
h('h1', {}, title),
|
||||
h(
|
||||
'dl',
|
||||
{},
|
||||
h('dt', {}, 'Posts on Frontpage'),
|
||||
h('dd', {}, widgetsFor('posts').widgets.front_limit || 0),
|
||||
|
||||
h('dt', {}, 'Default Author'),
|
||||
h('dd', {}, widgetsFor('posts').data.author || 'None'),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
CMS.registerPreviewTemplate('general', GeneralPreview);
|
||||
```
|
||||
|
||||
```jsx
|
||||
import CMS from '@staticcms/core';
|
||||
|
||||
const GeneralPreview = ({ entry, widgetsFor }) => {
|
||||
const title = entry.data.site_title;
|
||||
const posts = entry.data.posts;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{title}</h1>
|
||||
<dl>
|
||||
<dt>Posts on Frontpage</dt>
|
||||
<dd>{widgetsFor('posts').widgets.front_limit || 0)}</dd>
|
||||
<dt>Default Author</dt>
|
||||
<dd>{widgetsFor('posts').data.author || 'None')}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CMS.registerPreviewTemplate('general', GeneralPreview);
|
||||
```
|
||||
|
||||
```tsx
|
||||
import CMS from '@staticcms/core';
|
||||
|
||||
import type { TemplatePreviewProps } from '@staticcms/core';
|
||||
|
||||
interface Posts {
|
||||
front_limit?: number;
|
||||
author?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type for 'entry.data'
|
||||
*/
|
||||
interface GeneralSettings {
|
||||
site_title: string;
|
||||
posts: Posts;
|
||||
}
|
||||
|
||||
const GeneralPreview = ({ entry, widgetsFor }: TemplatePreviewProps<GeneralSettings>) => {
|
||||
const title = entry.data.site_title;
|
||||
const posts = entry.data.posts;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{title}</h1>
|
||||
<dl>
|
||||
<dt>Posts on Frontpage</dt>
|
||||
<dd>{widgetsFor('posts').widgets.front_limit || 0)}</dd>
|
||||
<dt>Default Author</dt>
|
||||
<dd>{widgetsFor('posts').data.author || 'None')}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CMS.registerPreviewTemplate('general', GeneralPreview);
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
@ -24,6 +24,7 @@ CMS.registerWidget(name, control, [preview], [{ schema }]);
|
||||
|
||||
// Using npm module import
|
||||
import CMS from '@staticcms/core';
|
||||
|
||||
CMS.registerWidget(name, control, [preview], [{ schema }]);
|
||||
```
|
||||
|
||||
@ -120,49 +121,136 @@ Register widget takes an optional object of options. These options include:
|
||||
|
||||
### Example
|
||||
|
||||
`admin/index.html`
|
||||
<CodeTabs>
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/@staticcms/app@^1.0.0/dist/static-cms-app.js"></script>
|
||||
<script>
|
||||
const CategoriesControl = ({ label, value, field, onChange }) => {
|
||||
const separator = useMemo(() => field.separator ?? ', ', [field.separator]);
|
||||
```js
|
||||
const CategoriesControl = ({ label, value, field, onChange }) => {
|
||||
const separator = useMemo(() => field.separator ?? ', ', [field.separator]);
|
||||
|
||||
const handleChange = useCallback((e) => {
|
||||
onChange(e.target.value.split(separator).map(e => e.trim()));
|
||||
}, [separator, onChange]);
|
||||
const handleChange = useCallback((e) => {
|
||||
onChange(e.target.value.split(separator).map(e => e.trim()));
|
||||
}, [separator, onChange]);
|
||||
|
||||
return h('div', {}, {
|
||||
h('label', { for: 'inputId' }, label),
|
||||
h('input', {
|
||||
id: 'inputId',
|
||||
type: 'text',
|
||||
value: value ? value.join(separator) : '',
|
||||
onChange: this.handleChange,
|
||||
})
|
||||
});
|
||||
};
|
||||
return h('div', {}, {
|
||||
h('label', { for: 'inputId' }, label),
|
||||
h('input', {
|
||||
id: 'inputId',
|
||||
type: 'text',
|
||||
value: value ? value.join(separator) : '',
|
||||
onChange: handleChange,
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
const CategoriesPreview = ({ value }) => {
|
||||
return h(
|
||||
'ul',
|
||||
{},
|
||||
value.map(function (val, index) {
|
||||
return h('li', { key: index }, val);
|
||||
}),
|
||||
);
|
||||
};
|
||||
const CategoriesPreview = ({ value }) => {
|
||||
return h(
|
||||
'ul',
|
||||
{},
|
||||
value.map((val, index) => {
|
||||
return h('li', { key: index }, val);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const schema = {
|
||||
properties: {
|
||||
separator: { type: 'string' },
|
||||
},
|
||||
};
|
||||
const schema = {
|
||||
properties: {
|
||||
separator: { type: 'string' },
|
||||
},
|
||||
};
|
||||
|
||||
CMS.registerWidget('categories', CategoriesControl, CategoriesPreview, { schema });
|
||||
</script>
|
||||
CMS.registerWidget('categories', CategoriesControl, CategoriesPreview, { schema });
|
||||
```
|
||||
|
||||
```jsx
|
||||
import CMS from '@staticcms/core';
|
||||
|
||||
const CategoriesControl = ({ label, value, field, onChange }) => {
|
||||
const separator = useMemo(() => field.separator ?? ', ', [field.separator]);
|
||||
|
||||
const handleChange = useCallback((e) => {
|
||||
onChange(e.target.value.split(separator).map(e => e.trim()));
|
||||
}, [separator, onChange]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label for="inputId">{label}</label>
|
||||
<input
|
||||
id="inputId"
|
||||
type="text"
|
||||
value={value ? value.join(separator) : ''}
|
||||
onChange={handleChange} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CategoriesPreview = ({ value }) => {
|
||||
return (
|
||||
<ul>
|
||||
{value.map((val, index) => {
|
||||
return <li key={index}>{value}</li>;
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
const schema = {
|
||||
properties: {
|
||||
separator: { type: 'string' },
|
||||
},
|
||||
};
|
||||
|
||||
CMS.registerWidget('categories', CategoriesControl, CategoriesPreview, { schema });
|
||||
```
|
||||
|
||||
```tsx
|
||||
import CMS from '@staticcms/core';
|
||||
|
||||
import type { WidgetControlProps, WidgetPreviewProps } from '@staticcms/core';
|
||||
|
||||
interface CategoriesField {
|
||||
widget: 'categories'
|
||||
}
|
||||
|
||||
const CategoriesControl = ({ label, value, field, onChange }: WidgetControlProps<string[], CategoriesField>) => {
|
||||
const separator = useMemo(() => field.separator ?? ', ', [field.separator]);
|
||||
|
||||
const handleChange = useCallback((e) => {
|
||||
onChange(e.target.value.split(separator).map(e => e.trim()));
|
||||
}, [separator, onChange]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label for="inputId">{label}</label>
|
||||
<input
|
||||
id="inputId"
|
||||
type="text"
|
||||
value={value ? value.join(separator) : ''}
|
||||
onChange={handleChange} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CategoriesPreview = ({ value }: WidgetPreviewProps<string[], CategoriesField>) => {
|
||||
return (
|
||||
<ul>
|
||||
{value.map((val, index) => {
|
||||
return <li key={index}>{value}</li>;
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
const schema = {
|
||||
properties: {
|
||||
separator: { type: 'string' },
|
||||
},
|
||||
};
|
||||
|
||||
CMS.registerWidget('categories', CategoriesControl, CategoriesPreview, { schema });
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
`admin/config.yml` (or `admin/config.js`)
|
||||
|
||||
<CodeTabs>
|
||||
@ -272,48 +360,61 @@ If you want to use the media library in your custom widget you will need to use
|
||||
<CodeTabs>
|
||||
|
||||
```js
|
||||
const FileControl = ({ collection, field, value, entry, onChange }) => {
|
||||
const handleOpenMediaLibrary = useMediaInsert(value, { field, controlID }, onChange);
|
||||
|
||||
const assetSource = useMediaAsset(value, 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 FileControl = ({ collection, field, value, entry, onChange }) => {
|
||||
const handleOpenMediaLibrary = useMediaInsert(
|
||||
value,
|
||||
{ field, controlID },
|
||||
onChange,
|
||||
);
|
||||
const handleOpenMediaLibrary = useMediaInsert(value, { field, controlID }, onChange);
|
||||
|
||||
const assetSource = useMediaAsset(value, collection, field, entry);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button type="button" onClick={handleOpenMediaLibrary}>Upload</button>
|
||||
<button type="button" onClick={handleOpenMediaLibrary}>
|
||||
Upload
|
||||
</button>
|
||||
<img role="presentation" src={assetSource} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```ts
|
||||
```tsx
|
||||
import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset';
|
||||
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';
|
||||
|
||||
import type { WidgetControlProps } from '@staticcms/core/interface';
|
||||
import type { FC } from 'react';
|
||||
|
||||
const FileControl: FC<WidgetControlProps<string, MyField>> =
|
||||
({ collection, field, value, entry, onChange }) => {
|
||||
|
||||
const handleOpenMediaLibrary = useMediaInsert(
|
||||
internalValue,
|
||||
{ field, controlID },
|
||||
onChange,
|
||||
);
|
||||
const FileControl: FC<WidgetControlProps<string, MyField>> = ({
|
||||
collection,
|
||||
field,
|
||||
value,
|
||||
entry,
|
||||
onChange,
|
||||
}) => {
|
||||
const handleOpenMediaLibrary = useMediaInsert(internalValue, { field, controlID }, onChange);
|
||||
|
||||
const assetSource = useMediaAsset(value, collection, field, entry);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button type="button" onClick={handleOpenMediaLibrary}>Upload</button>
|
||||
<button type="button" onClick={handleOpenMediaLibrary}>
|
||||
Upload
|
||||
</button>
|
||||
<img role="presentation" src={assetSource} />
|
||||
</>
|
||||
);
|
||||
|
@ -60,6 +60,10 @@ Shortcodes can be added to customize the Markdown editor via `registerShortcode`
|
||||
|
||||
### Usage
|
||||
|
||||
```markdown
|
||||
[youtube|p6h-rYSVX90]
|
||||
```
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```js
|
||||
@ -117,8 +121,110 @@ CMS.registerShortcode('youtube', {
|
||||
});
|
||||
```
|
||||
|
||||
```markdown
|
||||
[youtube|p6h-rYSVX90]
|
||||
```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>
|
||||
|
@ -55,6 +55,11 @@ const supportedLanguages: Record<string, CodeLanguage> = {
|
||||
grammar: Prism.languages.javascript,
|
||||
language: 'javascript',
|
||||
},
|
||||
'language-jsx': {
|
||||
title: 'JSX',
|
||||
grammar: Prism.languages.jsx,
|
||||
language: 'javascript',
|
||||
},
|
||||
'language-markdown': {
|
||||
title: 'Markdown',
|
||||
grammar: Prism.languages.markdown,
|
||||
@ -65,6 +70,11 @@ const supportedLanguages: Record<string, CodeLanguage> = {
|
||||
grammar: Prism.languages.typescript,
|
||||
language: 'typescript',
|
||||
},
|
||||
'language-tsx': {
|
||||
title: 'Typescript',
|
||||
grammar: Prism.languages.tsx,
|
||||
language: 'typescript',
|
||||
},
|
||||
};
|
||||
|
||||
interface TabData {
|
||||
|
Loading…
x
Reference in New Issue
Block a user