Make list widget sortable
This commit is contained in:
parent
13cbf21159
commit
c23b2fb531
@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -1,3 +1,7 @@
|
||||
:global(.list-item-dragging) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.addButton {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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>
|
||||
{
|
||||
|
@ -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());
|
||||
|
Loading…
x
Reference in New Issue
Block a user