in src/select/select.tsx [433:686]
filterValue: getValueForFilter(prevState.selected, type, filterValue),
});
}
}
if ('selected' in nextProps && nextProps.selected !== prevSelected) {
const selected = nextProps.selected || (multiple ? [] : null);
Object.assign(nextState, {
selected,
filterValue: getValueForFilter(selected, type, filterValue),
});
}
if (prevMultiple !== multiple && !dequal(prevMultiple, multiple)) {
nextState.selected = multiple ? [] : null;
}
if (multiple && !nextState.selected) {
nextState.selected = prevState.selected;
}
const {selected} = {...prevState, ...nextState};
if (selected && Array.isArray(selected)) {
nextState.multipleMap = buildMultipleMap(selected);
const {filteredData, addButton} = getListItems(nextProps, nextState, filterValue, data);
Object.assign(nextState, {shownData: filteredData, addButton});
}
const isSelectionEmpty =
nextProps.selected === null ||
nextProps.selected === undefined ||
(Array.isArray(nextProps.selected) && nextProps.selected.length === 0);
if (isSelectionEmpty) {
nextState.lastInteractedKey = null;
}
return nextState;
}
state: SelectState<T> = {
data: [],
shownData: [],
selected: this.props.multiple ? [] : null,
lastInteractedKey: null,
filterValue: (this.props.filter && typeof this.props.filter === 'object' && this.props.filter.value) || '',
shortcutsEnabled: false,
popupShortcuts: false,
showPopup: this.props.showPopup,
prevData: [],
prevSelected: null,
prevMultiple: this.props.multiple,
multipleMap: {},
addButton: null,
};
componentDidUpdate(prevProps: SelectProps<T>, prevState: SelectState<T>) {
const {showPopup, selected} = this.state;
const {onClose, onOpen, onChange, multiple} = this.props;
if (prevState.showPopup && !showPopup) {
(onClose as (s: typeof selected) => void)(selected);
} else if (!prevState.showPopup && showPopup) {
onOpen();
}
if (multiple !== prevProps.multiple && !dequal(multiple, prevProps.multiple)) {
(onChange as (s: typeof selected) => void)(selected);
}
}
static contextType = ControlsHeightContext;
declare context: React.ContextType<typeof ControlsHeightContext>;
static Type = Type;
static Size = Size;
id = getUID('select-');
shortcutsScope = this.id;
listId = `${this.id}:list`;
private _focusHandler = (e: React.FocusEvent<HTMLInputElement>) => {
this.props.onFocus(e);
this.setState({
shortcutsEnabled: true,
focused: true,
});
};
isClickingSelect = false;
mouseDownHandler = () => {
this.isClickingSelect = true;
};
mouseUpHandler = () => {
this.isClickingSelect = false;
};
private _blurHandler = () => {
this.props.onBlur();
if (this._popup && this._popup.isVisible() && !this._popup.isClickingPopup && !this.isClickingSelect) {
window.setTimeout(() => {
this.setState({showPopup: false});
});
}
if (!this._popup?.isClickingPopup) {
this.setState({
shortcutsEnabled: false,
focused: false,
});
}
};
node?: HTMLElement | null;
nodeRef = (el: HTMLElement | null) => {
this.node = el;
};
_popup: SelectPopup<SelectItemData<T>> | null = null;
onEmptyPopupEnter = () => {
if (this.state.addButton) {
this.addHandler();
}
};
focus = () => {
const focusableSelectExists = this.node?.querySelector<HTMLElement>('[data-test~=ring-select__focus]');
const restoreFocusNode = this.props.targetElement || focusableSelectExists;
restoreFocusNode?.focus();
};
private _onEnter = () => {
if (this.state.addButton && this.state.shownData.length === 0) {
this.addHandler();
}
this.props.onDone();
if (!this._popup?.isVisible() && this.props.allowAny) {
return true;
}
return undefined;
};
private _onEsc = (event: KeyboardEvent) => {
if (!this._popup?.isVisible()) {
return true;
}
if (this.props.multiple || !this.props.getInitial) {
return false;
}
const selected = {
key: Math.random(),
label: this.props.getInitial(),
} as SelectItem<T>;
this.setState(
{
selected,
filterValue: this.getValueForFilter(selected),
},
() => {
(this.props.onChange as (s: typeof selected, e?: Event) => void)(selected, event);
this.props.onReset();
},
);
return undefined;
};
_inputShortcutHandler = () => {
if (this.state.focused && this._popup && !this._popup.isVisible()) {
this._clickHandler();
}
};
getValueForFilter(selected: SelectItem<T> | readonly SelectItem<T>[] | null | undefined): string {
return getValueForFilter(selected, this.props.type, this.state.filterValue);
}
private _getActiveIndex(items: SelectItem<T>[]): number | null {
const {selected, lastInteractedKey} = this.state;
const isNonOptionItem = (item: SelectItem<T>) =>
item.isResetItem || List.isItemType(List.ListProps.Type.SEPARATOR, item);
if (lastInteractedKey !== null && lastInteractedKey !== undefined) {
const index = items.findIndex(item => item.key === lastInteractedKey && !isNonOptionItem(item));
if (index >= 0) return index;
}
let selectedItems: SelectItem<T>[] = [];
if (Array.isArray(selected)) {
selectedItems = selected;
} else if (selected) {
selectedItems = [selected];
}
if (selectedItems.length > 0) {
const lastSelected = selectedItems[selectedItems.length - 1];
const index = items.findIndex(item => item.key === lastSelected.key);
if (index >= 0) return index;
}
return null;
}
popupRef = (el: SelectPopup<SelectItemData<T>> | null) => {
this._popup = el;
};
_getResetOption(): SelectItem<T> | null {
const isOptionsSelected = Array.isArray(this.state.selected) && this.state.selected.length;
const reset = this.props.tags && typeof this.props.tags === 'object' ? this.props.tags.reset : null;
if (!isOptionsSelected || !reset) {
return null;
}
const resetHandler = (item: SelectItem<T>, event: Event | SyntheticEvent) => {
this.clear(event);
this.clearFilter();
this.props.onFilter('');
this.setState(prevState => ({
shownData: prevState.shownData.slice(reset.separator ? 2 : 1),
multipleMap: {},
}));
this._redrawPopup();
};
return {
isResetItem: true,
separator: reset.separator,
key: reset.label,
rgItemType: List.ListProps.Type.CUSTOM,
template: (
<Button inline className={styles.button} data-test='ring-select-reset-tags-button' height={ControlsHeight.S}>
{reset.label}
</Button>
),
glyph: reset.glyph,
onClick: resetHandler,
} as SelectItem<T>;
}
_prependResetOption(shownData: SelectItem<T>[]): SelectItem<T>[] {
const resetOption = this._getResetOption();
if (resetOption) {
const resetItems = [resetOption];
if (resetOption.separator) {
resetItems.push({