diff --git a/src/components/EditorWidgets/List/List.css b/src/components/EditorWidgets/List/List.css index 441299cc..6e5cbdd8 100644 --- a/src/components/EditorWidgets/List/List.css +++ b/src/components/EditorWidgets/List/List.css @@ -38,11 +38,16 @@ align-items: center; font-size: 14px; font-weight: 500; - cursor: pointer; line-height: 1; +} - & .nc-icon { - padding-right: 8px; +.nc-listControl-listCollapseToggleButton{ + padding: 4px; + background-color: transparent; + color: inherit; + + &:last-of-type { + margin-right: 4px; } } diff --git a/src/components/EditorWidgets/List/ListControl.js b/src/components/EditorWidgets/List/ListControl.js index 1399697b..e1b39f3f 100644 --- a/src/components/EditorWidgets/List/ListControl.js +++ b/src/components/EditorWidgets/List/ListControl.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { List, Map, fromJS } from 'immutable'; +import { List, Map } from 'immutable'; import { partial } from 'lodash'; import c from 'classnames'; import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc'; @@ -23,10 +23,12 @@ function valueToString(value) { 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-listCollapseToggle" onClick={onCollapseToggle}> - <Icon type="caret" direction={collapsed ? 'up' : 'down'} size="small"/> + <div className="nc-listControl-listCollapseToggle"> + <button className="nc-listControl-listCollapseToggleButton" onClick={onCollapseAllToggle}> + <Icon type="chevron" direction={allItemsCollapsed ? 'up' : 'down'} size="small" /> + </button> {itemsCount} {listLabel} </div> <button className="nc-listControl-addButton" onClick={onAdd}> @@ -48,11 +50,8 @@ export default class ListControl extends Component { static propTypes = { onChange: PropTypes.func.isRequired, onChangeObject: PropTypes.func.isRequired, - value: PropTypes.oneOfType([ - ImmutablePropTypes.list, - PropTypes.string, - ]), - field: PropTypes.node, + value: ImmutablePropTypes.list, + field: PropTypes.object, forID: PropTypes.string, mediaPaths: ImmutablePropTypes.map.isRequired, getAsset: PropTypes.func.isRequired, @@ -64,13 +63,21 @@ export default class ListControl extends Component { setInactiveStyle: PropTypes.func.isRequired, }; + static defaultProps = { + value: List(), + }; + constructor(props) { super(props); + const { field, value } = props; + const allItemsCollapsed = field.get('collapsed') || true; + const itemsCollapsed = Array(value.size).fill(allItemsCollapsed); + this.state = { - collapsed: false, - itemsCollapsed: List(), - value: valueToString(props.value), + itemsCollapsed: List(itemsCollapsed), + value: valueToString(value), }; + this.valueType = null; } @@ -86,6 +93,7 @@ export default class ListControl extends Component { componentDidMount() { const { field } = this.props; + if (field.get('fields')) { this.valueType = valueTypes.MULTIPLE; } else if (field.get('field')) { @@ -131,7 +139,7 @@ export default class ListControl extends Component { e.preventDefault(); const { value, onChange } = this.props; 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)); }; @@ -156,21 +164,28 @@ export default class ListControl extends Component { handleRemove = (index, event) => { event.preventDefault(); + const { itemsCollapsed } = this.state; const { value, metadata, onChange, forID } = this.props; const parsedMetadata = metadata && { [forID]: metadata.removeIn(value.get(index).valueSeq()) }; - onChange(value.remove(index), parsedMetadata); - } - handleCollapseToggle = () => { - this.setState({ collapsed: !this.state.collapsed }); + this.setState({ itemsCollapsed: itemsCollapsed.delete(index) }); + + onChange(value.remove(index), parsedMetadata); } handleItemCollapseToggle = (index, event) => { event.preventDefault(); const { itemsCollapsed } = this.state; - this.setState({ - itemsCollapsed: itemsCollapsed.set(index, !itemsCollapsed.get(index, false)), - }); + const collapsed = itemsCollapsed.get(index); + 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) { @@ -184,6 +199,7 @@ export default class ListControl extends Component { onSortEnd = ({ oldIndex, newIndex }) => { const { value, onChange } = this.props; + const { itemsCollapsed } = this.state; // Update value const item = value.get(oldIndex); @@ -191,10 +207,9 @@ export default class ListControl extends Component { this.props.onChange(newValue); // Update collapsing - const { itemsCollapsed } = this.state; const collapsed = itemsCollapsed.get(oldIndex); - const newItemsCollapsed = itemsCollapsed.delete(oldIndex).insert(newIndex, collapsed); - this.setState({ itemsCollapsed: newItemsCollapsed }); + const updatedItemsCollapsed = itemsCollapsed.delete(oldIndex).insert(newIndex, collapsed); + this.setState({ itemsCollapsed: updatedItemsCollapsed }); }; renderItem = (item, index) => { @@ -236,31 +251,25 @@ export default class ListControl extends Component { renderListControl() { const { value, forID, field, classNameWrapper } = this.props; - const { collapsed } = this.state; + const { itemsCollapsed } = this.state; const items = value || List(); - const className = c(classNameWrapper, 'nc-listControl', { - 'nc-listControl-collapsed' : collapsed, - }); return ( - <div id={forID} className={className}> + <div id={forID} className={c(classNameWrapper, 'nc-listControl')}> <TopBar onAdd={this.handleAdd} listLabel={field.get('label').toLowerCase()} - onCollapseToggle={this.handleCollapseToggle} - collapsed={collapsed} + onCollapseAllToggle={this.handleCollapseAllToggle} + allItemsCollapsed={itemsCollapsed.every(val => val === true)} itemsCount={items.size} /> - { - collapsed ? null : - <SortableList - items={items} - renderItem={this.renderItem} - onSortEnd={this.onSortEnd} - useDragHandle - lockAxis="y" - /> - } + <SortableList + items={items} + renderItem={this.renderItem} + onSortEnd={this.onSortEnd} + useDragHandle + lockAxis="y" + /> </div> ); } diff --git a/src/components/EditorWidgets/Object/ObjectControl.js b/src/components/EditorWidgets/Object/ObjectControl.js index 4b46ba54..c7699376 100644 --- a/src/components/EditorWidgets/Object/ObjectControl.js +++ b/src/components/EditorWidgets/Object/ObjectControl.js @@ -10,7 +10,7 @@ import EditorControl from 'Editor/EditorControlPane/EditorControl'; const TopBar = ({ collapsed, onCollapseToggle }) => <div className="nc-listControl-topBar"> <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} </div> </div>; diff --git a/src/components/UI/Icon/icons.js b/src/components/UI/Icon/icons.js index c43017ef..6a71771f 100644 --- a/src/components/UI/Icon/icons.js +++ b/src/components/UI/Icon/icons.js @@ -22,7 +22,10 @@ const config = { 'arrow': { direction: 'left', }, - 'caret': { + 'chevron': { + direction: 'down', + }, + 'chevron-double': { direction: 'down', } }; diff --git a/src/components/UI/Icon/images/_index.js b/src/components/UI/Icon/images/_index.js index 818656a4..f974d873 100644 --- a/src/components/UI/Icon/images/_index.js +++ b/src/components/UI/Icon/images/_index.js @@ -2,8 +2,9 @@ import iconAdd from './add.svg'; import iconAddWith from './add-with.svg'; import iconArrow from './arrow.svg'; import iconBold from './bold.svg'; -import iconCaret from './caret.svg'; import iconCheck from './check.svg'; +import iconChevron from './chevron.svg'; +import iconChevronDouble from './chevron-double.svg'; import iconCircle from './circle.svg'; import iconClose from './close.svg'; import iconCode from './code.svg'; @@ -43,8 +44,9 @@ const images = { 'add-with': iconAddWith, 'arrow': iconArrow, 'bold': iconBold, - 'caret': iconCaret, 'check': iconCheck, + 'chevron': iconChevron, + 'chevron-double': iconChevronDouble, 'circle': iconCircle, 'close': iconClose, 'code': iconCode, diff --git a/src/components/UI/Icon/images/chevron-double.svg b/src/components/UI/Icon/images/chevron-double.svg new file mode 100644 index 00000000..4df690a0 --- /dev/null +++ b/src/components/UI/Icon/images/chevron-double.svg @@ -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> \ No newline at end of file diff --git a/src/components/UI/Icon/images/caret.svg b/src/components/UI/Icon/images/chevron.svg similarity index 100% rename from src/components/UI/Icon/images/caret.svg rename to src/components/UI/Icon/images/chevron.svg diff --git a/src/components/UI/ListItemTopBar/ListItemTopBar.js b/src/components/UI/ListItemTopBar/ListItemTopBar.js index 297511a8..5d7f3d46 100644 --- a/src/components/UI/ListItemTopBar/ListItemTopBar.js +++ b/src/components/UI/ListItemTopBar/ListItemTopBar.js @@ -15,7 +15,7 @@ export const ListItemTopBar = ({ collapsed, onCollapseToggle, onRemove, dragHand { 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> : null }