Make list widget sortable

This commit is contained in:
Mathias Biilmann Christensen 2016-10-30 16:01:10 -07:00
parent 13cbf21159
commit c23b2fb531
7 changed files with 87 additions and 33 deletions

View File

@ -33,7 +33,7 @@
content: '{"site_title": "CMS Demo"}'
},
"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'
}
}
}

View File

@ -91,7 +91,6 @@
"@kadira/storybook": "^1.36.0",
"autoprefixer": "^6.3.3",
"bricks.js": "^1.7.0",
"textarea-caret-position": "^0.1.1",
"dateformat": "^1.0.12",
"fuzzy": "^0.1.1",
"immutability-helper": "^2.0.0",
@ -120,6 +119,7 @@
"react-router": "^2.5.1",
"react-router-redux": "^4.0.5",
"react-simple-dnd": "^0.1.2",
"react-sortable": "^1.2.0",
"react-toolbox": "^1.2.1",
"react-topbar-progress-indicator": "^1.0.0",
"react-waypoint": "^3.1.3",
@ -131,6 +131,7 @@
"semaphore": "^1.0.5",
"slate": "^0.14.14",
"slate-drop-or-paste-images": "^0.2.0",
"textarea-caret-position": "^0.1.1",
"uuid": "^2.0.3",
"whatwg-fetch": "^1.0.0"
},

View File

@ -1,3 +1,7 @@
:global(.list-item-dragging) {
opacity: 0.5;
}
.addButton {
display: block;
cursor: pointer;

View File

@ -1,17 +1,30 @@
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 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 {
static propTypes = {
onChange: PropTypes.func.isRequired,
value: PropTypes.node,
field: PropTypes.node,
};
constructor(props) {
super(props);
this.state = {itemStates: Map()};
this.state = { itemStates: Map() };
}
handleChange = (e) => {
@ -55,36 +68,52 @@ export default class ListControl extends Component {
const fields = field.get('fields');
const first = fields.first();
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) {
const { field, getMedia, onAddMedia, onRemoveMedia } = this.props;
const { itemStates } = this.state;
const { value, field, getMedia, onAddMedia, onRemoveMedia } = this.props;
const { itemStates, draggedItem } = this.state;
const collapsed = itemStates.getIn([index, 'collapsed']);
const classNames = [styles.item, collapsed ? styles.collapsed : styles.expanded];
return (<div key={index} className={classNames.join(' ')}>
<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}
/>
return (<SortableListItem
key={index}
updateState={this.handleSort}
items={value ? value.toJS() : []}
draggingIndex={this.state.draggingIndex}
sortId={index}
outline="list"
>
<div className={classNames.join(' ')}>
<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>
<button className={styles.toggleButton} onClick={this.handleToggle(index)}>
{collapsed ? '+' : '-'}
</button>
<button className={styles.removeButton} onClick={this.handleRemove(index)}>x</button>
</div>);
</SortableListItem>);
}
renderListControl() {
const { value } = this.props;
const { value, field } = this.props;
return (<div>
{value && value.map((item, index) => this.renderItem(item, index))}
<div><button className={styles.addButton} onClick={this.handleAdd}>new</button></div>
@ -93,7 +122,6 @@ export default class ListControl extends Component {
render() {
const { value, field } = this.props;
console.log('field: %o', field.toJS());
if (field.get('fields')) {
return this.renderListControl();

View File

@ -1,11 +1,33 @@
import React, { PropTypes } from 'react';
import React, { PropTypes, Component } from 'react';
import { resolveWidget } from '../Widgets';
export default function ListPreview({ value }) {
return (<ul>
{ value && value.map(item => <li key={item}>{item}</li>) }
</ul>);
export default class ObjectPreview extends Component {
widgetFor = (field, value) => {
const { getMedia } = this.props;
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,
field: PropTypes.node,
getMedia: PropTypes.func.isRequired,
};

View File

@ -18,7 +18,7 @@ export default class ObjectControl extends Component {
const widget = resolveWidget(field.get('widget') || 'string');
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')}>
<label className={controlStyles.label}>{field.get('label')}</label>
{

View File

@ -33,7 +33,6 @@ 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());