Add collapse all/expand all to Listcontrol (#912)

This commit is contained in:
Christian Nolte 2017-12-22 22:30:05 +01:00 committed by Shawn Erquhart
parent 8849953b50
commit 6ca9c04105
8 changed files with 75 additions and 48 deletions

View File

@ -38,11 +38,16 @@
align-items: center; align-items: center;
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
cursor: pointer;
line-height: 1; line-height: 1;
}
& .nc-icon { .nc-listControl-listCollapseToggleButton{
padding-right: 8px; padding: 4px;
background-color: transparent;
color: inherit;
&:last-of-type {
margin-right: 4px;
} }
} }

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { List, Map, fromJS } from 'immutable'; import { List, Map } from 'immutable';
import { partial } from 'lodash'; import { partial } from 'lodash';
import c from 'classnames'; import c from 'classnames';
import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc'; import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
@ -23,10 +23,12 @@ function valueToString(value) {
const SortableListItem = SortableElement(ListItem); const SortableListItem = SortableElement(ListItem);
const TopBar = ({ onAdd, listLabel, collapsed, onCollapseToggle, itemsCount }) => ( const TopBar = ({ onAdd, listLabel, onCollapseAllToggle, allItemsCollapsed, itemsCount }) => (
<div className="nc-listControl-topBar"> <div className="nc-listControl-topBar">
<div className="nc-listControl-listCollapseToggle" onClick={onCollapseToggle}> <div className="nc-listControl-listCollapseToggle">
<Icon type="caret" direction={collapsed ? 'up' : 'down'} size="small"/> <button className="nc-listControl-listCollapseToggleButton" onClick={onCollapseAllToggle}>
<Icon type="chevron" direction={allItemsCollapsed ? 'up' : 'down'} size="small" />
</button>
{itemsCount} {listLabel} {itemsCount} {listLabel}
</div> </div>
<button className="nc-listControl-addButton" onClick={onAdd}> <button className="nc-listControl-addButton" onClick={onAdd}>
@ -48,11 +50,8 @@ export default class ListControl extends Component {
static propTypes = { static propTypes = {
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
onChangeObject: PropTypes.func.isRequired, onChangeObject: PropTypes.func.isRequired,
value: PropTypes.oneOfType([ value: ImmutablePropTypes.list,
ImmutablePropTypes.list, field: PropTypes.object,
PropTypes.string,
]),
field: PropTypes.node,
forID: PropTypes.string, forID: PropTypes.string,
mediaPaths: ImmutablePropTypes.map.isRequired, mediaPaths: ImmutablePropTypes.map.isRequired,
getAsset: PropTypes.func.isRequired, getAsset: PropTypes.func.isRequired,
@ -64,13 +63,21 @@ export default class ListControl extends Component {
setInactiveStyle: PropTypes.func.isRequired, setInactiveStyle: PropTypes.func.isRequired,
}; };
static defaultProps = {
value: List(),
};
constructor(props) { constructor(props) {
super(props); super(props);
const { field, value } = props;
const allItemsCollapsed = field.get('collapsed') || true;
const itemsCollapsed = Array(value.size).fill(allItemsCollapsed);
this.state = { this.state = {
collapsed: false, itemsCollapsed: List(itemsCollapsed),
itemsCollapsed: List(), value: valueToString(value),
value: valueToString(props.value),
}; };
this.valueType = null; this.valueType = null;
} }
@ -86,6 +93,7 @@ export default class ListControl extends Component {
componentDidMount() { componentDidMount() {
const { field } = this.props; const { field } = this.props;
if (field.get('fields')) { if (field.get('fields')) {
this.valueType = valueTypes.MULTIPLE; this.valueType = valueTypes.MULTIPLE;
} else if (field.get('field')) { } else if (field.get('field')) {
@ -131,7 +139,7 @@ export default class ListControl extends Component {
e.preventDefault(); e.preventDefault();
const { value, onChange } = this.props; const { value, onChange } = this.props;
const parsedValue = (this.valueType === valueTypes.SINGLE) ? null : Map(); const parsedValue = (this.valueType === valueTypes.SINGLE) ? null : Map();
this.setState({ collapsed: false }); this.setState({ itemsCollapsed: this.state.itemsCollapsed.push(false) });
onChange((value || List()).push(parsedValue)); onChange((value || List()).push(parsedValue));
}; };
@ -156,21 +164,28 @@ export default class ListControl extends Component {
handleRemove = (index, event) => { handleRemove = (index, event) => {
event.preventDefault(); event.preventDefault();
const { itemsCollapsed } = this.state;
const { value, metadata, onChange, forID } = this.props; const { value, metadata, onChange, forID } = this.props;
const parsedMetadata = metadata && { [forID]: metadata.removeIn(value.get(index).valueSeq()) }; const parsedMetadata = metadata && { [forID]: metadata.removeIn(value.get(index).valueSeq()) };
onChange(value.remove(index), parsedMetadata);
}
handleCollapseToggle = () => { this.setState({ itemsCollapsed: itemsCollapsed.delete(index) });
this.setState({ collapsed: !this.state.collapsed });
onChange(value.remove(index), parsedMetadata);
} }
handleItemCollapseToggle = (index, event) => { handleItemCollapseToggle = (index, event) => {
event.preventDefault(); event.preventDefault();
const { itemsCollapsed } = this.state; const { itemsCollapsed } = this.state;
this.setState({ const collapsed = itemsCollapsed.get(index);
itemsCollapsed: itemsCollapsed.set(index, !itemsCollapsed.get(index, false)), this.setState({ itemsCollapsed: itemsCollapsed.set(index, !collapsed) });
}); }
handleCollapseAllToggle = (e) => {
e.preventDefault();
const { value } = this.props;
const { itemsCollapsed } = this.state;
const allItemsCollapsed = itemsCollapsed.every(val => val === true);
this.setState({ itemsCollapsed: List(Array(value.size).fill(!allItemsCollapsed)) });
} }
objectLabel(item) { objectLabel(item) {
@ -184,6 +199,7 @@ export default class ListControl extends Component {
onSortEnd = ({ oldIndex, newIndex }) => { onSortEnd = ({ oldIndex, newIndex }) => {
const { value, onChange } = this.props; const { value, onChange } = this.props;
const { itemsCollapsed } = this.state;
// Update value // Update value
const item = value.get(oldIndex); const item = value.get(oldIndex);
@ -191,10 +207,9 @@ export default class ListControl extends Component {
this.props.onChange(newValue); this.props.onChange(newValue);
// Update collapsing // Update collapsing
const { itemsCollapsed } = this.state;
const collapsed = itemsCollapsed.get(oldIndex); const collapsed = itemsCollapsed.get(oldIndex);
const newItemsCollapsed = itemsCollapsed.delete(oldIndex).insert(newIndex, collapsed); const updatedItemsCollapsed = itemsCollapsed.delete(oldIndex).insert(newIndex, collapsed);
this.setState({ itemsCollapsed: newItemsCollapsed }); this.setState({ itemsCollapsed: updatedItemsCollapsed });
}; };
renderItem = (item, index) => { renderItem = (item, index) => {
@ -236,23 +251,18 @@ export default class ListControl extends Component {
renderListControl() { renderListControl() {
const { value, forID, field, classNameWrapper } = this.props; const { value, forID, field, classNameWrapper } = this.props;
const { collapsed } = this.state; const { itemsCollapsed } = this.state;
const items = value || List(); const items = value || List();
const className = c(classNameWrapper, 'nc-listControl', {
'nc-listControl-collapsed' : collapsed,
});
return ( return (
<div id={forID} className={className}> <div id={forID} className={c(classNameWrapper, 'nc-listControl')}>
<TopBar <TopBar
onAdd={this.handleAdd} onAdd={this.handleAdd}
listLabel={field.get('label').toLowerCase()} listLabel={field.get('label').toLowerCase()}
onCollapseToggle={this.handleCollapseToggle} onCollapseAllToggle={this.handleCollapseAllToggle}
collapsed={collapsed} allItemsCollapsed={itemsCollapsed.every(val => val === true)}
itemsCount={items.size} itemsCount={items.size}
/> />
{
collapsed ? null :
<SortableList <SortableList
items={items} items={items}
renderItem={this.renderItem} renderItem={this.renderItem}
@ -260,7 +270,6 @@ export default class ListControl extends Component {
useDragHandle useDragHandle
lockAxis="y" lockAxis="y"
/> />
}
</div> </div>
); );
} }

View File

@ -10,7 +10,7 @@ import EditorControl from 'Editor/EditorControlPane/EditorControl';
const TopBar = ({ collapsed, onCollapseToggle }) => const TopBar = ({ collapsed, onCollapseToggle }) =>
<div className="nc-listControl-topBar"> <div className="nc-listControl-topBar">
<div className="nc-listControl-listCollapseToggle" onClick={onCollapseToggle}> <div className="nc-listControl-listCollapseToggle" onClick={onCollapseToggle}>
<Icon type="caret" direction={collapsed ? 'up' : 'down'} size="small"/> <Icon type="chevron" direction={collapsed ? 'up' : 'down'} size="small"/>
{itemsCount} {listLabel} {itemsCount} {listLabel}
</div> </div>
</div>; </div>;

View File

@ -22,7 +22,10 @@ const config = {
'arrow': { 'arrow': {
direction: 'left', direction: 'left',
}, },
'caret': { 'chevron': {
direction: 'down',
},
'chevron-double': {
direction: 'down', direction: 'down',
} }
}; };

View File

@ -2,8 +2,9 @@ import iconAdd from './add.svg';
import iconAddWith from './add-with.svg'; import iconAddWith from './add-with.svg';
import iconArrow from './arrow.svg'; import iconArrow from './arrow.svg';
import iconBold from './bold.svg'; import iconBold from './bold.svg';
import iconCaret from './caret.svg';
import iconCheck from './check.svg'; import iconCheck from './check.svg';
import iconChevron from './chevron.svg';
import iconChevronDouble from './chevron-double.svg';
import iconCircle from './circle.svg'; import iconCircle from './circle.svg';
import iconClose from './close.svg'; import iconClose from './close.svg';
import iconCode from './code.svg'; import iconCode from './code.svg';
@ -43,8 +44,9 @@ const images = {
'add-with': iconAddWith, 'add-with': iconAddWith,
'arrow': iconArrow, 'arrow': iconArrow,
'bold': iconBold, 'bold': iconBold,
'caret': iconCaret,
'check': iconCheck, 'check': iconCheck,
'chevron': iconChevron,
'chevron-double': iconChevronDouble,
'circle': iconCircle, 'circle': iconCircle,
'close': iconClose, 'close': iconClose,
'code': iconCode, 'code': iconCode,

View File

@ -0,0 +1,8 @@
<svg viewBox="0 0 24 24">
<g id="caret" transform="translate(2.000000, 3.000000)" fill-rule="nonzero">
<polygon points="3.1231456 0.32943568 0.86323447 2.46718624 9.5186981 11.6172615 18.8632345 2.5123409 16.6923073 0.28428102 9.6090173 7.1859389"/>
</g>
<g id="caret" transform="translate(2.000000, 10.000000)" fill-rule="nonzero">
<polygon points="3.1231456 0.32943568 0.86323447 2.46718624 9.5186981 11.6172615 18.8632345 2.5123409 16.6923073 0.28428102 9.6090173 7.1859389"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 511 B

View File

Before

Width:  |  Height:  |  Size: 184 B

After

Width:  |  Height:  |  Size: 184 B

View File

@ -15,7 +15,7 @@ export const ListItemTopBar = ({ collapsed, onCollapseToggle, onRemove, dragHand
{ {
onCollapseToggle onCollapseToggle
? <button className="nc-listItemTopBar-toggleButton" onClick={onCollapseToggle}> ? <button className="nc-listItemTopBar-toggleButton" onClick={onCollapseToggle}>
<Icon type="caret" size="small" direction={collapsed ? 'up' : 'down'}/> <Icon type="chevron" size="small" direction={collapsed ? 'up' : 'down'}/>
</button> </button>
: null : null
} }