feat: v4.0.0 (#1016)

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -24,27 +24,6 @@ const PostDateFieldPreview = ({ value }) => {
);
};
const PostDraftFieldPreview = ({ value }) => {
return h(
'div',
{
style: {
backgroundColor: value === true ? 'rgb(37 99 235)' : 'rgb(22 163 74)',
color: 'white',
border: 'none',
padding: '2px 6px',
textAlign: 'center',
textDecoration: 'none',
display: 'inline-block',
cursor: 'pointer',
borderRadius: '4px',
fontSize: '14px',
},
},
value === true ? 'Draft' : 'Published',
);
};
const GeneralPreview = ({ widgetsFor, entry, collection }) => {
const title = entry.data.site_title;
const posts = entry.data.posts;
@ -88,38 +67,16 @@ const AuthorsPreview = ({ widgetsFor }) => {
);
};
const RelationKitchenSinkPostPreview = ({ fieldsMetaData }) => {
// When a post is selected from the relation field, all of it's data
// will be available in the field's metadata nested under the collection
// name, and then further nested under the value specified in `value_field`.
// In this case, the post would be nested under "posts" and then under
// the title of the selected post, since our `value_field` in the config
// is "title".
const post = fieldsMetaData && fieldsMetaData.posts.value;
const style = { border: '2px solid #ccc', borderRadius: '8px', padding: '20px' };
return post
? h(
'div',
{ style: style },
h('h2', {}, 'Related Post'),
h('h3', {}, post.title),
h('img', { src: post.image }),
h('p', {}, (post.body ?? '').slice(0, 100) + '...'),
)
: null;
};
const CustomPage = () => {
return h('div', {}, 'I am a custom page!');
};
CMS.registerPreviewTemplate('posts', PostPreview);
CMS.registerFieldPreview('posts', 'date', PostDateFieldPreview);
CMS.registerFieldPreview('posts', 'draft', PostDraftFieldPreview);
CMS.registerPreviewTemplate('general', GeneralPreview);
CMS.registerPreviewTemplate('authors', AuthorsPreview);
// Pass the name of a registered control to reuse with a new widget preview.
CMS.registerWidget('relationKitchenSinkPost', 'relation', RelationKitchenSinkPostPreview);
CMS.registerWidget('relationKitchenSinkPost', 'relation');
CMS.registerAdditionalLink({
id: 'example',
title: 'Example.com',
@ -152,7 +109,9 @@ CMS.registerShortcode('youtube', {
toArgs: ({ src }) => {
return [src];
},
control: ({ src, onChange, theme }) => {
control: ({ src, onChange }) => {
const theme = useTheme();
return h('span', {}, [
h('input', {
key: 'control-input',
@ -162,8 +121,8 @@ CMS.registerShortcode('youtube', {
},
style: {
width: '100%',
backgroundColor: theme === 'dark' ? 'rgb(30, 41, 59)' : 'white',
color: theme === 'dark' ? 'white' : 'black',
backgroundColor: theme.common.gray,
color: theme.text.primary,
padding: '4px 8px',
},
}),

View File

@ -1,6 +1,5 @@
---
title: Something something something2...
draft: false
date: 2022-11-01 06:30
image: static-cms-icon.svg
---

View File

@ -1,6 +1,5 @@
---
title: Test
draft: false
date: 2022-11-01 14:28
image: kittens.jpg
---

View File

@ -1,6 +1,5 @@
---
title: Test3
draft: false
date: 2022-11-02 08:43
image: ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg
---

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Static CMS Development Test</title>
</head>
<body>
<script src="/static-cms-core.js"></script>
<script src="/data.js"></script>
<script type="module" src="/index.js"></script>
</body>
</html>

View File

@ -1,8 +1,7 @@
backend:
name: test-repo
site_url: 'https://example.com'
media_folder: assets/uploads
public_folder: /assets/uploads
media_folder: /assets/uploads
media_library:
folder_support: true
locale: en
@ -18,7 +17,7 @@ i18n:
# Optional, defaults to the first item in locales.
# The locale to be used for fields validation and as a baseline for the entry.
defaultLocale: en
default_locale: en
collections:
- name: posts
label: Posts
@ -31,7 +30,6 @@ collections:
summary_fields:
- title
- date
- draft
sortable_fields:
fields:
- title
@ -40,28 +38,35 @@ collections:
field: title
create: true
view_filters:
- label: Posts With Index
field: title
pattern: 'This is post #'
- label: Posts Without Index
field: title
pattern: front matter post
- label: Drafts
field: draft
pattern: true
filters:
- name: posts-with-index
label: Posts With Index
field: title
pattern: 'This is post #'
- name: posts-without-index
label: Posts Without Index
field: title
pattern: front matter post
- name: draft
label: Drafts
field: draft
pattern: true
view_groups:
- label: Year
field: date
pattern: '\d{4}'
- label: Drafts
field: draft
groups:
- name: by-year
label: Year
field: date
pattern: '\d{4}'
- name: draft
label: Drafts
field: draft
fields:
- label: Title
name: title
widget: string
- label: Draft
name: draft
widget: boolean
- label: 'Draft'
name: 'draft'
widget: 'boolean'
default: false
- label: Publish Date
name: date
@ -73,10 +78,19 @@ collections:
name: image
widget: image
required: false
- label: Description
name: description
widget: text
- label: Category
name: category
widget: string
- label: Body
name: body
widget: markdown
hint: "*Main* __content__ __*goes*__ [here](https://example.com/)."
hint: '*Main* __content__ __*goes*__ [here](https://example.com/).'
- label: Tags
name: tags
widget: list
- name: faq
label: FAQ
folder: _faqs
@ -150,6 +164,19 @@ collections:
widget: boolean
pattern: ['true', 'Must be true']
required: false
- name: prefix
label: With Prefix
widget: boolean
prefix: "I'm a prefix"
- name: suffix
label: With Suffix
widget: boolean
suffix: "I'm a suffix"
- name: prefix_and_suffix
label: With Prefix and Suffix
widget: boolean
prefix: "I'm a prefix"
suffix: "I'm a suffix"
- name: code
label: Code
file: _widgets/code.json
@ -571,6 +598,7 @@ collections:
- label: Type 2 Object
name: type_2_object
widget: object
summary: "{{datetime | date('yyyy-MM-dd')}}"
fields:
- label: Number
name: number
@ -778,6 +806,19 @@ collections:
widget: number
pattern: ['[0-9]{3,}', 'Must be at least 3 digits']
required: false
- name: prefix
label: With Prefix
widget: number
prefix: '$'
- name: suffix
label: With Suffix
widget: number
suffix: '%'
- name: prefix_and_suffix
label: With Prefix and Suffix
widget: number
prefix: '$'
suffix: '%'
- name: object
label: Object
file: _widgets/object.json
@ -1055,6 +1096,19 @@ collections:
widget: string
pattern: ['.{12,}', 'Must have at least 12 characters']
required: false
- name: prefix
label: With Prefix
widget: string
prefix: '$'
- name: suffix
label: With Suffix
widget: string
suffix: '%'
- name: prefix_and_suffix
label: With Prefix and Suffix
widget: string
prefix: '$'
suffix: '%'
- name: text
label: Text
file: _widgets/text.json
@ -1099,11 +1153,6 @@ collections:
file: _data/settings.json
description: General Site Settings
fields:
- label: Number of posts on frontpage
name: front_limit
widget: number
min: 1
max: 10
- label: Global title
name: site_title
widget: string
@ -1142,6 +1191,34 @@ collections:
- label: Description
name: description
widget: text
- name: hotels
label: Hotel Locations
file: _data/hotel_locations.yml
fields:
- name: country
label: Country
widget: string
- name: hotel_locations
label: Hotel Locations
widget: list
fields:
- name: cities
label: Cities
widget: list
fields:
- name: city
label: City
widget: string
- name: number_of_hotels_in_city
label: Number of Hotels in City
widget: number
- name: city_locations
label: City Locations
widget: list
fields:
- name: hotel_name
label: Hotel Name
widget: string
- name: kitchenSink
label: Kitchen Sink
folder: _sink

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -11,134 +11,6 @@ const PostPreview = ({ entry, widgetFor }) => {
);
};
const PostPreviewCard = ({ entry, theme, hasLocalBackup, collection }) => {
const date = new Date(entry.data.date);
const month = date.getMonth() + 1;
const day = date.getDate();
const imageField = useMemo(() => collection.fields.find((f) => f.name === 'image'), []);
const image = useMediaAsset(entry.data.image, collection, imageField, entry);
return h(
'div',
{ style: { width: '100%' } },
h('div', {
style: {
width: '100%',
borderTopLeftRadius: '8px',
borderTopRightRadius: '8px',
overflow: 'hidden',
height: '140px',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
objectFit: 'cover',
backgroundImage: `url('${image}')`,
},
}),
h(
'div',
{ style: { padding: '16px', width: '100%' } },
h(
'div',
{
style: {
display: 'flex',
width: '100%',
justifyContent: 'space-between',
alignItems: 'start',
gap: '4px',
color: theme === 'dark' ? 'white' : 'inherit',
},
},
h(
'div',
{
style: {
display: 'flex',
flexDirection: 'column',
alignItems: 'baseline',
gap: '4px',
},
},
h(
'div',
{
style: {
fontSize: '14px',
fontWeight: 700,
color: 'rgb(107, 114, 128)',
lineHeight: '18px',
},
},
entry.data.title,
),
h(
'span',
{ style: { fontSize: '14px' } },
`${date.getFullYear()}-${month < 10 ? `0${month}` : month}-${
day < 10 ? `0${day}` : day
}`,
),
),
h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
whiteSpace: 'no-wrap',
gap: '8px',
},
},
hasLocalBackup
? h(
'div',
{
style: {
border: '2px solid rgb(147, 197, 253)',
borderRadius: '50%',
color: 'rgb(147, 197, 253)',
height: '18px',
width: '18px',
fontWeight: 'bold',
fontSize: '11px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center',
},
title: 'Has local backup',
},
'i',
)
: null,
h(
'div',
{
style: {
backgroundColor:
entry.data.draft === true ? 'rgb(37, 99, 235)' : 'rgb(22, 163, 74)',
color: 'white',
border: 'none',
padding: '2px 6px',
textAlign: 'center',
textDecoration: 'none',
display: 'inline-block',
cursor: 'pointer',
borderRadius: '4px',
fontSize: '14px',
},
},
entry.data.draft === true ? 'Draft' : 'Published',
),
),
),
),
);
};
const PostDateFieldPreview = ({ value }) => {
const date = new Date(value);
@ -152,29 +24,6 @@ const PostDateFieldPreview = ({ value }) => {
);
};
const PostDraftFieldPreview = ({ value }) => {
return h(
'div',
{
style: {
backgroundColor: value === true ? 'rgb(37 99 235)' : 'rgb(22 163 74)',
color: 'white',
border: 'none',
padding: '2px 6px',
textAlign: 'center',
textDecoration: 'none',
display: 'inline-block',
cursor: 'pointer',
borderRadius: '4px',
fontSize: '14px',
lineHeight: '16px',
height: '20px',
},
},
value === true ? 'Draft' : 'Published',
);
};
const GeneralPreview = ({ widgetsFor, entry, collection }) => {
const title = entry.data.site_title;
const posts = entry.data.posts;
@ -218,39 +67,16 @@ const AuthorsPreview = ({ widgetsFor }) => {
);
};
const RelationKitchenSinkPostPreview = ({ fieldsMetaData }) => {
// When a post is selected from the relation field, all of it's data
// will be available in the field's metadata nested under the collection
// name, and then further nested under the value specified in `value_field`.
// In this case, the post would be nested under "posts" and then under
// the title of the selected post, since our `value_field` in the config
// is "title".
const post = fieldsMetaData && fieldsMetaData.posts.value;
const style = { border: '2px solid #ccc', borderRadius: '8px', padding: '20px' };
return post
? h(
'div',
{ style: style },
h('h2', {}, 'Related Post'),
h('h3', {}, post.title),
h('img', { src: post.image }),
h('p', {}, (post.body ?? '').slice(0, 100) + '...'),
)
: null;
};
const CustomPage = () => {
return h('div', {}, 'I am a custom page!');
};
CMS.registerPreviewTemplate('posts', PostPreview);
CMS.registerPreviewCard('posts', PostPreviewCard, () => 240);
CMS.registerFieldPreview('posts', 'date', PostDateFieldPreview);
CMS.registerFieldPreview('posts', 'draft', PostDraftFieldPreview);
CMS.registerPreviewTemplate('general', GeneralPreview);
CMS.registerPreviewTemplate('authors', AuthorsPreview);
// Pass the name of a registered control to reuse with a new widget preview.
CMS.registerWidget('relationKitchenSinkPost', 'relation', RelationKitchenSinkPostPreview);
CMS.registerWidget('relationKitchenSinkPost', 'relation');
CMS.registerAdditionalLink({
id: 'example',
title: 'Example.com',
@ -268,6 +94,14 @@ CMS.registerAdditionalLink({
},
});
CMS.registerTheme({
name: 'Custom Red Orange',
extends: 'dark',
primary: {
main: '#ff4500',
}
});
CMS.registerShortcode('youtube', {
label: 'YouTube',
openTag: '[',
@ -283,7 +117,9 @@ CMS.registerShortcode('youtube', {
toArgs: ({ src }) => {
return [src];
},
control: ({ src, onChange, theme }) => {
control: ({ src, onChange }) => {
const theme = useTheme();
return h('span', {}, [
h('input', {
key: 'control-input',
@ -293,8 +129,8 @@ CMS.registerShortcode('youtube', {
},
style: {
width: '100%',
backgroundColor: theme === 'dark' ? 'rgb(30, 41, 59)' : 'white',
color: theme === 'dark' ? 'white' : 'black',
backgroundColor: theme.common.gray,
color: theme.text.primary,
padding: '4px 8px',
},
}),