diff --git a/package.json b/package.json
index 592840a3..c86e3cbd 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,10 @@
"git add"
]
},
- "files": ["dist/", "README.md"],
+ "files": [
+ "dist/",
+ "README.md"
+ ],
"pre-commit": "lint:staged",
"jest": {
"moduleNameMapper": {
@@ -127,7 +130,7 @@
"react-datetime": "^2.6.0",
"react-dom": "^15.1.0",
"react-hot-loader": "^3.0.0-beta.2",
- "react-immutable-proptypes": "^1.6.0",
+ "react-immutable-proptypes": "^2.1.0",
"react-lazy-load": "^3.0.3",
"react-portal": "^2.2.1",
"react-pure-render": "^1.0.2",
diff --git a/src/components/ControlPanel/ControlPane.js b/src/components/ControlPanel/ControlPane.js
index f11114f0..493c3da7 100644
--- a/src/components/ControlPanel/ControlPane.js
+++ b/src/components/ControlPanel/ControlPane.js
@@ -10,7 +10,7 @@ function isHidden(field) {
export default class ControlPane extends Component {
controlFor(field) {
- const { entry, fields, getMedia, onChange, onAddMedia, onRemoveMedia } = this.props;
+ const { entry, getMedia, onChange, onAddMedia, onRemoveMedia } = this.props;
const widget = resolveWidget(field.get('widget'));
const fieldName = field.get('name');
const value = entry.getIn(['data', fieldName]);
@@ -41,14 +41,12 @@ export default class ControlPane extends Component {
return (
{
- fields.map(field =>
- isHidden(field) ? null :
- {this.controlFor(field)}
-
- )
+ fields.map((field) => {
+ if (isHidden(field)) {
+ return null;
+ }
+ return
{this.controlFor(field)}
;
+ })
}
);
diff --git a/src/components/PreviewPane/PreviewPane.js b/src/components/PreviewPane/PreviewPane.js
index 87734a2d..e9f14abc 100644
--- a/src/components/PreviewPane/PreviewPane.js
+++ b/src/components/PreviewPane/PreviewPane.js
@@ -15,7 +15,7 @@ export default class PreviewPane extends React.Component {
widgetFor = (name) => {
const { fields, entry, getMedia } = this.props;
- const field = fields.find(field => field.get('name') === name);
+ const field = fields.find(f => f.get('name') === name);
const widget = resolveWidget(field.get('widget'));
return React.createElement(widget.preview, {
key: field.get('name'),
@@ -25,6 +25,21 @@ export default class PreviewPane extends React.Component {
});
};
+ handleIframeRef = (ref) => {
+ if (ref) {
+ registry.getPreviewStyles().forEach((style) => {
+ const linkEl = document.createElement('link');
+ linkEl.setAttribute('rel', 'stylesheet');
+ linkEl.setAttribute('href', style);
+ ref.contentDocument.head.appendChild(linkEl);
+ });
+ this.previewEl = document.createElement('div');
+ this.iframeBody = ref.contentDocument.body;
+ this.iframeBody.appendChild(this.previewEl);
+ this.renderPreview();
+ }
+ };
+
renderPreview() {
const { entry, collection } = this.props;
const component = registry.getPreviewTemplate(selectTemplateName(collection, entry.get('slug'))) || Preview;
@@ -42,21 +57,6 @@ export default class PreviewPane extends React.Component {
, this.previewEl);
}
- handleIframeRef = (ref) => {
- if (ref) {
- registry.getPreviewStyles().forEach((style) => {
- const linkEl = document.createElement('link');
- linkEl.setAttribute('rel', 'stylesheet');
- linkEl.setAttribute('href', style);
- ref.contentDocument.head.appendChild(linkEl);
- });
- this.previewEl = document.createElement('div');
- this.iframeBody = ref.contentDocument.body;
- this.iframeBody.appendChild(this.previewEl);
- this.renderPreview();
- }
- };
-
render() {
const { collection } = this.props;
if (!collection) {
@@ -72,7 +72,4 @@ PreviewPane.propTypes = {
fields: ImmutablePropTypes.list.isRequired,
entry: ImmutablePropTypes.map.isRequired,
getMedia: PropTypes.func.isRequired,
- scrollTop: PropTypes.number,
- scrollHeight: PropTypes.number,
- offsetHeight: PropTypes.number,
};
diff --git a/src/components/Widgets.js b/src/components/Widgets.js
index bdecd996..a0a6e118 100644
--- a/src/components/Widgets.js
+++ b/src/components/Widgets.js
@@ -13,8 +13,12 @@ import MarkdownControl from './Widgets/MarkdownControl';
import MarkdownPreview from './Widgets/MarkdownPreview';
import ImageControl from './Widgets/ImageControl';
import ImagePreview from './Widgets/ImagePreview';
+import DateControl from './Widgets/DateControl';
+import DatePreview from './Widgets/DatePreview';
import DateTimeControl from './Widgets/DateTimeControl';
import DateTimePreview from './Widgets/DateTimePreview';
+import SelectControl from './Widgets/SelectControl';
+import SelectPreview from './Widgets/SelectPreview';
import ObjectControl from './Widgets/ObjectControl';
import ObjectPreview from './Widgets/ObjectPreview';
@@ -24,10 +28,12 @@ registry.registerWidget('number', NumberControl, NumberPreview);
registry.registerWidget('list', ListControl, ListPreview);
registry.registerWidget('markdown', MarkdownControl, MarkdownPreview);
registry.registerWidget('image', ImageControl, ImagePreview);
+registry.registerWidget('date', DateControl, DatePreview);
registry.registerWidget('datetime', DateTimeControl, DateTimePreview);
+registry.registerWidget('select', SelectControl, SelectPreview);
registry.registerWidget('object', ObjectControl, ObjectPreview);
registry.registerWidget('unknown', UnknownControl, UnknownPreview);
-export function resolveWidget(name) {
- return registry.getWidget(name) || registry.getWidget('unknown');
+export function resolveWidget(name) { // eslint-disable-line
+ return registry.getWidget(name || 'string') || registry.getWidget('unknown');
}
diff --git a/src/components/Widgets/DateControl.js b/src/components/Widgets/DateControl.js
new file mode 100644
index 00000000..b0170ee7
--- /dev/null
+++ b/src/components/Widgets/DateControl.js
@@ -0,0 +1,21 @@
+import React, { PropTypes } from 'react';
+import DateTime from 'react-datetime';
+
+export default class DateControl extends React.Component {
+ handleChange = (datetime) => {
+ this.props.onChange(datetime);
+ };
+
+ render() {
+ return ();
+ }
+}
+
+DateControl.propTypes = {
+ onChange: PropTypes.func.isRequired,
+ value: PropTypes.object,
+};
diff --git a/src/components/Widgets/DatePreview.js b/src/components/Widgets/DatePreview.js
new file mode 100644
index 00000000..e009069b
--- /dev/null
+++ b/src/components/Widgets/DatePreview.js
@@ -0,0 +1,9 @@
+import React, { PropTypes } from 'react';
+
+export default function DatePreview({ value }) {
+ return {value ? value.toString() : null};
+}
+
+DatePreview.propTypes = {
+ value: PropTypes.node,
+};
diff --git a/src/components/Widgets/ListControl.js b/src/components/Widgets/ListControl.js
index 62c634b5..55617adb 100644
--- a/src/components/Widgets/ListControl.js
+++ b/src/components/Widgets/ListControl.js
@@ -13,6 +13,10 @@ ListItem.propTypes = {
};
ListItem.displayName = 'list-item';
+function valueToString(value) {
+ return value ? value.join(',').replace(/,([^\s]|$)/g, ', $1') : '';
+}
+
const SortableListItem = sortable(ListItem);
export default class ListControl extends Component {
@@ -20,15 +24,32 @@ export default class ListControl extends Component {
onChange: PropTypes.func.isRequired,
value: PropTypes.node,
field: PropTypes.node,
+ getMedia: PropTypes.func.isRequired,
+ onAddMedia: PropTypes.func.isRequired,
+ onRemoveMedia: PropTypes.func.isRequired,
};
constructor(props) {
super(props);
- this.state = { itemStates: Map() };
+ this.state = { itemStates: Map(), value: valueToString(props.value) };
}
handleChange = (e) => {
- this.props.onChange(e.target.value.split(',').map(item => item.trim()));
+ const oldValue = this.state.value;
+ const newValue = e.target.value;
+ const listValue = e.target.value.split(',');
+ if (newValue.match(/,$/) && oldValue.match(/, $/)) {
+ listValue.pop();
+ }
+
+ this.setState({ value: valueToString(listValue) });
+ this.props.onChange(listValue);
+ };
+
+ handleCleanup = (e) => {
+ const listValue = e.target.value.split(',').map(el => el.trim()).filter(el => el);
+ this.setState({ value: valueToString(listValue) });
+ this.props.onChange(listValue);
};
handleAdd = (e) => {
@@ -80,13 +101,13 @@ export default class ListControl extends Component {
renderItem(item, index) {
const { value, field, getMedia, onAddMedia, onRemoveMedia } = this.props;
- const { itemStates, draggedItem } = this.state;
+ const { itemStates } = this.state;
const collapsed = itemStates.getIn([index, 'collapsed']);
const classNames = [styles.item, collapsed ? styles.collapsed : styles.expanded];
return (
{value && value.map((item, index) => this.renderItem(item, index))}
@@ -121,12 +142,18 @@ export default class ListControl extends Component {
}
render() {
- const { value, field } = this.props;
+ const { field } = this.props;
+ const { value } = this.state;
if (field.get('fields')) {
return this.renderListControl();
}
- return ;
+ return ();
}
}
diff --git a/src/components/Widgets/MarkdownControlElements/RawEditor/index.js b/src/components/Widgets/MarkdownControlElements/RawEditor/index.js
index 36234c8d..d921a4c1 100644
--- a/src/components/Widgets/MarkdownControlElements/RawEditor/index.js
+++ b/src/components/Widgets/MarkdownControlElements/RawEditor/index.js
@@ -1,5 +1,4 @@
import React, { PropTypes } from 'react';
-import { fromJS } from 'immutable';
import MarkupIt from 'markup-it';
import markdownSyntax from 'markup-it/syntaxes/markdown';
import htmlSyntax from 'markup-it/syntaxes/html';
@@ -65,29 +64,6 @@ function getCleanPaste(e) {
});
}
-const buildtInPlugins = [{
- label: 'Image',
- id: 'image',
- fromBlock: match => match && {
- image: match[2],
- alt: match[1],
- },
- toBlock: data => `![${ data.alt }](${ data.image })`,
- toPreview: (data) => {
- return ;
- },
- pattern: /^!\[([^\]]+)\]\(([^\)]+)\)$/,
- fields: [{
- label: 'Image',
- name: 'image',
- widget: 'image',
- }, {
- label: 'Alt Text',
- name: 'alt',
- }],
-}];
-buildtInPlugins.forEach(plugin => registry.registerEditorComponent(plugin));
-
export default class RawEditor extends React.Component {
constructor(props) {
super(props);
@@ -237,7 +213,6 @@ export default class RawEditor extends React.Component {
if (selection.start !== selection.end && !HAS_LINE_BREAK.test(selection.selected)) {
try {
const selectionPosition = this.caretPosition.get(selection.start, selection.end);
- console.log('pos: %o', selectionPosition);
this.setState({ showToolbar: true, showBlockMenu: false, selectionPosition });
} catch (e) {
this.setState({ showToolbar: false, showBlockMenu: false });
diff --git a/src/components/Widgets/SelectControl.js b/src/components/Widgets/SelectControl.js
new file mode 100644
index 00000000..c6c6a61d
--- /dev/null
+++ b/src/components/Widgets/SelectControl.js
@@ -0,0 +1,44 @@
+import React, { PropTypes } from 'react';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+
+export default class SelectControl extends React.Component {
+ handleChange = (e) => {
+ this.props.onChange(e.target.value);
+ };
+
+ render() {
+ const { field, value } = this.props;
+ const fieldOptions = field.get('options');
+
+ if (!fieldOptions) {
+ return Error rendering select control for {field.get('name')}: No options
;
+ }
+
+ const options = fieldOptions.map((option) => {
+ if (typeof option === 'string') {
+ return { label: option, value: option };
+ }
+ return option;
+ });
+
+ return ();
+ }
+}
+
+SelectControl.propTypes = {
+ onChange: PropTypes.func.isRequired,
+ value: PropTypes.node,
+ field: ImmutablePropTypes.contains({
+ options: ImmutablePropTypes.listOf(PropTypes.oneOf([
+ PropTypes.string,
+ ImmutablePropTypes.contains({
+ label: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ }),
+ ])).isRequired,
+ }),
+};
diff --git a/src/components/Widgets/SelectPreview.js b/src/components/Widgets/SelectPreview.js
new file mode 100644
index 00000000..f03b7e64
--- /dev/null
+++ b/src/components/Widgets/SelectPreview.js
@@ -0,0 +1,9 @@
+import React, { PropTypes } from 'react';
+
+export default function SelectPreview({ value }) {
+ return {value ? value.toString() : null};
+}
+
+SelectPreview.propTypes = {
+ value: PropTypes.string,
+};
diff --git a/src/index.js b/src/index.js
index daa0530f..62ec89eb 100644
--- a/src/index.js
+++ b/src/index.js
@@ -39,3 +39,26 @@ for (const method in registry) {
}
window.createClass = React.createClass;
window.h = React.createElement;
+
+const buildtInPlugins = [{
+ label: 'Image',
+ id: 'image',
+ fromBlock: match => match && {
+ image: match[2],
+ alt: match[1],
+ },
+ toBlock: data => `![${ data.alt }](${ data.image })`,
+ toPreview: (data) => {
+ return ;
+ },
+ pattern: /^!\[([^\]]+)\]\(([^\)]+)\)$/,
+ fields: [{
+ label: 'Image',
+ name: 'image',
+ widget: 'image',
+ }, {
+ label: 'Alt Text',
+ name: 'alt',
+ }],
+}];
+buildtInPlugins.forEach(plugin => registry.registerEditorComponent(plugin));
diff --git a/yarn.lock b/yarn.lock
index 2782d87c..b1221d5c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6989,9 +6989,9 @@ react-hot-loader@^3.0.0-beta.2:
redbox-react "^1.2.5"
source-map "^0.4.4"
-react-immutable-proptypes@^1.6.0:
- version "1.7.2"
- resolved "https://registry.yarnpkg.com/react-immutable-proptypes/-/react-immutable-proptypes-1.7.2.tgz#fb1fdca24e30501617732781f4341b704ef7c320"
+react-immutable-proptypes:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz#023d6f39bb15c97c071e9e60d00d136eac5fa0b4"
react-inspector@^1.1.0:
version "1.1.0"