From 6b73c39ba844f1f3f072aafa773eaf552c7ae6a4 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Christensen Date: Fri, 28 Oct 2016 04:51:37 +0200 Subject: [PATCH] Support for object widget --- example/config.yml | 7 ++++ src/backends/backend.js | 10 +++-- src/components/Widgets.js | 6 +++ src/components/Widgets/NumberControl.js | 16 ++++++++ src/components/Widgets/NumberPreview.js | 9 +++++ src/components/Widgets/ObjectControl.js | 51 +++++++++++++++++++++++++ src/components/Widgets/ObjectPreview.js | 28 ++++++++++++++ src/formats/formats.js | 1 + src/formats/json.js | 2 +- 9 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 src/components/Widgets/NumberControl.js create mode 100644 src/components/Widgets/NumberPreview.js create mode 100644 src/components/Widgets/ObjectControl.js create mode 100644 src/components/Widgets/ObjectPreview.js diff --git a/example/config.yml b/example/config.yml index c53282ca..15f01c74 100644 --- a/example/config.yml +++ b/example/config.yml @@ -37,6 +37,13 @@ collections: # A list of collections the CMS should be able to edit description: "General Site Settings" fields: - {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} + - {label: "Default Author", name: author, widget: string} + - {label: "Default Thumbnail", name: thumb, widget: image, class: "thumb"} - name: "authors" label: "Authors" diff --git a/src/backends/backend.js b/src/backends/backend.js index 47db23e8..68bd3857 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -154,16 +154,18 @@ class Backend { throw (new Error('Not allowed to create new entries in this collection')); } const slug = slugFormatter(collection.get('slug'), entryDraft.getIn(['entry', 'data'])); + const path = collectionModel.entryPath(slug); entryObj = { - path: collectionModel.entryPath(slug), + path, slug, - raw: this.entryToRaw(collection, entryData), + raw: this.entryToRaw(collection, Object.assign({ path }, entryData)), }; } else { + const path = entryDraft.getIn(['entry', 'path']); entryObj = { - path: entryDraft.getIn(['entry', 'path']), + path, slug: entryDraft.getIn(['entry', 'slug']), - raw: this.entryToRaw(collection, entryData), + raw: this.entryToRaw(collection, Object.assign({ path }, entryData)), }; } diff --git a/src/components/Widgets.js b/src/components/Widgets.js index 74bd97ec..bdecd996 100644 --- a/src/components/Widgets.js +++ b/src/components/Widgets.js @@ -3,6 +3,8 @@ import UnknownControl from './Widgets/UnknownControl'; import UnknownPreview from './Widgets/UnknownPreview'; import StringControl from './Widgets/StringControl'; import StringPreview from './Widgets/StringPreview'; +import NumberControl from './Widgets/NumberControl'; +import NumberPreview from './Widgets/NumberPreview'; import ListControl from './Widgets/ListControl'; import ListPreview from './Widgets/ListPreview'; import TextControl from './Widgets/TextControl'; @@ -13,13 +15,17 @@ import ImageControl from './Widgets/ImageControl'; import ImagePreview from './Widgets/ImagePreview'; import DateTimeControl from './Widgets/DateTimeControl'; import DateTimePreview from './Widgets/DateTimePreview'; +import ObjectControl from './Widgets/ObjectControl'; +import ObjectPreview from './Widgets/ObjectPreview'; registry.registerWidget('string', StringControl, StringPreview); registry.registerWidget('text', TextControl, TextPreview); +registry.registerWidget('number', NumberControl, NumberPreview); registry.registerWidget('list', ListControl, ListPreview); registry.registerWidget('markdown', MarkdownControl, MarkdownPreview); registry.registerWidget('image', ImageControl, ImagePreview); registry.registerWidget('datetime', DateTimeControl, DateTimePreview); +registry.registerWidget('object', ObjectControl, ObjectPreview); registry.registerWidget('unknown', UnknownControl, UnknownPreview); export function resolveWidget(name) { diff --git a/src/components/Widgets/NumberControl.js b/src/components/Widgets/NumberControl.js new file mode 100644 index 00000000..fe92b768 --- /dev/null +++ b/src/components/Widgets/NumberControl.js @@ -0,0 +1,16 @@ +import React, { PropTypes } from 'react'; + +export default class StringControl extends React.Component { + handleChange = e => { + this.props.onChange(e.target.value); + }; + + render() { + return ; + } +} + +StringControl.propTypes = { + onChange: PropTypes.func.isRequired, + value: PropTypes.node, +}; diff --git a/src/components/Widgets/NumberPreview.js b/src/components/Widgets/NumberPreview.js new file mode 100644 index 00000000..972e068c --- /dev/null +++ b/src/components/Widgets/NumberPreview.js @@ -0,0 +1,9 @@ +import React, { PropTypes } from 'react'; + +export default function StringPreview({ value }) { + return {value}; +} + +StringPreview.propTypes = { + value: PropTypes.node, +}; diff --git a/src/components/Widgets/ObjectControl.js b/src/components/Widgets/ObjectControl.js new file mode 100644 index 00000000..d2dfaf37 --- /dev/null +++ b/src/components/Widgets/ObjectControl.js @@ -0,0 +1,51 @@ +import React, { Component, PropTypes } from 'react'; +import { Map } from 'immutable'; +import { resolveWidget } from '../Widgets'; +import styles from '../ControlPanel/ControlPane.css'; + +export default class ObjectControl extends Component { + static propTypes = { + onChange: PropTypes.func.isRequired, + onAddMedia: PropTypes.func.isRequired, + getMedia: PropTypes.func.isRequired, + value: PropTypes.node, + field: PropTypes.node, + }; + + controlFor(field) { + const { onAddMedia, onRemoveMedia, getMedia, value, onChange } = this.props; + const widget = resolveWidget(field.get('widget') || 'string'); + const fieldValue = value && value.get(field.get('name')); + + return ( +
+ + { + React.createElement(widget.control, { + field, + value: fieldValue, + onChange: (val) => { + onChange((value || Map()).set(field.get('name'), val)); + }, + onAddMedia, + onRemoveMedia, + getMedia, + }) + } +
+ ); + } + + render() { + const { field } = this.props; + const fields = field.get('fields'); + + if (!fields) { + return

No fields defined for this widget

; + } + + return (
+ {field.get('fields').map(field => this.controlFor(field))} +
); + } +} diff --git a/src/components/Widgets/ObjectPreview.js b/src/components/Widgets/ObjectPreview.js new file mode 100644 index 00000000..3c541ef6 --- /dev/null +++ b/src/components/Widgets/ObjectPreview.js @@ -0,0 +1,28 @@ +import React, { PropTypes, Component } from 'react'; +import { resolveWidget } from '../Widgets'; + +export default class ObjectPreview extends Component { + widgetFor = (field) => { + const { value, getMedia } = this.props; + const widget = resolveWidget(field.get('widget')); + return (
{React.createElement(widget.preview, { + key: field.get('name'), + value: value && value.get(field.get('name')), + field, + getMedia, + })}
); + }; + + render() { + const { field } = this.props; + const fields = field && field.get('fields'); + + return
{fields && fields.map(f => this.widgetFor(f))}
; + } +} + +ObjectPreview.propTypes = { + value: PropTypes.node, + field: PropTypes.node, + getMedia: PropTypes.func.isRequired, +}; diff --git a/src/formats/formats.js b/src/formats/formats.js index 48fa5367..2b7d9648 100644 --- a/src/formats/formats.js +++ b/src/formats/formats.js @@ -33,6 +33,7 @@ export function resolveFormat(collectionOrEntity, entry) { if (typeof collectionOrEntity === 'string') { return formatByType(collectionOrEntity); } + console.log('entry: %o', entry); const path = entry && entry.path; if (path) { return formatByExtension(path.split('.').pop()); diff --git a/src/formats/json.js b/src/formats/json.js index 8cfd87dd..e2e5467e 100644 --- a/src/formats/json.js +++ b/src/formats/json.js @@ -4,6 +4,6 @@ export default class JSONFormatter { } toFile(data) { - return JSON.generate(data); + return JSON.stringify(data); } }