Add collapse all/expand all to Listcontrol (#912)
This commit is contained in:
parent
8849953b50
commit
6ca9c04105
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,31 +251,25 @@ 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}
|
||||||
/>
|
/>
|
||||||
{
|
<SortableList
|
||||||
collapsed ? null :
|
items={items}
|
||||||
<SortableList
|
renderItem={this.renderItem}
|
||||||
items={items}
|
onSortEnd={this.onSortEnd}
|
||||||
renderItem={this.renderItem}
|
useDragHandle
|
||||||
onSortEnd={this.onSortEnd}
|
lockAxis="y"
|
||||||
useDragHandle
|
/>
|
||||||
lockAxis="y"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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>;
|
||||||
|
@ -22,7 +22,10 @@ const config = {
|
|||||||
'arrow': {
|
'arrow': {
|
||||||
direction: 'left',
|
direction: 'left',
|
||||||
},
|
},
|
||||||
'caret': {
|
'chevron': {
|
||||||
|
direction: 'down',
|
||||||
|
},
|
||||||
|
'chevron-double': {
|
||||||
direction: 'down',
|
direction: 'down',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
|
8
src/components/UI/Icon/images/chevron-double.svg
Normal file
8
src/components/UI/Icon/images/chevron-double.svg
Normal 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 |
Before Width: | Height: | Size: 184 B After Width: | Height: | Size: 184 B |
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user