mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
feat: add filter field to suggest select field.
This commit is contained in:
@@ -17,11 +17,11 @@ export default function ContactsMultiSelect({
|
||||
contactsSelected = [],
|
||||
...multiSelectProps
|
||||
}) {
|
||||
const [localSelected, setLocalSelected] = useState([ ...contactsSelected]);
|
||||
const [localSelected, setLocalSelected] = useState([...contactsSelected]);
|
||||
|
||||
// Detarmines the given id is selected.
|
||||
const isItemSelected = useCallback(
|
||||
(id) => localSelected.some(s => s === id),
|
||||
(id) => localSelected.some((s) => s === id),
|
||||
[localSelected],
|
||||
);
|
||||
|
||||
@@ -45,15 +45,30 @@ export default function ContactsMultiSelect({
|
||||
const handleItemSelect = useCallback(
|
||||
({ id }) => {
|
||||
const selected = isItemSelected(id)
|
||||
? localSelected.filter(s => s !== id)
|
||||
? localSelected.filter((s) => s !== id)
|
||||
: [...localSelected, id];
|
||||
|
||||
setLocalSelected([ ...selected ]);
|
||||
|
||||
setLocalSelected([...selected]);
|
||||
safeInvoke(onContactSelect, selected);
|
||||
},
|
||||
[setLocalSelected, localSelected, isItemSelected, onContactSelect],
|
||||
);
|
||||
|
||||
// Filters accounts items.
|
||||
const filterContactsPredicater = useCallback(
|
||||
(query, contact, _index, exactMatch) => {
|
||||
const normalizedTitle = contact.display_name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return normalizedTitle.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
items={contacts}
|
||||
@@ -62,6 +77,7 @@ export default function ContactsMultiSelect({
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={true}
|
||||
onItemSelect={handleItemSelect}
|
||||
itemPredicate={filterContactsPredicater}
|
||||
{...multiSelectProps}
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -31,6 +31,7 @@ export function ItemsMultiSelect({
|
||||
<MenuItem
|
||||
icon={isItemSelected(item.id) ? 'tick' : 'blank'}
|
||||
text={item.name}
|
||||
label={item.code}
|
||||
key={item.id}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
@@ -54,6 +55,21 @@ export function ItemsMultiSelect({
|
||||
[setLocalSelected, localSelected, isItemSelected, onItemSelect],
|
||||
);
|
||||
|
||||
// Filters accounts items.
|
||||
const filterItemsPredicater = useCallback(
|
||||
(query, item, _index, exactMatch) => {
|
||||
const normalizedTitle = item.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return `${normalizedTitle} ${item.code}`.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
items={items}
|
||||
@@ -62,6 +78,7 @@ export function ItemsMultiSelect({
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={true}
|
||||
onItemSelect={handleItemSelect}
|
||||
itemPredicate={filterItemsPredicater}
|
||||
{...multiSelectProps}
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -83,7 +83,7 @@ export default function ItemsSuggestField({
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return normalizedTitle.indexOf(normalizedQuery) >= 0;
|
||||
return `${normalizedTitle} ${item.code}`.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
},
|
||||
[],
|
||||
|
||||
@@ -21,6 +21,9 @@ import {
|
||||
Popover,
|
||||
Position,
|
||||
Utils,
|
||||
InputGroup,
|
||||
Button,
|
||||
refHandler,
|
||||
} from '@blueprintjs/core';
|
||||
import { QueryList } from '@blueprintjs/select';
|
||||
|
||||
@@ -61,6 +64,13 @@ import { QueryList } from '@blueprintjs/select';
|
||||
// }
|
||||
|
||||
export default class MultiSelect extends React.Component {
|
||||
inputElement = null;
|
||||
handleInputRef = refHandler(
|
||||
this,
|
||||
'inputElement',
|
||||
this.props.inputProps?.inputRef,
|
||||
);
|
||||
|
||||
static get displayName() {
|
||||
return `${DISPLAYNAME_PREFIX}.MultiSelect`;
|
||||
}
|
||||
@@ -92,8 +102,7 @@ export default class MultiSelect extends React.Component {
|
||||
|
||||
render() {
|
||||
// omit props specific to this component, spread the rest.
|
||||
const { openOnKeyDown, popoverProps, tagInputProps, ...restProps } =
|
||||
this.props;
|
||||
const { openOnKeyDown, popoverProps, ...restProps } = this.props;
|
||||
|
||||
return (
|
||||
<QueryList
|
||||
@@ -106,20 +115,36 @@ export default class MultiSelect extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
maybeRenderClearButton(query) {
|
||||
return query.length > 0 ? (
|
||||
<Button icon="cross" minimal={true} onClick={this.resetQuery} />
|
||||
) : undefined;
|
||||
}
|
||||
|
||||
renderQueryList(listProps) {
|
||||
const { fill, tagInputProps = {}, popoverProps = {} } = this.props;
|
||||
const { filterable, fill, inputProps = {}, popoverProps = {} } = this.props;
|
||||
const { handleKeyDown, handleKeyUp } = listProps;
|
||||
|
||||
if (fill) {
|
||||
popoverProps.fill = true;
|
||||
tagInputProps.fill = true;
|
||||
}
|
||||
|
||||
// add our own inputProps.className so that we can reference it in event handlers
|
||||
const { inputProps = {} } = tagInputProps;
|
||||
inputProps.className = classNames(
|
||||
inputProps.className,
|
||||
Classes.MULTISELECT_TAG_INPUT_INPUT,
|
||||
// const handleInputRef = refHandler(
|
||||
// this,
|
||||
// 'inputElement',
|
||||
// this.props.inputProps?.inputRef,
|
||||
// );
|
||||
|
||||
const input = (
|
||||
<InputGroup
|
||||
leftIcon="search"
|
||||
placeholder="Filter..."
|
||||
rightElement={this.maybeRenderClearButton(listProps.query)}
|
||||
{...inputProps}
|
||||
inputRef={this.handleInputRef}
|
||||
onChange={listProps.handleQueryChange}
|
||||
value={listProps.query}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -147,6 +172,7 @@ export default class MultiSelect extends React.Component {
|
||||
{this.props.children}
|
||||
</div>
|
||||
<div onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} ref={this.listRef}>
|
||||
{filterable ? input : undefined}
|
||||
{listProps.itemList}
|
||||
</div>
|
||||
</Popover>
|
||||
@@ -179,6 +205,15 @@ export default class MultiSelect extends React.Component {
|
||||
// scroll active item into view after popover transition completes and all dimensions are stable.
|
||||
this.queryList.scrollActiveItemIntoView();
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
const { inputProps = {} } = this.props;
|
||||
// autofocus is enabled by default
|
||||
if (inputProps.autoFocus !== false) {
|
||||
this.inputElement.focus();
|
||||
}
|
||||
});
|
||||
Utils.safeInvokeMember(this.props.popoverProps, 'onOpened', node);
|
||||
}
|
||||
|
||||
resetQuery = () => this.queryList && this.queryList.setQuery("", true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user