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
|
* Markdown editor shortcodes
|
||||||
*/
|
*/
|
||||||
export function registerShortcode(name: string, config: ShortcodeConfig) {
|
export function registerShortcode<P = {}>(name: string, config: ShortcodeConfig<P>) {
|
||||||
if (registry.backends[name]) {
|
if (registry.backends[name]) {
|
||||||
console.error(`Shortcode [${name}] already registered. Please choose a different name.`);
|
console.error(`Shortcode [${name}] already registered. Please choose a different name.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
registry.shortcodes[name] = config;
|
registry.shortcodes[name] = config as unknown as ShortcodeConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getShortcode(name: string): ShortcodeConfig {
|
export function getShortcode(name: string): ShortcodeConfig {
|
||||||
|
@ -50,6 +50,8 @@ CMS.registerAdditionalLink({
|
|||||||
|
|
||||||
### Custom Page
|
### Custom Page
|
||||||
|
|
||||||
|
<CodeTabs>
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const CustomPage = () => {
|
const CustomPage = () => {
|
||||||
return h('div', {}, 'I am a custom page!');
|
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.
|
This example uses Font Awesome to supply the icon.
|
||||||
|
|
||||||
|
<CodeTabs>
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { faHouse } from '@fortawesome/free-solid-svg-icons';
|
import { faHouse } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
@ -27,6 +29,24 @@ import CMS from '@staticcms/core';
|
|||||||
CMS.registerIcon('house', () => h(FontAwesomeIcon, {icon=faHouse, size="lg"}));
|
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
|
## Usage
|
||||||
|
|
||||||
### Collection
|
### Collection
|
||||||
|
@ -34,67 +34,96 @@ The following parameters will be passed to your `react_component` during render:
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
```html
|
<CodeTabs>
|
||||||
<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]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
```js
|
||||||
let alive = true;
|
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());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loadImage();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
alive = false;
|
|
||||||
};
|
|
||||||
}, [image]);
|
|
||||||
|
|
||||||
return h(
|
return h(
|
||||||
'div',
|
'div',
|
||||||
{},
|
{},
|
||||||
h('h1', {}, entry.data.title),
|
h('h1', {}, entry.data.title),
|
||||||
h('img', { src: imageUrl }),
|
h('img', { src: imageUrl }),
|
||||||
h('div', { classtitle: 'text' }, widgetFor('body')),
|
h('div', { className: 'text' }, widgetFor('body')),
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|
||||||
CMS.registerPreviewTemplate('posts', PostPreview);
|
CMS.registerPreviewTemplate('posts', PostPreview);
|
||||||
</script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```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
|
### 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.
|
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
|
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:
|
||||||
<script>
|
|
||||||
// For list fields, the widgetFor function returns an array of objects
|
```js
|
||||||
// that you can map over in your template. If our field is a list of
|
[{
|
||||||
// authors containing two entries, with fields `name` and `description`,
|
data: { title: 'Mathias', description: 'Co-Founder'},
|
||||||
// the return value of `widgetsFor` would look like this:
|
widgets: { title: <WidgetComponent>, description: <WidgetComponent>}
|
||||||
//
|
},
|
||||||
// [{
|
{
|
||||||
// data: { title: 'Mathias', description: 'Co-Founder'},
|
data: { title: 'Chris', description: 'Co-Founder'},
|
||||||
// widgets: { title: (<WidgetComponent>), description: (WidgetComponent>)}
|
widgets: { title: <WidgetComponent>, description: <WidgetComponent>}
|
||||||
// },
|
}]
|
||||||
// {
|
```
|
||||||
// data: { title: 'Chris', description: 'Co-Founder'},
|
|
||||||
// widgets: { title: (<WidgetComponent>), description: (WidgetComponent>)}
|
<CodeTabs>
|
||||||
// }]
|
|
||||||
//
|
```js
|
||||||
// Templating would look something like this:
|
const AuthorsPreview = ({ widgetsFor }) => {
|
||||||
const AuthorsPreview = ({ widgetsFor }) => {
|
|
||||||
return h(
|
return h(
|
||||||
'div',
|
'div',
|
||||||
{},
|
{},
|
||||||
@ -114,25 +143,88 @@ The API for accessing the individual fields of list- and object-type entries is
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
CMS.registerPreviewTemplate('authors', AuthorsPreview);
|
CMS.registerPreviewTemplate('authors', AuthorsPreview);
|
||||||
</script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Object Example:**
|
```jsx
|
||||||
|
import CMS from '@staticcms/core';
|
||||||
|
|
||||||
```html
|
const AuthorsPreview = ({ widgetsFor }) => {
|
||||||
<script>
|
return (
|
||||||
// Object fields are simpler than lists - instead of `widgetsFor` returning
|
<div>
|
||||||
// an array of objects, it returns a single object. Accessing the shape of
|
{/* This is a static header that would only be rendered once for the entire list */}
|
||||||
// that object is the same as the shape of objects returned for list fields:
|
<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) => (
|
||||||
// data: { front_limit: 0, author: 'Chris' },
|
<div key={index}>
|
||||||
// widgets: { front_limit: (<WidgetComponent>), author: (WidgetComponent>)}
|
<hr />
|
||||||
// }
|
<strong>{author.data.name}</strong>
|
||||||
const GeneralPreview = ({ entry, widgetsFor }) => {
|
{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 title = entry.data.site_title;
|
||||||
const posts = entry.data.posts;
|
const posts = entry.data.posts;
|
||||||
|
|
||||||
@ -150,8 +242,70 @@ The API for accessing the individual fields of list- and object-type entries is
|
|||||||
h('dd', {}, widgetsFor('posts').data.author || 'None'),
|
h('dd', {}, widgetsFor('posts').data.author || 'None'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
CMS.registerPreviewTemplate('general', GeneralPreview);
|
CMS.registerPreviewTemplate('general', GeneralPreview);
|
||||||
</script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```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
|
// Using npm module import
|
||||||
import CMS from '@staticcms/core';
|
import CMS from '@staticcms/core';
|
||||||
|
|
||||||
CMS.registerWidget(name, control, [preview], [{ schema }]);
|
CMS.registerWidget(name, control, [preview], [{ schema }]);
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -120,12 +121,10 @@ Register widget takes an optional object of options. These options include:
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
`admin/index.html`
|
<CodeTabs>
|
||||||
|
|
||||||
```html
|
```js
|
||||||
<script src="https://unpkg.com/@staticcms/app@^1.0.0/dist/static-cms-app.js"></script>
|
const CategoriesControl = ({ label, value, field, onChange }) => {
|
||||||
<script>
|
|
||||||
const CategoriesControl = ({ label, value, field, onChange }) => {
|
|
||||||
const separator = useMemo(() => field.separator ?? ', ', [field.separator]);
|
const separator = useMemo(() => field.separator ?? ', ', [field.separator]);
|
||||||
|
|
||||||
const handleChange = useCallback((e) => {
|
const handleChange = useCallback((e) => {
|
||||||
@ -138,31 +137,120 @@ Register widget takes an optional object of options. These options include:
|
|||||||
id: 'inputId',
|
id: 'inputId',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: value ? value.join(separator) : '',
|
value: value ? value.join(separator) : '',
|
||||||
onChange: this.handleChange,
|
onChange: handleChange,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const CategoriesPreview = ({ value }) => {
|
const CategoriesPreview = ({ value }) => {
|
||||||
return h(
|
return h(
|
||||||
'ul',
|
'ul',
|
||||||
{},
|
{},
|
||||||
value.map(function (val, index) {
|
value.map((val, index) => {
|
||||||
return h('li', { key: index }, val);
|
return h('li', { key: index }, val);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
properties: {
|
properties: {
|
||||||
separator: { type: 'string' },
|
separator: { type: 'string' },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
CMS.registerWidget('categories', CategoriesControl, CategoriesPreview, { schema });
|
CMS.registerWidget('categories', CategoriesControl, CategoriesPreview, { schema });
|
||||||
</script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```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`)
|
`admin/config.yml` (or `admin/config.js`)
|
||||||
|
|
||||||
<CodeTabs>
|
<CodeTabs>
|
||||||
@ -272,48 +360,61 @@ If you want to use the media library in your custom widget you will need to use
|
|||||||
<CodeTabs>
|
<CodeTabs>
|
||||||
|
|
||||||
```js
|
```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 useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset';
|
||||||
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';
|
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';
|
||||||
|
|
||||||
const FileControl = ({ collection, field, value, entry, onChange }) => {
|
const FileControl = ({ collection, field, value, entry, onChange }) => {
|
||||||
const handleOpenMediaLibrary = useMediaInsert(
|
const handleOpenMediaLibrary = useMediaInsert(value, { field, controlID }, onChange);
|
||||||
value,
|
|
||||||
{ field, controlID },
|
|
||||||
onChange,
|
|
||||||
);
|
|
||||||
|
|
||||||
const assetSource = useMediaAsset(value, collection, field, entry);
|
const assetSource = useMediaAsset(value, collection, field, entry);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button type="button" onClick={handleOpenMediaLibrary}>Upload</button>
|
<button type="button" onClick={handleOpenMediaLibrary}>
|
||||||
|
Upload
|
||||||
|
</button>
|
||||||
<img role="presentation" src={assetSource} />
|
<img role="presentation" src={assetSource} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
```ts
|
```tsx
|
||||||
import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset';
|
import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset';
|
||||||
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';
|
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';
|
||||||
|
|
||||||
import type { WidgetControlProps } from '@staticcms/core/interface';
|
import type { WidgetControlProps } from '@staticcms/core/interface';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
const FileControl: FC<WidgetControlProps<string, MyField>> =
|
const FileControl: FC<WidgetControlProps<string, MyField>> = ({
|
||||||
({ collection, field, value, entry, onChange }) => {
|
collection,
|
||||||
|
field,
|
||||||
const handleOpenMediaLibrary = useMediaInsert(
|
value,
|
||||||
internalValue,
|
entry,
|
||||||
{ field, controlID },
|
|
||||||
onChange,
|
onChange,
|
||||||
);
|
}) => {
|
||||||
|
const handleOpenMediaLibrary = useMediaInsert(internalValue, { field, controlID }, onChange);
|
||||||
|
|
||||||
const assetSource = useMediaAsset(value, collection, field, entry);
|
const assetSource = useMediaAsset(value, collection, field, entry);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button type="button" onClick={handleOpenMediaLibrary}>Upload</button>
|
<button type="button" onClick={handleOpenMediaLibrary}>
|
||||||
|
Upload
|
||||||
|
</button>
|
||||||
<img role="presentation" src={assetSource} />
|
<img role="presentation" src={assetSource} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -60,6 +60,10 @@ Shortcodes can be added to customize the Markdown editor via `registerShortcode`
|
|||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
[youtube|p6h-rYSVX90]
|
||||||
|
```
|
||||||
|
|
||||||
<CodeTabs>
|
<CodeTabs>
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@ -117,8 +121,110 @@ CMS.registerShortcode('youtube', {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
```markdown
|
```jsx
|
||||||
[youtube|p6h-rYSVX90]
|
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>
|
</CodeTabs>
|
||||||
|
@ -55,6 +55,11 @@ const supportedLanguages: Record<string, CodeLanguage> = {
|
|||||||
grammar: Prism.languages.javascript,
|
grammar: Prism.languages.javascript,
|
||||||
language: 'javascript',
|
language: 'javascript',
|
||||||
},
|
},
|
||||||
|
'language-jsx': {
|
||||||
|
title: 'JSX',
|
||||||
|
grammar: Prism.languages.jsx,
|
||||||
|
language: 'javascript',
|
||||||
|
},
|
||||||
'language-markdown': {
|
'language-markdown': {
|
||||||
title: 'Markdown',
|
title: 'Markdown',
|
||||||
grammar: Prism.languages.markdown,
|
grammar: Prism.languages.markdown,
|
||||||
@ -65,6 +70,11 @@ const supportedLanguages: Record<string, CodeLanguage> = {
|
|||||||
grammar: Prism.languages.typescript,
|
grammar: Prism.languages.typescript,
|
||||||
language: 'typescript',
|
language: 'typescript',
|
||||||
},
|
},
|
||||||
|
'language-tsx': {
|
||||||
|
title: 'Typescript',
|
||||||
|
grammar: Prism.languages.tsx,
|
||||||
|
language: 'typescript',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
interface TabData {
|
interface TabData {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user