Make list widget sortable
This commit is contained in:
@ -33,7 +33,7 @@
|
|||||||
content: '{"site_title": "CMS Demo"}'
|
content: '{"site_title": "CMS Demo"}'
|
||||||
},
|
},
|
||||||
"authors.yml": {
|
"authors.yml": {
|
||||||
content: 'authors:\n - name: Mathias\n description: Co-founder @ Netlify\n'
|
content: 'authors:\n - name: Mathias\n description: Co-founder @ Netlify\n - name: Chris\n description: Co-founder @ Netlify\n'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,6 @@
|
|||||||
"@kadira/storybook": "^1.36.0",
|
"@kadira/storybook": "^1.36.0",
|
||||||
"autoprefixer": "^6.3.3",
|
"autoprefixer": "^6.3.3",
|
||||||
"bricks.js": "^1.7.0",
|
"bricks.js": "^1.7.0",
|
||||||
"textarea-caret-position": "^0.1.1",
|
|
||||||
"dateformat": "^1.0.12",
|
"dateformat": "^1.0.12",
|
||||||
"fuzzy": "^0.1.1",
|
"fuzzy": "^0.1.1",
|
||||||
"immutability-helper": "^2.0.0",
|
"immutability-helper": "^2.0.0",
|
||||||
@ -120,6 +119,7 @@
|
|||||||
"react-router": "^2.5.1",
|
"react-router": "^2.5.1",
|
||||||
"react-router-redux": "^4.0.5",
|
"react-router-redux": "^4.0.5",
|
||||||
"react-simple-dnd": "^0.1.2",
|
"react-simple-dnd": "^0.1.2",
|
||||||
|
"react-sortable": "^1.2.0",
|
||||||
"react-toolbox": "^1.2.1",
|
"react-toolbox": "^1.2.1",
|
||||||
"react-topbar-progress-indicator": "^1.0.0",
|
"react-topbar-progress-indicator": "^1.0.0",
|
||||||
"react-waypoint": "^3.1.3",
|
"react-waypoint": "^3.1.3",
|
||||||
@ -131,6 +131,7 @@
|
|||||||
"semaphore": "^1.0.5",
|
"semaphore": "^1.0.5",
|
||||||
"slate": "^0.14.14",
|
"slate": "^0.14.14",
|
||||||
"slate-drop-or-paste-images": "^0.2.0",
|
"slate-drop-or-paste-images": "^0.2.0",
|
||||||
|
"textarea-caret-position": "^0.1.1",
|
||||||
"uuid": "^2.0.3",
|
"uuid": "^2.0.3",
|
||||||
"whatwg-fetch": "^1.0.0"
|
"whatwg-fetch": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
:global(.list-item-dragging) {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
.addButton {
|
.addButton {
|
||||||
display: block;
|
display: block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -1,17 +1,30 @@
|
|||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { List, Map } from 'immutable';
|
import { List, Map, fromJS } from 'immutable';
|
||||||
|
import { sortable } from 'react-sortable';
|
||||||
import ObjectControl from './ObjectControl';
|
import ObjectControl from './ObjectControl';
|
||||||
import styles from './ListControl.css';
|
import styles from './ListControl.css';
|
||||||
|
|
||||||
|
function ListItem(props) {
|
||||||
|
return <div {...props} className={`list-item ${ props.className }`}>{props.children}</div>;
|
||||||
|
}
|
||||||
|
ListItem.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
children: PropTypes.node,
|
||||||
|
};
|
||||||
|
ListItem.displayName = 'list-item';
|
||||||
|
|
||||||
|
const SortableListItem = sortable(ListItem);
|
||||||
|
|
||||||
export default class ListControl extends Component {
|
export default class ListControl extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
value: PropTypes.node,
|
value: PropTypes.node,
|
||||||
|
field: PropTypes.node,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {itemStates: Map()};
|
this.state = { itemStates: Map() };
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = (e) => {
|
handleChange = (e) => {
|
||||||
@ -55,36 +68,52 @@ export default class ListControl extends Component {
|
|||||||
const fields = field.get('fields');
|
const fields = field.get('fields');
|
||||||
const first = fields.first();
|
const first = fields.first();
|
||||||
const value = item.get(first.get('name'));
|
const value = item.get(first.get('name'));
|
||||||
return value || `No ${first.get('name')}`;
|
return value || `No ${ first.get('name') }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSort = (obj) => {
|
||||||
|
this.setState({ draggingIndex: obj.draggingIndex });
|
||||||
|
if ('items' in obj) {
|
||||||
|
this.props.onChange(fromJS(obj.items));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
renderItem(item, index) {
|
renderItem(item, index) {
|
||||||
const { field, getMedia, onAddMedia, onRemoveMedia } = this.props;
|
const { value, field, getMedia, onAddMedia, onRemoveMedia } = this.props;
|
||||||
const { itemStates } = this.state;
|
const { itemStates, draggedItem } = this.state;
|
||||||
const collapsed = itemStates.getIn([index, 'collapsed']);
|
const collapsed = itemStates.getIn([index, 'collapsed']);
|
||||||
const classNames = [styles.item, collapsed ? styles.collapsed : styles.expanded];
|
const classNames = [styles.item, collapsed ? styles.collapsed : styles.expanded];
|
||||||
|
|
||||||
return (<div key={index} className={classNames.join(' ')}>
|
return (<SortableListItem
|
||||||
<div className={styles.objectLabel}>{this.objectLabel(item)}</div>
|
key={index}
|
||||||
<div className={styles.objectControl}>
|
updateState={this.handleSort}
|
||||||
<ObjectControl
|
items={value ? value.toJS() : []}
|
||||||
value={item}
|
draggingIndex={this.state.draggingIndex}
|
||||||
field={field}
|
sortId={index}
|
||||||
onChange={this.handleChangeFor(index)}
|
outline="list"
|
||||||
getMedia={getMedia}
|
>
|
||||||
onAddMedia={onAddMedia}
|
<div className={classNames.join(' ')}>
|
||||||
onRemoveMedia={onRemoveMedia}
|
<div className={styles.objectLabel}>{this.objectLabel(item)}</div>
|
||||||
/>
|
<div className={styles.objectControl}>
|
||||||
|
<ObjectControl
|
||||||
|
value={item}
|
||||||
|
field={field}
|
||||||
|
onChange={this.handleChangeFor(index)}
|
||||||
|
getMedia={getMedia}
|
||||||
|
onAddMedia={onAddMedia}
|
||||||
|
onRemoveMedia={onRemoveMedia}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button className={styles.toggleButton} onClick={this.handleToggle(index)}>
|
||||||
|
{collapsed ? '+' : '-'}
|
||||||
|
</button>
|
||||||
|
<button className={styles.removeButton} onClick={this.handleRemove(index)}>x</button>
|
||||||
</div>
|
</div>
|
||||||
<button className={styles.toggleButton} onClick={this.handleToggle(index)}>
|
</SortableListItem>);
|
||||||
{collapsed ? '+' : '-'}
|
|
||||||
</button>
|
|
||||||
<button className={styles.removeButton} onClick={this.handleRemove(index)}>x</button>
|
|
||||||
</div>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderListControl() {
|
renderListControl() {
|
||||||
const { value } = this.props;
|
const { value, field } = this.props;
|
||||||
return (<div>
|
return (<div>
|
||||||
{value && value.map((item, index) => this.renderItem(item, index))}
|
{value && value.map((item, index) => this.renderItem(item, index))}
|
||||||
<div><button className={styles.addButton} onClick={this.handleAdd}>new</button></div>
|
<div><button className={styles.addButton} onClick={this.handleAdd}>new</button></div>
|
||||||
@ -93,7 +122,6 @@ export default class ListControl extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { value, field } = this.props;
|
const { value, field } = this.props;
|
||||||
console.log('field: %o', field.toJS());
|
|
||||||
|
|
||||||
if (field.get('fields')) {
|
if (field.get('fields')) {
|
||||||
return this.renderListControl();
|
return this.renderListControl();
|
||||||
|
@ -1,11 +1,33 @@
|
|||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes, Component } from 'react';
|
||||||
|
import { resolveWidget } from '../Widgets';
|
||||||
|
|
||||||
export default function ListPreview({ value }) {
|
export default class ObjectPreview extends Component {
|
||||||
return (<ul>
|
widgetFor = (field, value) => {
|
||||||
{ value && value.map(item => <li key={item}>{item}</li>) }
|
const { getMedia } = this.props;
|
||||||
</ul>);
|
const widget = resolveWidget(field.get('widget'));
|
||||||
|
return (<div key={field.get('name')}>{React.createElement(widget.preview, {
|
||||||
|
key: field.get('name'),
|
||||||
|
value: value && value.get(field.get('name')),
|
||||||
|
field,
|
||||||
|
getMedia,
|
||||||
|
})}</div>);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { field, value } = this.props;
|
||||||
|
const fields = field && field.get('fields');
|
||||||
|
if (fields) {
|
||||||
|
return value ? (<div>{value.map((val, index) => <div key={index}>
|
||||||
|
{fields && fields.map(f => this.widgetFor(f, val))}
|
||||||
|
</div>)}</div>) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value ? value.join(', ') : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListPreview.propTypes = {
|
ObjectPreview.propTypes = {
|
||||||
value: PropTypes.node,
|
value: PropTypes.node,
|
||||||
|
field: PropTypes.node,
|
||||||
|
getMedia: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -18,7 +18,7 @@ export default class ObjectControl extends Component {
|
|||||||
const widget = resolveWidget(field.get('widget') || 'string');
|
const widget = resolveWidget(field.get('widget') || 'string');
|
||||||
const fieldValue = value && value.get(field.get('name'));
|
const fieldValue = value && value.get(field.get('name'));
|
||||||
|
|
||||||
return (<div className={controlStyles.widget}>
|
return (<div className={controlStyles.widget} key={field.get('name')}>
|
||||||
<div className={controlStyles.control} key={field.get('name')}>
|
<div className={controlStyles.control} key={field.get('name')}>
|
||||||
<label className={controlStyles.label}>{field.get('label')}</label>
|
<label className={controlStyles.label}>{field.get('label')}</label>
|
||||||
{
|
{
|
||||||
|
@ -33,7 +33,6 @@ export function resolveFormat(collectionOrEntity, entry) {
|
|||||||
if (typeof collectionOrEntity === 'string') {
|
if (typeof collectionOrEntity === 'string') {
|
||||||
return formatByType(collectionOrEntity);
|
return formatByType(collectionOrEntity);
|
||||||
}
|
}
|
||||||
console.log('entry: %o', entry);
|
|
||||||
const path = entry && entry.path;
|
const path = entry && entry.path;
|
||||||
if (path) {
|
if (path) {
|
||||||
return formatByExtension(path.split('.').pop());
|
return formatByExtension(path.split('.').pop());
|
||||||
|
Reference in New Issue
Block a user