Skip to content

Commit

Permalink
Locked_items_right_section (#148)
Browse files Browse the repository at this point in the history
* support locked items in destination list

* use locked function instead of item.disabled

* added README & story

* migration to isLocked

* migration to isLocked

* migration to isLocked

* added test coverage

* corected climatcode

* corected climatcode

* corected climatcode
  • Loading branch information
MaksimDrobchak authored and stepovat committed May 19, 2019
1 parent 139cc3b commit e763c1a
Show file tree
Hide file tree
Showing 19 changed files with 520 additions and 83 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class Example extends Component {
| `searchSelectedItemsValue` | `string` | | The value of the search field for destination list. |
| `searchSelectedItemsChanged` | `function` | | Function to handle the change of search field for destination list. Accepts value as a single argument. |
| `selectedItemsFilterFunction` | `function` | based on label | Is the same as filterFunction by default to filter items based on the search query in destination list. |

| `isLocked` | `function` | `item => item.disabled` | Function to be used to define whether item is locked or not|

## Customization

Expand Down Expand Up @@ -168,6 +168,21 @@ The `SelectedItem` component receives the following props:

`height` - receives the height defined by the list


You can disable specific selected items by passing `item.disabled: true` or pass `isLocked` function which will be used to define whether the item is disabled.

Example (selected & disabled):
```javascript
function Exemple(){
const items = [{id:0,label: 'item 0'}, {id:1,label: 'item 1'}];
return (
<MultiSelect
isLocked={item => item.label === 'item 0'}
items={items}
selectedItems={items}
/>
}
```
<br/>
**Search**
Expand Down
7 changes: 5 additions & 2 deletions src/components/destination_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const DestinationList = ({
noItemsRenderer,
withGrouping,
filteredItems,
children
children,
isLocked
}) => {
const SelectionStatusRenderer = selectionStatusRenderer;
const updatedSelectedItems = withGrouping
Expand All @@ -47,6 +48,7 @@ const DestinationList = ({
renderer={selectedItemRenderer}
noItemsRenderer={noItemsRenderer}
noItemsMessage={messages.noItemsMessage}
isLocked={isLocked}
/>
</Column>
);
Expand All @@ -64,7 +66,8 @@ DestinationList.propTypes = {
noItemsRenderer: PropTypes.any,
withGrouping: PropTypes.bool,
filteredItems: PropTypes.arrayOf(PropTypes.object),
children: PropTypes.node
children: PropTypes.node,
isLocked: PropTypes.func
};

DestinationList.defaultProps = {
Expand Down
15 changes: 10 additions & 5 deletions src/components/items/selected_item.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ import PropTypes from "prop-types";
import CloseIcon from "react-icons/lib/md/close";
import IconButton from "@material-ui/core/IconButton";
import ItemLabel from "./item_label";

import classnames from "classnames";
import styles from "./selected_item.scss";

const SelectedItem = ({ item, height, group }) => (
const SelectedItem = ({ item, height, group, disabled }) => (
<div
className={group ? styles.with_grouping : styles.selected_item}
className={classnames({
[styles.with_grouping]: group,
[styles.selected_item]: !group,
[styles.disabled]: disabled
})}
style={{ height }}
>
<ItemLabel label={item.label} />
{!group && (
{!group && !disabled && (
<IconButton>
<CloseIcon />
</IconButton>
Expand All @@ -22,7 +26,8 @@ const SelectedItem = ({ item, height, group }) => (

SelectedItem.propTypes = {
item: PropTypes.object,
height: PropTypes.number
height: PropTypes.number,
isLocked: PropTypes.func
};

SelectedItem.defaultProps = {
Expand Down
6 changes: 6 additions & 0 deletions src/components/items/selected_item.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@
pointer-events: none;
color: $color-black;
}

.disabled {
color: $disabled-color;
background-color: $selected-background-color;
cursor: default;
}
7 changes: 5 additions & 2 deletions src/components/list/items_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class ItemsList extends PureComponent {
selectedIds: PropTypes.arrayOf(PropTypes.number),
items: PropTypes.array,
disabled: PropTypes.bool,
disabledItemsTooltip: PropTypes.string
disabledItemsTooltip: PropTypes.string,
isLocked: PropTypes.func
};

static defaultProps = {
Expand Down Expand Up @@ -61,7 +62,8 @@ class ItemsList extends PureComponent {
selectedIds,
onClick,
disabled,
disabledItemsTooltip
disabledItemsTooltip,
isLocked
} = this.props;
return (
<AutoSizer>
Expand All @@ -84,6 +86,7 @@ class ItemsList extends PureComponent {
selectedIds={selectedIds}
disabled={disabled}
disabledItemsTooltip={disabledItemsTooltip}
isLocked={isLocked}
/>
)}
</AutoSizer>
Expand Down
11 changes: 7 additions & 4 deletions src/components/list/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class InnerList extends PureComponent {
selectedIds: PropTypes.arrayOf(PropTypes.number),
items: PropTypes.array,
disabled: PropTypes.bool,
disabledItemsTooltip: PropTypes.string
disabledItemsTooltip: PropTypes.string,
isLocked: PropTypes.func
};

static defaultProps = {
Expand Down Expand Up @@ -55,12 +56,14 @@ class InnerList extends PureComponent {
onClick,
items,
selectedIds,
disabledItemsTooltip
disabledItemsTooltip,
isLocked
} = this.props;
const Renderer = renderer;
const item = items[index];
const checked = selectedIds.includes(item.id);
const disabled = this.props.disabled || item.disabled;
const disabled = (this.props.disabled && !checked) || isLocked(item);

return (
<div
key={key}
Expand All @@ -74,7 +77,7 @@ class InnerList extends PureComponent {
group={item.isGroup}
height={itemHeight}
checked={checked}
disabled={disabled && !checked}
disabled={disabled}
/>
</div>
);
Expand Down
8 changes: 6 additions & 2 deletions src/components/multi_select.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export class MultiSelect extends PureComponent {
withGrouping: PropTypes.bool,
listRenderer: PropTypes.func,
generateClassName: PropTypes.func,
showSelectedItemsSearch: PropTypes.bool
showSelectedItemsSearch: PropTypes.bool,
isLocked: PropTypes.func
};

static defaultProps = {
Expand Down Expand Up @@ -110,7 +111,8 @@ export class MultiSelect extends PureComponent {
showSelectedItemsSearch,
searchSelectedItemsValue,
filterSelectedItems,
filteredSelectedItems
filteredSelectedItems,
isLocked
} = this.props;
const calculatedHeight = this.calculateHeight();
const selectedIds = selectedItems.map(item => item.id);
Expand Down Expand Up @@ -148,6 +150,7 @@ export class MultiSelect extends PureComponent {
selectAllHeight={selectAllHeight}
withGrouping={withGrouping}
listRenderer={listRenderer}
isLocked={isLocked}
/>
)}
{!loading && showSelectedItems && (
Expand All @@ -167,6 +170,7 @@ export class MultiSelect extends PureComponent {
filteredItems={filteredSelectedItems}
searchIcon={searchIcon}
filterItems={filterSelectedItems}
isLocked={isLocked}
/>
)}
</div>
Expand Down
105 changes: 52 additions & 53 deletions src/components/multi_select_state.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React, { PureComponent } from "react";
import {
getSelectedByAllItems,
filterByIds,
findItem
filterUnselectedByIds,
findItem,
getMinMaxIndexes,
isWithin,
getNewSelectedItems
} from "./multi_select_state_utils";

const withMultiSelectState = WrappedComponent =>
Expand All @@ -13,7 +16,8 @@ const withMultiSelectState = WrappedComponent =>
.toLowerCase()
.includes(value.toLowerCase()),
items: [],
selectedItems: []
selectedItems: [],
isLocked: item => item.disabled
};

constructor(props) {
Expand Down Expand Up @@ -48,10 +52,8 @@ const withMultiSelectState = WrappedComponent =>
this.setState({ selectedItems: nextProps.selectedItems });
}
if (this.props.items !== nextProps.items) {
this.setState({
items: nextProps.items,
filteredItems: nextProps.items
});
const { items } = nextProps;
this.setState({ items, filteredItems: items });
}
if (searchValue !== nextProps.searchValue) {
this.filterItems({ target: { value: nextProps.searchValue } });
Expand All @@ -64,16 +66,16 @@ const withMultiSelectState = WrappedComponent =>
}

handleMultiSelection(index) {
const { items } = this.props;
const { selectedItems, filteredItems } = this.state;
const { items, isLocked } = this.props;
const { filteredItems, firstItemShiftSelected } = this.state;

const { minIndex, maxIndex } = this.getMinMaxIndexes(index);
const interval = getMinMaxIndexes(index, firstItemShiftSelected);
const newSelectedItems = items.filter(
(item, index) =>
(index >= minIndex &&
index <= maxIndex &&
(isWithin(index, interval) &&
!isLocked(item) &&
findItem(item, filteredItems)) ||
findItem(item, selectedItems)
findItem(item, this.state.selectedItems)
);
const newFilteredSelectedItems = this.getNewFilteredSelectedItems(
newSelectedItems
Expand All @@ -87,25 +89,6 @@ const withMultiSelectState = WrappedComponent =>
);
}

getMinMaxIndexes(currentIndex) {
const { firstItemShiftSelected } = this.state;
return firstItemShiftSelected > currentIndex
? { minIndex: currentIndex, maxIndex: firstItemShiftSelected }
: { minIndex: firstItemShiftSelected, maxIndex: currentIndex };
}

getNewSelectedItems(itemId) {
const { items } = this.props;
const { selectedItems } = this.state;
const sourceItems = items.filter(
item => item.id === itemId || findItem(item, selectedItems)
);
const destinationItems = selectedItems.filter(
selectedItem => !findItem(selectedItem, items)
);
return [...destinationItems, ...sourceItems];
}

getNewFilteredSelectedItems(items) {
const { searchSelectedItemsValue } = this.props;

Expand Down Expand Up @@ -140,27 +123,39 @@ const withMultiSelectState = WrappedComponent =>
const index = items.findIndex(item => item.id === id);
this.setState({ firstItemShiftSelected: index });
}
const newSelectedItems = this.getNewSelectedItems(id);
const newFilteredSelectedItems = this.getNewFilteredSelectedItems(
newSelectedItems
);
this.setState(
{
selectedItems: newSelectedItems,
filteredSelectedItems: newFilteredSelectedItems
},
this.handleChange
);
this.setNewItemsBySelectItem(id, items, selectedItems);
}
} else {
this.unselectItems([id]);
}
}

setNewItemsBySelectItem(id, items, selectedItems) {
const newSelectedItems = getNewSelectedItems(id, items, selectedItems);
const newFilteredSelectedItems = this.getNewFilteredSelectedItems(
newSelectedItems
);
this.setState(
{
selectedItems: newSelectedItems,
filteredSelectedItems: newFilteredSelectedItems
},
this.handleChange
);
}
unselectItems(ids) {
const { selectedItems, filteredSelectedItems } = this.state;
const newSelectedItems = filterByIds(selectedItems, ids);
const newFilteredSelectedItems = filterByIds(filteredSelectedItems, ids);
const { isLocked } = this.props;
const newSelectedItems = filterUnselectedByIds(
selectedItems,
ids,
isLocked
);
const newFilteredSelectedItems = filterUnselectedByIds(
filteredSelectedItems,
ids,
isLocked
);
this.setState(
{
selectedItems: newSelectedItems,
Expand All @@ -171,19 +166,21 @@ const withMultiSelectState = WrappedComponent =>
}

clearAll() {
const { selectedItems, isLocked } = this.props;
const lockedItems = selectedItems.filter(isLocked);
this.setState(
{ selectedItems: [], filteredSelectedItems: [] },
{
selectedItems: lockedItems,
filteredSelectedItems: lockedItems
},
this.handleChange
);
}

filterItems(event) {
const { items, filterFunction, searchValueChanged } = this.props;
const { value } = event.target;
this.setState({
filteredItems: items.filter(filterFunction(value))
});

this.setState({ filteredItems: items.filter(filterFunction(value)) });
searchValueChanged && searchValueChanged(value);
}

Expand Down Expand Up @@ -225,11 +222,13 @@ const withMultiSelectState = WrappedComponent =>

isAllSelected() {
const { filteredItems, selectedItems } = this.state;
const selectedItemsInFilteredItems = selectedItems.filter(selectedItem =>
findItem(selectedItem, filteredItems)
const { isLocked } = this.props;
const selectedItemsInFilteredItems = selectedItems.filter(
selectedItem =>
!isLocked(selectedItem) && findItem(selectedItem, filteredItems)
);
const selectableFilteredItems = filteredItems.filter(
item => !item.disabled
item => !isLocked(item)
);
return (
selectedItemsInFilteredItems.length ===
Expand Down
Loading

0 comments on commit e763c1a

Please sign in to comment.