Changed implementation of select all option for key navigation.

This commit is contained in:
devanshu052000 2025-04-11 15:36:15 +05:30
parent eb89ef1eeb
commit e381c75866
4 changed files with 43 additions and 61 deletions

View file

@ -3,7 +3,6 @@ import { components } from 'react-select';
import SolidIcon from '@/_ui/Icon/SolidIcons';
import Loader from '@/ToolJetUI/Loader/Loader';
import './dropdownV2.scss';
import { FormCheck } from 'react-bootstrap';
// eslint-disable-next-line import/no-unresolved
import { useVirtualizer } from '@tanstack/react-virtual';
import cx from 'classnames';
@ -12,20 +11,8 @@ const { MenuList } = components;
// This Menulist also used in MultiselectV2
const CustomMenuList = ({ selectProps, ...props }) => {
const {
onInputChange,
onMenuInputFocus,
showAllOption,
isSelectAllSelected,
optionsLoadingState,
darkMode,
setSelected,
setIsSelectAllSelected,
fireEvent,
inputValue,
menuId,
showSearchInput,
} = selectProps;
const { onInputChange, onMenuInputFocus, optionsLoadingState, darkMode, inputValue, menuId, showSearchInput } =
selectProps;
const parentRef = useRef(null);
const virtualizer = useVirtualizer({
@ -35,16 +22,6 @@ const CustomMenuList = ({ selectProps, ...props }) => {
overscan: 15,
});
const handleSelectAll = (e) => {
e.target.checked && fireEvent();
if (e.target.checked) {
setSelected(props.options);
} else {
setSelected([]);
}
setIsSelectAllSelected(e.target.checked);
};
useEffect(() => {
const searchInput = document.querySelector('.dropdown-multiselect-widget-search-box');
if (searchInput) {
@ -89,17 +66,6 @@ const CustomMenuList = ({ selectProps, ...props }) => {
/>
</div>
)}
{showAllOption && !optionsLoadingState && (
<label htmlFor="select-all-checkbox" className="multiselect-custom-menulist-select-all">
<FormCheck
id="select-all-checkbox"
checked={isSelectAllSelected}
onChange={handleSelectAll}
onTouchEnd={handleSelectAll}
/>
<span style={{ marginLeft: '4px' }}>Select all</span>
</label>
)}
{!optionsLoadingState && (
<div
ref={parentRef}
@ -112,19 +78,22 @@ const CustomMenuList = ({ selectProps, ...props }) => {
style={{
height: `${virtualizer.getTotalSize() || 38}px`,
position: 'relative',
marginTop: '5px',
}}
>
{!virtualizer.getTotalSize() && props.children}
{virtualizer.getVirtualItems().map((virtualItem) => {
const option = props.options[virtualItem.index];
const child = props.children[virtualItem.index];
const isSelectAll = option?.value === 'multiselect-custom-menulist-select-all';
return (
<div
key={option.value}
style={{
position: 'absolute',
position: isSelectAll ? 'sticky' : 'absolute',
width: '100%',
top: 0,
zIndex: isSelectAll && 10,
transform: `translateY(${virtualItem.start}px)`,
}}
data-index={virtualItem.index}

View file

@ -1,7 +1,7 @@
.dropdown-multiselect-widget-custom-menu-list-body {
overflow-y: auto;
position: relative;
padding: 5px 0;
padding: 0 0 5px;
background-color: var(--surfaces-surface-01) !important;
border-radius: 0 0 8px 8px;
}

View file

@ -21,7 +21,9 @@ const CustomOption = (props) => {
<div className="d-flex multiselct-widget-option">
<FormCheck checked={props.isSelected} disabled={props?.isDisabled} />
<span style={{ marginLeft: '5px' }}>
{highlightText(props.label?.toString(), props.selectProps.inputValue)}
{props.label?.includes('Select all')
? 'Select all'
: highlightText(props.label?.toString(), props.selectProps.inputValue)}
</span>
</div>
</Option>

View file

@ -74,7 +74,6 @@ export const MultiselectV2 = ({
const [visibility, setVisibility] = useState(properties.visibility);
const [isMultiSelectLoading, setIsMultiSelectLoading] = useState(multiSelectLoadingState);
const [isMultiSelectDisabled, setIsMultiSelectDisabled] = useState(disabledState);
const [isSelectAllSelected, setIsSelectAllSelected] = useState(false);
const [searchInputValue, setSearchInputValue] = useState('');
const _height = padding === 'default' ? `${height}px` : `${height + 4}px`;
const [userInteracted, setUserInteracted] = useState(false);
@ -104,6 +103,17 @@ export const MultiselectV2 = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [advanced, JSON.stringify(schema), JSON.stringify(options), sort]);
const modifiedSelectOptions = useMemo(() => {
// Adding select all option dynamically to the options
if (showAllOption && !optionsLoadingState) {
return [
// Appended search input value so that it is always visible
{ label: `Select all ${searchInputValue}`, value: 'multiselect-custom-menulist-select-all' },
...selectOptions,
];
} else return selectOptions;
}, [showAllOption, JSON.stringify(selectOptions), optionsLoadingState, searchInputValue]);
function findDefaultItem(value, isAdvanced, isDefault) {
if (isAdvanced) {
const foundItem = Array.isArray(schema) ? schema.filter((item) => item?.visible && item?.default) : [];
@ -128,8 +138,28 @@ export const MultiselectV2 = ({
}
return false;
}
const onChangeHandler = (items, action) => {
setInputValue(items);
const SELECT_ALL = 'multiselect-custom-menulist-select-all';
if (action.option?.value === SELECT_ALL) {
// Case 1 - If select all is selected
if (action.action === 'select-option') {
setInputValue(modifiedSelectOptions);
} else {
setInputValue([]);
}
} else if (items?.some((item) => item.value === SELECT_ALL)) {
// Case 2 - If select all is not selected but selected options include select all
setInputValue(items.filter((item) => item.value !== SELECT_ALL));
} else if (selectOptions?.length === items?.length) {
// Case 3 - If all options are selected except select all
setInputValue(modifiedSelectOptions);
} else {
// Case 4 - Normal selection
setInputValue(items);
}
fireEvent('onSelect');
setUserInteracted(true);
};
@ -315,15 +345,6 @@ export const MultiselectV2 = ({
};
}, [isMultiselectOpen, componentName]);
// Handle Select all logic
useEffect(() => {
if (selectOptions?.length === selected?.length) {
setIsSelectAllSelected(true);
} else {
setIsSelectAllSelected(false);
}
}, [selectOptions, selected]);
const customStyles = {
container: (base) => ({
...base,
@ -481,7 +502,7 @@ export const MultiselectV2 = ({
isDisabled={isMultiSelectDisabled}
value={selected}
onChange={onChangeHandler}
options={selectOptions}
options={modifiedSelectOptions}
styles={customStyles}
// Only show loading when dynamic options are enabled
isLoading={isMultiSelectLoading}
@ -521,19 +542,9 @@ export const MultiselectV2 = ({
icon={icon}
doShowIcon={iconVisibility}
containerRef={valueContainerRef}
showAllOption={showAllOption}
isSelectAllSelected={isSelectAllSelected}
setIsSelectAllSelected={(value) => {
setIsSelectAllSelected(value);
if (!value) {
fireEvent('onSelect');
}
}}
setSelected={setInputValue}
iconColor={iconColor}
optionsLoadingState={optionsLoadingState && advanced}
darkMode={darkMode}
fireEvent={() => fireEvent('onSelect')}
menuPlacement="auto"
menuPortalTarget={document.body}
/>