Skip to content

Commit

Permalink
FEATURE (dropdown): allow to Select/clear all (#286)
Browse files Browse the repository at this point in the history
  • Loading branch information
sanusart authored Jul 25, 2023
1 parent 5687a6b commit 0477985
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 84 deletions.
81 changes: 42 additions & 39 deletions README.md

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions docs/src/examples/SelectAll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { Heading } from './components/Heading';
import Select from '../../../src';
import { LiveProvider, LiveEditor, LiveError, LivePreview } from 'react-live';
import theme from 'prism-react-renderer/themes/github';

const code = `<Select
options={options}
multi
selectAll
selectAllLabel="Select all" // this is the default
clearAllLabel="Clear all" // this is the default
values={[]}
onChange={(value) => console.log(value)}
/>`;

const SelectAll = ({ options, title }) => (
<React.Fragment>
<Heading
title={title}
source="https://github.com/sanusart/react-dropdown-select/tree/master/docs/src/examples/Basic.js"
/>

<LiveProvider theme={theme} code={code} scope={{ Select, options }}>
<LiveEditor />
<br />
<LiveError />
<LivePreview />
</LiveProvider>
</React.Fragment>
);

export default SelectAll;
3 changes: 3 additions & 0 deletions docs/src/pages/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ title: 'API'
| backspaceDelete | bool | true | If true, backspace key will delete last value |
| createNewLabel | string | "add {search}" | If create set to true, this will be the label of the "add new" component. `{search}` will be replaced by search value |
| disabledLabel | string | "disabled" | Label shown on disabled field (after) the text |
| selectAll | bool | false | Allow to select all |
| selectAllLabel | string | "Select all" | Label for "Select all" |
| clearAllLabel | string | "Clear all" | Label for "Clear all" |
| additionalProps | object | null | Additional props to pass to Select |

## Callback props
Expand Down
17 changes: 8 additions & 9 deletions docs/src/pages/examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Basic from '../examples/Basic';
import Form from '../examples/Form';
import Windowed from '../examples/Windowed';
import Multi from '../examples/Multi';
import SelectAll from '../examples/SelectAll';
import OpenOnTop from '../examples/OpenOnTop';
import Styled from '../examples/Styled';
import ItemRenderer from '../examples/ItemRenderer';
Expand Down Expand Up @@ -52,6 +53,10 @@ const Examples = () => (
<Multi options={demoOptions} title="Multi" />
</Wrapper>

<Wrapper>
<SelectAll options={demoOptions} title="With select/clear all" />
</Wrapper>

<Wrapper>
<CustomDropdownHandle options={demoOptions} title="Custom dropdown handle" />
</Wrapper>
Expand All @@ -72,25 +77,19 @@ const Examples = () => (
</Wrapper>

<Wrapper>
<CreateEntries
options={demoOptions}
title="Create new Entries"
/>
<CreateEntries options={demoOptions} title="Create new Entries" />
</Wrapper>

<Wrapper>
<Rtl title="Right to left (rtl)" />
</Wrapper>

<Wrapper>
<DropdownAutoPosition options={demoOptions} title="Dropdown auto-position"/>
<DropdownAutoPosition options={demoOptions} title="Dropdown auto-position" />
</Wrapper>

<Wrapper>
<RenderInBody
options={demoOptions}
title="Render dropdown in body"
/>
<RenderInBody options={demoOptions} title="Render dropdown in body" />
</Wrapper>

<Wrapper>
Expand Down
89 changes: 60 additions & 29 deletions src/components/Dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@ import { valueExistInSelected, hexToRGBA, isomorphicWindow } from '../util';
const dropdownPosition = (props, methods) => {
const DropdownBoundingClientRect = methods.getSelectRef().getBoundingClientRect();
const dropdownHeight =
DropdownBoundingClientRect.bottom + parseInt(props.dropdownHeight, 10) + parseInt(props.dropdownGap, 10);
DropdownBoundingClientRect.bottom +
parseInt(props.dropdownHeight, 10) +
parseInt(props.dropdownGap, 10);

if (props.dropdownPosition !== 'auto') {
return props.dropdownPosition;
}

if (dropdownHeight > isomorphicWindow().innerHeight && dropdownHeight > DropdownBoundingClientRect.top) {
if (
dropdownHeight > isomorphicWindow().innerHeight &&
dropdownHeight > DropdownBoundingClientRect.top
) {
return 'top';
}

Expand All @@ -41,35 +46,41 @@ const Dropdown = ({ props, state, methods }) => (
props.dropdownRenderer({ props, state, methods })
) : (
<React.Fragment>
{props.create && state.search && !valueExistInSelected(state.search, [...state.values, ...props.options], props) && (
<AddNew
{props.create &&
state.search &&
!valueExistInSelected(state.search, [...state.values, ...props.options], props) && (
<AddNew
role="button"
className={`${LIB_NAME}-dropdown-add-new`}
color={props.color}
onClick={() => methods.createNew(state.search)}>
{props.createNewLabel.replace('{search}', `"${state.search}"`)}
</AddNew>
)}
{state.searchResults.length === 0 ? (
<NoData className={`${LIB_NAME}-no-data`} state={state} props={props} methods={methods} />
) : (
state.searchResults.map((item, itemIndex) => (
<Item
key={item[props.valueField].toString()}
item={item}
itemIndex={itemIndex}
state={state}
props={props}
methods={methods}
/>
))
)}

{props.selectAll && props.options && props.multi && (
<SelectAll
role="button"
className={`${LIB_NAME}-dropdown-add-new`}
className={`${LIB_NAME}-dropdown-select-all`}
color={props.color}
onClick={() => methods.createNew(state.search)}>
{props.createNewLabel.replace('{search}', `"${state.search}"`)}
</AddNew>
onClick={() => (methods.areAllSelected() ? methods.clearAll() : methods.selectAll())}>
{methods.areAllSelected() ? props.clearAllLabel : props.selectAllLabel}
</SelectAll>
)}
{state.searchResults.length === 0 ? (
<NoData
className={`${LIB_NAME}-no-data`}
state={state}
props={props}
methods={methods}
/>
) : (
state.searchResults
.map((item, itemIndex) => (
<Item
key={item[props.valueField].toString()}
item={item}
itemIndex={itemIndex}
state={state}
props={props}
methods={methods}
/>
))
)}
</React.Fragment>
)}
</DropDown>
Expand All @@ -86,7 +97,11 @@ const DropDown = styled.div`
portal
? `
position: fixed;
${dropdownPosition === 'bottom' ? `top: ${selectBounds.bottom + dropdownGap}px;` : `bottom: ${isomorphicWindow().innerHeight - selectBounds.top + dropdownGap}px;`}
${
dropdownPosition === 'bottom'
? `top: ${selectBounds.bottom + dropdownGap}px;`
: `bottom: ${isomorphicWindow().innerHeight - selectBounds.top + dropdownGap}px;`
}
left: ${selectBounds.left - 1}px;`
: 'left: -1px;'};
border: 1px solid #ccc;
Expand Down Expand Up @@ -118,4 +133,20 @@ const AddNew = styled.div`
}
`;

const SelectAll = styled.div`
color: ${({ color }) => color};
padding: 5px 10px;
position: sticky;
bottom: 0;
margin: 0;
opacity: 1;
background: #fff;
box-shadow: 0 0 10px 0 ${() => hexToRGBA('#000000', 0.2)};
:hover {
outline: none;
cursor: pointer;
}
`;

export default Dropdown;
20 changes: 13 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class Select extends Component {
search: '',
selectBounds: {},
cursor: null,
searchResults: props.options,
searchResults: props.options
};

this.methods = {
Expand Down Expand Up @@ -234,7 +234,7 @@ export class Select extends Component {
return this.setState({
dropdown: false,
search: this.props.clearOnBlur ? '' : this.state.search,
searchResults: this.props.options,
searchResults: this.props.options
});
}

Expand Down Expand Up @@ -298,11 +298,14 @@ export class Select extends Component {
cursor: null
});

this.setState({
search: event.target.value,
}, () => {
this.setState({ searchResults: this.searchResults() })
});
this.setState(
{
search: event.target.value
},
() => {
this.setState({ searchResults: this.searchResults() });
}
);
};

getInputSize = () => {
Expand Down Expand Up @@ -558,6 +561,9 @@ export class Select extends Component {
Select.defaultProps = {
addPlaceholder: '',
placeholder: 'Select...',
selectAll: false,
selectAllLabel: 'Select all',
clearAllLabel: 'Clear all',
values: [],
options: [],
multi: false,
Expand Down

0 comments on commit 0477985

Please sign in to comment.