diff --git a/packages/app/webpack.config.js b/packages/app/webpack.config.js
index 03aa4296..0eadba40 100644
--- a/packages/app/webpack.config.js
+++ b/packages/app/webpack.config.js
@@ -94,18 +94,10 @@ module.exports = {
].filter(Boolean),
output: {
path: path.resolve(__dirname, 'dist'),
- filename: 'static-cms-core.js',
+ filename: 'static-cms-app.js',
library: {
- name: 'StaticCmsCore',
+ name: 'StaticCmsApp',
type: 'umd',
},
- },
- devServer: {
- static: {
- directory: './dev-test',
- },
- host: '0.0.0.0',
- port: devServerPort,
- hot: true,
- },
+ }
};
diff --git a/packages/demo/config.yml b/packages/demo/config.yml
new file mode 100644
index 00000000..58e214ad
--- /dev/null
+++ b/packages/demo/config.yml
@@ -0,0 +1,1162 @@
+backend:
+ name: demo-repo
+site_url: 'https://staticjscms.netlify.app/'
+media_folder: assets/uploads
+locale: en
+collections:
+ - name: posts
+ label: Posts
+ label_singular: Post
+ description: >
+ The description is a great place for tone setting, high level information,
+ and editing guidelines that are specific to a collection.
+ folder: _posts
+ slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
+ summary: '{{title}} -- {{year}}/{{month}}/{{day}}'
+ sortable_fields:
+ fields:
+ - title
+ - date
+ default:
+ 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
+ view_groups:
+ - label: Year
+ field: date
+ pattern: '\d{4}'
+ - label: Drafts
+ field: draft
+ fields:
+ - label: Title
+ name: title
+ widget: string
+ - label: Draft
+ name: draft
+ widget: boolean
+ default: false
+ - label: Publish Date
+ name: date
+ widget: datetime
+ date_format: yyyy-MM-dd
+ time_format: 'HH:mm'
+ format: "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
+ - label: Cover Image
+ name: image
+ widget: image
+ required: false
+ - label: Body
+ name: body
+ widget: markdown
+ hint: Main content goes here.
+ - name: faq
+ label: FAQ
+ folder: _faqs
+ create: true
+ editor:
+ frame: false
+ fields:
+ - label: Question
+ name: title
+ widget: string
+ - label: Answer
+ name: body
+ widget: markdown
+ - name: posts
+ label: Posts
+ label_singular: Post
+ widget: list
+ summary: "{{fields.post | split('|', '$1')}}"
+ fields:
+ - label: Related Post
+ name: post
+ widget: relationKitchenSinkPost
+ collection: posts
+ display_fields:
+ - title
+ - date
+ search_fields:
+ - title
+ - body
+ value_field: '{{title}}|{{date}}'
+ - name: widgets
+ label: Widgets
+ delete: false
+ files:
+ - name: boolean
+ label: Boolean
+ file: _widgets/boolean.json
+ description: Boolean widget
+ fields:
+ - name: required
+ label: Required Validation
+ widget: boolean
+ - name: with_default
+ label: Required With Default
+ widget: boolean
+ default: true
+ - name: pattern
+ label: Pattern Validation
+ widget: boolean
+ pattern: ['true', 'Must be true']
+ required: false
+ - name: code
+ label: Code
+ file: _widgets/code.json
+ description: Code widget
+ fields:
+ - name: required
+ label: Required Validation
+ widget: code
+ - name: with_default
+ label: Required With Default
+ widget: code
+ default: '
Some html!
'
+ - name: pattern
+ label: Pattern Validation
+ widget: code
+ pattern: ['.{12,}', 'Must have at least 12 characters']
+ allow_input: true
+ required: false
+ - name: language
+ label: Language Selection
+ widget: code
+ allow_language_selection: true
+ required: false
+ - name: language_with_default
+ label: Language Selection With Default Language
+ widget: code
+ allow_language_selection: true
+ required: false
+ default_language: html
+ - name: language_with_default_language_and_value
+ label: Language Selection With Default Language and Value
+ widget: code
+ allow_language_selection: true
+ required: false
+ default:
+ lang: html
+ code: 'Some html!
'
+ - name: language_with_default_language_and_value_string_default
+ label: Language Selection With Default Language and Value (String Default)
+ widget: code
+ allow_language_selection: true
+ required: false
+ default_language: html
+ default: 'Some html!
'
+ - name: color
+ label: Color
+ file: _widgets/color.json
+ description: Color widget
+ fields:
+ - name: required
+ label: Required Validation
+ widget: color
+ - name: with_default
+ label: Required With Default
+ widget: color
+ default: '#2121c5'
+ - name: pattern
+ label: Pattern Validation
+ widget: color
+ pattern: ['^#([0-9a-fA-F]{3})(?:[0-9a-fA-F]{3})?$', 'Must be a valid hex code']
+ allow_input: true
+ required: false
+ - name: alpha
+ label: Alpha
+ widget: color
+ enable_alpha: true
+ required: false
+ - name: alpha_with_default
+ label: Alpha With Default
+ widget: color
+ enable_alpha: true
+ required: false
+ default: 'rgba(175, 28, 28, 0.65)'
+ - name: datetime
+ label: DateTime
+ file: _widgets/datetime.json
+ description: DateTime widget
+ fields:
+ - name: required
+ label: 'Required Validation'
+ widget: datetime
+ - name: pattern
+ label: 'Pattern Validation'
+ widget: datetime
+ format: 'MMM d, yyyy h:mm aaa'
+ date_format: 'MMM d, yyyy'
+ time_format: 'h:mm aaa'
+ pattern: ['pm', 'Must be in the afternoon']
+ required: false
+ - name: date_and_time
+ label: Date and Time
+ widget: datetime
+ format: 'MMM d, yyyy h:mm aaa'
+ date_format: 'MMM d, yyyy'
+ time_format: 'h:mm aaa'
+ required: false
+ - name: date_and_time_with_default
+ label: Date and Time With Deafult
+ widget: datetime
+ format: 'MMM d, yyyy h:mm aaa'
+ date_format: 'MMM d, yyyy'
+ time_format: 'h:mm aaa'
+ required: false
+ default: 'Jan 12, 2023 12:00 am'
+ - name: date
+ label: Date
+ widget: datetime
+ format: 'MMM d, yyyy'
+ date_format: 'MMM d, yyyy'
+ required: false
+ - name: date_with_default
+ label: Date With Deafult
+ widget: datetime
+ format: 'MMM d, yyyy'
+ date_format: 'MMM d, yyyy'
+ required: false
+ default: 'Jan 12, 2023'
+ - name: time
+ label: Time
+ widget: datetime
+ format: 'h:mm aaa'
+ time_format: 'h:mm aaa'
+ required: false
+ - name: time_with_default
+ label: Time With Deafult
+ widget: datetime
+ format: 'h:mm aaa'
+ time_format: 'h:mm aaa'
+ required: false
+ default: '12:00 am'
+ - name: file
+ label: File
+ file: _widgets/file.json
+ description: File widget
+ fields:
+ - name: required
+ label: Required Validation
+ widget: file
+ - name: with_default
+ label: Required With Default
+ widget: file
+ default: /assets/uploads/moby-dick.jpg
+ - name: pattern
+ label: Pattern Validation
+ widget: file
+ pattern: ['\.pdf', 'Must be a pdf']
+ required: false
+ - name: choose_url
+ label: Choose URL
+ widget: file
+ required: false
+ media_library:
+ choose_url: true
+ - name: image
+ label: Image
+ file: _widgets/image.json
+ description: Image widget
+ fields:
+ - name: required
+ label: Required Validation
+ widget: image
+ - name: with_default
+ label: Required With Default
+ widget: image
+ default: /assets/uploads/moby-dick.jpg
+ - name: pattern
+ label: Pattern Validation
+ widget: image
+ pattern: ['\.png', 'Must be a png']
+ required: false
+ - name: choose_url
+ label: Choose URL
+ widget: image
+ required: false
+ media_library:
+ choose_url: true
+ - name: list
+ label: List
+ file: _widgets/list.yml
+ description: List widget
+ fields:
+ - name: list
+ label: Required List
+ widget: list
+ fields:
+ - label: Name
+ name: name
+ widget: string
+ hint: First and Last
+ - label: Description
+ name: description
+ widget: text
+ - name: with_default
+ label: Required With Default
+ widget: list
+ default:
+ - name: Bob Billy
+ description: Some text about bob
+ fields:
+ - label: Name
+ name: name
+ widget: string
+ hint: First and Last
+ - label: Description
+ name: description
+ widget: text
+ - name: optional
+ label: Optional List
+ widget: list
+ required: false
+ fields:
+ - label: Name
+ name: name
+ widget: string
+ hint: First and Last
+ - label: Description
+ name: description
+ widget: text
+ - name: typed_list
+ label: Typed List
+ widget: list
+ types:
+ - label: Type 1 Object
+ name: type_1_object
+ widget: object
+ fields:
+ - label: String
+ name: string
+ widget: string
+ - label: Boolean
+ name: boolean
+ widget: boolean
+ - label: Text
+ name: text
+ widget: text
+ - label: Type 2 Object
+ name: type_2_object
+ widget: object
+ fields:
+ - label: Number
+ name: number
+ widget: number
+ - label: Select
+ name: select
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ - label: Datetime
+ name: datetime
+ widget: datetime
+ - label: Markdown
+ name: markdown
+ widget: markdown
+ - label: Type 3 Object
+ name: type_3_object
+ widget: object
+ fields:
+ - label: Image
+ name: image
+ widget: image
+ - label: File
+ name: file
+ widget: file
+ - name: typed_list_with_default
+ label: Typed List With Default
+ widget: list
+ default:
+ - type: type_2_object
+ number: 5
+ select: c
+ datetime: '2022-12-05T20:22:52+0000'
+ markdown: Some ***Markdown*** ~content~ text
+ types:
+ - label: Type 1 Object
+ name: type_1_object
+ widget: object
+ fields:
+ - label: String
+ name: string
+ widget: string
+ - label: Boolean
+ name: boolean
+ widget: boolean
+ - label: Text
+ name: text
+ widget: text
+ - label: Type 2 Object
+ name: type_2_object
+ widget: object
+ fields:
+ - label: Number
+ name: number
+ widget: number
+ - label: Select
+ name: select
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ - label: Datetime
+ name: datetime
+ widget: datetime
+ - label: Markdown
+ name: markdown
+ widget: markdown
+ - label: Type 3 Object
+ name: type_3_object
+ widget: object
+ fields:
+ - label: Image
+ name: image
+ widget: image
+ - label: File
+ name: file
+ widget: file
+ - name: map
+ label: Map
+ file: _widgets/map.json
+ description: Map widget
+ fields:
+ - name: required
+ label: Required Validation
+ widget: map
+ - name: with_default
+ label: Required With Default
+ widget: map
+ default: '{ "type": "Point", "coordinates": [-73.9852661, 40.7478738] }'
+ - name: pattern
+ label: Pattern Validation
+ widget: map
+ pattern: ['\[-([7-9][0-9]|1[0-2][0-9])\.', 'Must be between latitude -70 and -129']
+ required: false
+ - name: markdown
+ label: Markdown
+ file: _widgets/markdown.json
+ description: Markdown widget
+ fields:
+ - name: required
+ label: Required Validation
+ widget: markdown
+ - name: with_default
+ label: Required With Default
+ widget: markdown
+ default: Default **markdown** value
+ - name: pattern
+ label: Pattern Validation
+ widget: markdown
+ pattern: ['# [a-zA-Z0-9]+', 'Must have a header']
+ required: false
+ - name: number
+ label: Number
+ file: _widgets/number.json
+ description: Number widget
+ fields:
+ - name: required
+ label: Required Validation
+ widget: number
+ - name: with_default
+ label: Required With Default
+ widget: number
+ default: 5
+ - name: min
+ label: Min Validation
+ widget: number
+ min: 5
+ required: false
+ - name: max
+ label: Max Validation
+ widget: number
+ max: 10
+ required: false
+ - name: min_and_max
+ label: Min and Max Validation
+ widget: number
+ min: 5
+ max: 10
+ required: false
+ - name: pattern
+ label: Pattern Validation
+ widget: number
+ pattern: ['[0-9]{3,}', 'Must be at least 3 digits']
+ required: false
+ - name: object
+ label: Object
+ file: _widgets/object.json
+ description: Object widget
+ fields:
+ - label: Required Validation
+ name: required
+ widget: object
+ fields:
+ - label: Number of posts on frontpage
+ name: front_limit
+ widget: number
+ - label: Author
+ name: author
+ widget: string
+ - label: Thumbnail
+ name: thumb
+ widget: image
+ - label: Required With Defaults
+ name: with_defaults
+ widget: object
+ fields:
+ - label: Number of posts on frontpage
+ name: front_limit
+ widget: number
+ default: 5
+ - label: Author
+ name: author
+ widget: string
+ default: Bob
+ - label: Thumbnail
+ name: thumb
+ widget: image
+ default: /assets/uploads/moby-dick.jpg
+ - label: Optional Validation
+ name: optional
+ widget: object
+ required: false
+ fields:
+ - label: Number of posts on frontpage
+ name: front_limit
+ widget: number
+ required: false
+ - label: Author
+ name: author
+ widget: string
+ required: false
+ - label: Thumbnail
+ name: thumb
+ widget: image
+ required: false
+ - label: With Hidden Field
+ name: hidden_field
+ widget: object
+ required: false
+ fields:
+ - name: layout
+ widget: hidden
+ default: post
+ - label: Number of posts on frontpage
+ name: front_limit
+ widget: number
+ required: false
+ - label: Author
+ name: author
+ widget: string
+ required: false
+ - label: Thumbnail
+ name: thumb
+ widget: image
+ required: false
+ - name: relation
+ label: Relation
+ file: _widgets/relation.json
+ description: Relation widget
+ fields:
+ - label: Required Validation
+ name: required
+ widget: relation
+ collection: posts
+ display_fields:
+ - title
+ - date
+ search_fields:
+ - title
+ - body
+ value_field: title
+ - label: Required With Default
+ name: with_default
+ widget: relation
+ collection: posts
+ display_fields:
+ - title
+ - date
+ search_fields:
+ - title
+ - body
+ value_field: title
+ default: This is a YAML front matter post
+ - label: Optional Validation
+ name: optional
+ widget: relation
+ required: false
+ collection: posts
+ display_fields:
+ - title
+ - date
+ search_fields:
+ - title
+ - body
+ value_field: title
+ - label: Multiple
+ name: multiple
+ widget: relation
+ multiple: true
+ required: false
+ collection: posts
+ display_fields:
+ - title
+ - date
+ search_fields:
+ - title
+ - body
+ value_field: title
+ - label: Multiple With Default
+ name: multiple_with_default
+ widget: relation
+ multiple: true
+ required: false
+ collection: posts
+ default:
+ - This is a JSON front matter post
+ - This is a YAML front matter post
+ display_fields:
+ - title
+ - date
+ search_fields:
+ - title
+ - body
+ value_field: title
+ - name: select
+ label: Select
+ file: _widgets/select.json
+ description: Select widget
+ fields:
+ - label: Required Validation
+ name: required
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ - label: Required With Default
+ name: with_default
+ widget: select
+ default: b
+ options:
+ - a
+ - b
+ - c
+ - label: Pattern Validation
+ name: pattern
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ pattern: ['[a-b]', 'Must be a or b']
+ required: false
+ - label: Number Value
+ name: number
+ widget: select
+ options:
+ - 1
+ - 2
+ - 3
+ - label: Number With Default
+ name: number_with_default
+ widget: select
+ default: 3
+ options:
+ - 1
+ - 2
+ - 3
+ - label: Value and Label
+ name: value_and_label
+ widget: select
+ options:
+ - value: a
+ label: A fancy label
+ - value: 2
+ label: Another fancy label
+ - value: c
+ label: And one more fancy label
+ - label: Value and Label With Default
+ name: value_and_label_with_default
+ widget: select
+ default: 2
+ options:
+ - value: a
+ label: A fancy label
+ - value: 2
+ label: Another fancy label
+ - value: c
+ label: And one more fancy label
+ - label: Multiple
+ name: multiple
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ pattern: ['[a-b]', 'Must be a or b']
+ multiple: true
+ required: false
+ - label: Multiple With Default
+ name: multiple_with_default
+ widget: select
+ default:
+ - b
+ - c
+ options:
+ - a
+ - b
+ - c
+ pattern: ['[a-b]', 'Must be a or b']
+ multiple: true
+ required: false
+ - label: Value and Label Multiple
+ name: value_and_label_multiple
+ widget: select
+ multiple: true
+ options:
+ - value: a
+ label: A fancy label
+ - value: b
+ label: Another fancy label
+ - value: c
+ label: And one more fancy label
+ - name: string
+ label: String
+ file: _widgets/string.json
+ description: String widget
+ fields:
+ - name: required
+ label: Required Validation
+ widget: string
+ - name: with_default
+ label: Required With Default
+ widget: string
+ default: Default value
+ - name: pattern
+ label: Pattern Validation
+ widget: string
+ pattern: ['.{12,}', 'Must have at least 12 characters']
+ required: false
+ - name: text
+ label: Text
+ file: _widgets/text.json
+ description: Text widget
+ fields:
+ - name: required
+ label: 'Required Validation'
+ widget: text
+ - name: with_default
+ label: Required With Default
+ widget: text
+ default: Default value
+ - name: pattern
+ label: 'Pattern Validation'
+ widget: text
+ pattern: ['.{12,}', 'Must have at least 12 characters']
+ required: false
+ - name: settings
+ label: Settings
+ delete: false
+ editor:
+ preview: false
+ files:
+ - name: general
+ label: Site Settings
+ file: _data/settings.json
+ description: General Site Settings
+ editor:
+ preview: true
+ fields:
+ - label: Number of posts on frontpage
+ name: front_limit
+ widget: number
+ min: 1
+ max: 10
+ - label: Global title
+ name: site_title
+ widget: string
+ - label: Post Settings
+ name: posts
+ widget: object
+ fields:
+ - label: Number of posts on frontpage
+ name: front_limit
+ widget: number
+ min: 1
+ max: 10
+ - label: Default Author
+ name: author
+ widget: string
+ - label: Default Thumbnail
+ name: thumb
+ widget: image
+ required: false
+ - name: authors
+ label: Authors
+ file: _data/authors.yml
+ description: Author descriptions
+ editor:
+ preview: true
+ fields:
+ - name: authors
+ label: Authors
+ label_singular: Author
+ widget: list
+ fields:
+ - label: Name
+ name: name
+ widget: string
+ hint: First and Last
+ - label: Description
+ name: description
+ widget: text
+ - name: kitchenSink
+ label: Kitchen Sink
+ folder: _sink
+ create: true
+ fields:
+ - label: Related Post
+ name: post
+ widget: relationKitchenSinkPost
+ collection: posts
+ display_fields:
+ - title
+ - date
+ search_fields:
+ - title
+ - body
+ value_field: title
+ - label: Title
+ name: title
+ widget: string
+ - label: Boolean
+ name: boolean
+ widget: boolean
+ default: true
+ - label: Map
+ name: map
+ widget: map
+ - label: Text
+ name: text
+ widget: text
+ hint: 'Plain text, not markdown'
+ - label: Number
+ name: number
+ widget: number
+ hint: To infinity and beyond!
+ - label: Markdown
+ name: markdown
+ widget: markdown
+ - label: Datetime
+ name: datetime
+ widget: datetime
+ - label: Color
+ name: color
+ widget: color
+ - label: Color string editable and alpha enabled
+ name: colorEditable
+ widget: color
+ enable_alpha: true
+ allow_input: true
+ - label: Image
+ name: image
+ widget: image
+ - label: File
+ name: file
+ widget: file
+ - label: Select
+ name: select
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ - label: Select multiple
+ name: select_multiple
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ multiple: true
+ - label: Select numeric
+ name: select_numeric
+ widget: select
+ options:
+ - label: One
+ value: 1
+ - label: Two
+ value: 2
+ - label: Three
+ value: 3
+ - label: Select mixed string and numeric
+ name: select_mixed_string_numeric
+ widget: select
+ options:
+ - label: One
+ value: 'One'
+ - label: Two
+ value: 2
+ - label: Three
+ value: 3
+ - label: Hidden
+ name: hidden
+ widget: hidden
+ default: hidden
+ - label: Object
+ name: object
+ widget: object
+ collapsed: true
+ fields:
+ - label: Related Post
+ name: post
+ widget: relationKitchenSinkPost
+ collection: posts
+ search_fields:
+ - title
+ - body
+ value_field: title
+ - label: String
+ name: string
+ widget: string
+ - label: Boolean
+ name: boolean
+ widget: boolean
+ default: false
+ - label: Text
+ name: text
+ widget: text
+ - label: Number
+ name: number
+ widget: number
+ - label: Markdown
+ name: markdown
+ widget: markdown
+ - label: Datetime
+ name: datetime
+ widget: datetime
+ - label: Image
+ name: image
+ widget: image
+ - label: File
+ name: file
+ widget: file
+ - label: Select
+ name: select
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ - label: List
+ name: list
+ widget: list
+ fields:
+ - label: String
+ name: string
+ widget: string
+ - label: Boolean
+ name: boolean
+ widget: boolean
+ - label: Text
+ name: text
+ widget: text
+ - label: Number
+ name: number
+ widget: number
+ - label: Markdown
+ name: markdown
+ widget: markdown
+ - label: Datetime
+ name: datetime
+ widget: datetime
+ - label: Image
+ name: image
+ widget: image
+ - label: File
+ name: file
+ widget: file
+ - label: Select
+ name: select
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ - label: Object
+ name: object
+ widget: object
+ fields:
+ - label: String
+ name: string
+ widget: string
+ - label: Boolean
+ name: boolean
+ widget: boolean
+ - label: Text
+ name: text
+ widget: text
+ - label: Number
+ name: number
+ widget: number
+ - label: Markdown
+ name: markdown
+ widget: markdown
+ - label: Datetime
+ name: datetime
+ widget: datetime
+ - label: Image
+ name: image
+ widget: image
+ - label: File
+ name: file
+ widget: file
+ - label: Select
+ name: select
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ - label: List
+ name: list
+ widget: list
+ fields:
+ - label: Related Post
+ name: post
+ widget: relationKitchenSinkPost
+ collection: posts
+ search_fields:
+ - title
+ - body
+ value_field: title
+ - label: String
+ name: string
+ widget: string
+ - label: Boolean
+ name: boolean
+ widget: boolean
+ - label: Text
+ name: text
+ widget: text
+ - label: Number
+ name: number
+ widget: number
+ - label: Markdown
+ name: markdown
+ widget: markdown
+ - label: Datetime
+ name: datetime
+ widget: datetime
+ - label: Image
+ name: image
+ widget: image
+ - label: File
+ name: file
+ widget: file
+ - label: Select
+ name: select
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ - label: Hidden
+ name: hidden
+ widget: hidden
+ default: hidden
+ - label: Object
+ name: object
+ widget: object
+ fields:
+ - label: String
+ name: string
+ widget: string
+ - label: Boolean
+ name: boolean
+ widget: boolean
+ - label: Text
+ name: text
+ widget: text
+ - label: Number
+ name: number
+ widget: number
+ - label: Markdown
+ name: markdown
+ widget: markdown
+ - label: Datetime
+ name: datetime
+ widget: datetime
+ - label: Image
+ name: image
+ widget: image
+ - label: File
+ name: file
+ widget: file
+ - label: Select
+ name: select
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ - label: Typed List
+ name: typed_list
+ widget: list
+ types:
+ - label: Type 1 Object
+ name: type_1_object
+ widget: object
+ fields:
+ - label: String
+ name: string
+ widget: string
+ - label: Boolean
+ name: boolean
+ widget: boolean
+ - label: Text
+ name: text
+ widget: text
+ - label: Type 2 Object
+ name: type_2_object
+ widget: object
+ fields:
+ - label: Number
+ name: number
+ widget: number
+ - label: Select
+ name: select
+ widget: select
+ options:
+ - a
+ - b
+ - c
+ - label: Datetime
+ name: datetime
+ widget: datetime
+ - label: Markdown
+ name: markdown
+ widget: markdown
+ - label: Type 3 Object
+ name: type_3_object
+ widget: object
+ fields:
+ - label: Image
+ name: image
+ widget: image
+ - label: File
+ name: file
+ widget: file
diff --git a/packages/demo/example.css b/packages/demo/example.css
new file mode 100644
index 00000000..7b683ec3
--- /dev/null
+++ b/packages/demo/example.css
@@ -0,0 +1,21 @@
+html,
+body {
+ color: #444;
+ font-size: 14px;
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+}
+
+body {
+ padding: 20px;
+}
+
+h1 {
+ margin-top: 20px;
+ color: #666;
+ font-weight: bold;
+ font-size: 32px;
+}
+
+img {
+ max-width: 100%;
+}
diff --git a/packages/demo/index.html b/packages/demo/index.html
new file mode 100644
index 00000000..c2f0c027
--- /dev/null
+++ b/packages/demo/index.html
@@ -0,0 +1,248 @@
+
+
+
+
+
+ Static CMS Demo
+
+
+
+
+
+
+
diff --git a/packages/demo/index.js b/packages/demo/index.js
new file mode 100644
index 00000000..0428dfd3
--- /dev/null
+++ b/packages/demo/index.js
@@ -0,0 +1,173 @@
+// Register all the things
+CMS.init();
+
+const PostPreview = ({ entry, widgetFor, widgetsFor }) => {
+ return h(
+ 'div',
+ {},
+ h('div', { className: 'cover' }, h('h1', {}, entry.data.title), widgetFor('image')),
+ h('p', {}, h('small', {}, 'Written ' + entry.data.date)),
+ h('div', { className: 'text' }, widgetFor('body')),
+ );
+};
+
+const GeneralPreview = ({ widgetsFor, getAsset, entry }) => {
+ const title = entry.data.site_title;
+ const posts = entry.data.posts;
+ const thumb = posts && posts.thumb;
+
+ const [thumbUrl, setThumbUrl] = useState('');
+
+ useEffect(() => {
+ let alive = true;
+
+ const loadThumb = async () => {
+ const thumbAsset = await getAsset(thumb);
+ if (alive) {
+ setThumbUrl(thumbAsset.toString());
+ }
+ };
+
+ loadThumb();
+
+ return () => {
+ alive = false;
+ };
+ }, [thumb]);
+
+ 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'),
+
+ h('dt', {}, 'Default Thumbnail'),
+ h('dd', {}, thumb && h('img', { src: thumbUrl })),
+ ),
+ );
+};
+
+const AuthorsPreview = ({ widgetsFor }) => {
+ return h(
+ 'div',
+ {},
+ h('h1', {}, 'Authors'),
+ widgetsFor('authors').map(function (author, index) {
+ return h(
+ 'div',
+ { key: index },
+ h('hr', {}),
+ h('strong', {}, author.data.name),
+ author.widgets.description,
+ );
+ }),
+ );
+};
+
+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.registerPreviewStyle('.toastui-editor-contents h1 { color: blue }', { raw: true });
+CMS.registerPreviewTemplate('posts', PostPreview);
+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.registerAdditionalLink({
+ id: 'example',
+ title: 'Example.com',
+ data: 'https://example.com',
+ options: {
+ icon: 'page',
+ },
+});
+CMS.registerAdditionalLink({
+ id: 'custom-page',
+ title: 'Custom Page',
+ data: CustomPage,
+ options: {
+ icon: 'page',
+ },
+});
+
+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}`,
+ },
+ '',
+ ),
+ );
+ },
+});
diff --git a/yarn.lock b/yarn.lock
index a1858f24..a271ca9f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3393,10 +3393,10 @@
redux-thunk "^2.4.2"
reselect "^4.1.7"
-"@remix-run/router@1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.1.0.tgz#b48db8148c8a888e50580a8152b6f68161c49406"
- integrity sha512-rGl+jH/7x1KBCQScz9p54p0dtPLNeKGb3e0wD2H5/oZj41bwQUnXdzbj2TbUAFhvD7cp9EyEQA4dEgpUFa1O7Q==
+"@remix-run/router@1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.2.0.tgz#54eff8306938b64c521f4a9ed313d33a91ef019a"
+ integrity sha512-GO82KYYTWPRCgdNtnheaZG3LcViUlxRFlHM7ykh7N+ufoXi6PVIHoP+9RUG/vuzl2hr9i/h6EA1Eq+2HpqJ0gQ==
"@rollup/plugin-babel@^5.2.0":
version "5.3.1"
@@ -13151,20 +13151,20 @@ react-refresh@0.14.0:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
-react-router-dom@6.5.0:
- version "6.5.0"
- resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.5.0.tgz#3970bdcaa7c710a6e0b478a833ba0b4b8ae61a6f"
- integrity sha512-/XzRc5fq80gW1ctiIGilyKFZC/j4kfe75uivMsTChFbkvrK4ZrF3P3cGIc1f/SSkQ4JiJozPrf+AwUHHWVehVg==
+react-router-dom@6.6.0:
+ version "6.6.0"
+ resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.6.0.tgz#ad20b37b69e9fe7c6389663a0767ba40a19f71fe"
+ integrity sha512-qC4jnvpfCPKVle1mKLD75IvZLcbVJyFMlSn16WY9ZiOed3dgSmqhslCf/u3tmSccWOujkdsT/OwGq12bELmvjg==
dependencies:
- "@remix-run/router" "1.1.0"
- react-router "6.5.0"
+ "@remix-run/router" "1.2.0"
+ react-router "6.6.0"
-react-router@6.5.0:
- version "6.5.0"
- resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.5.0.tgz#b53f15543a60750c925609d2e38037ac5aed6dd3"
- integrity sha512-fqqUSU0NC0tSX0sZbyuxzuAzvGqbjiZItBQnyicWlOUmzhAU8YuLgRbaCL2hf3sJdtRy4LP/WBrWtARkMvdGPQ==
+react-router@6.6.0:
+ version "6.6.0"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.6.0.tgz#9ae27cfc6bf7f2b28e1f028fe01fdbec96a3a41d"
+ integrity sha512-+VPfCIaFbkW7BAiB/2oeprxKAt1KLbl+zXZ10CXOYezKWgBmTKyh8XjI53eLqY5kd7uY+V4rh3UW44FclwUU+Q==
dependencies:
- "@remix-run/router" "1.1.0"
+ "@remix-run/router" "1.2.0"
react-schemaorg@2.0.0:
version "2.0.0"