pl-fe: multiselect changes

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-11-22 20:23:41 +01:00
parent 0631c86cba
commit 1d1d4c8f7e
5 changed files with 161 additions and 360 deletions

View File

@ -103,7 +103,6 @@
"localforage": "^1.10.0",
"lodash": "^4.17.21",
"mini-css-extract-plugin": "^2.9.2",
"multiselect-react-dropdown": "^2.0.25",
"mutative": "^1.1.0",
"path-browserify": "^1.0.1",
"pl-api": "workspace:*",

View File

@ -25,10 +25,9 @@ THE SOFTWARE.
/* eslint-disable jsdoc/require-jsdoc */
// @ts-nocheck
import clsx from 'clsx';
import React, { useRef, useEffect } from 'react';
// import './styles.css';
import Icon from './icon';
function useOutsideAlerter(ref, clickEvent) {
@ -56,38 +55,20 @@ const OutsideAlerter = (props) => {
};
interface IMultiselectProps {
options: any;
disablePreSelectedValues?: boolean;
selectedValues?: any;
isObject?: boolean;
displayValue?: string;
showCheckbox?: boolean;
selectionLimit?: any;
placeholder?: string;
groupBy?: string;
loading?: boolean;
style?: object;
emptyRecordMsg?: string;
onSelect?: (selectedList:any, selectedItem: any) => void;
onRemove?: (selectedList:any, selectedItem: any) => void;
onSearch?: (value:string) => void;
onKeyPressFn?: (event:any, value:string) => void;
closeIcon?: string;
singleSelect?: boolean;
caseSensitiveSearch?: boolean;
id?: string;
closeOnSelect?: boolean;
avoidHighlightFirstOption?: boolean;
hidePlaceholder?: boolean;
showArrow?: boolean;
keepSearchTerm?: boolean;
customCloseIcon?: React.ReactNode | string;
customArrow?: any;
disable?: boolean;
className?: string;
selectedValueDecorator?: (v:string, option: any) => React.ReactNode | string;
optionValueDecorator?: (v:string, option: any) => React.ReactNode | string;
hideSelectedList?: boolean;
options: any;
selectedValues?: any;
displayValue?: string;
placeholder?: string;
loading?: boolean;
emptyRecordMsg?: string;
onSelect?: (selectedList:any, selectedItem: any) => void;
onRemove?: (selectedList:any, selectedItem: any) => void;
onSearch?: (value:string) => void;
onKeyPressFn?: (event:any, value:string) => void;
id?: string;
name?: string;
disabled?: boolean;
className?: string;
}
export class Multiselect extends React.Component<IMultiselectProps, any> {
@ -102,12 +83,8 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
filteredOptions: props.options,
unfilteredOptions: props.options,
selectedValues: Object.assign([], props.selectedValues),
preSelectedValues: Object.assign([], props.selectedValues),
toggleOptionsList: false,
highlightOption: props.avoidHighlightFirstOption ? -1 : 0,
showCheckbox: props.showCheckbox,
keepSearchTerm: props.keepSearchTerm,
groupedObject: [],
highlightOption: 0,
};
// @ts-ignore
this.optionTimeout = null;
@ -122,61 +99,19 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
this.renderMultiselectContainer = this.renderMultiselectContainer.bind(this);
this.renderSelectedList = this.renderSelectedList.bind(this);
this.onRemoveSelectedItem = this.onRemoveSelectedItem.bind(this);
this.toggelOptionList = this.toggelOptionList.bind(this);
this.toggleOptionList = this.toggleOptionList.bind(this);
this.onArrowKeyNavigation = this.onArrowKeyNavigation.bind(this);
this.onSelectItem = this.onSelectItem.bind(this);
this.filterOptionsByInput = this.filterOptionsByInput.bind(this);
this.removeSelectedValuesFromOptions = this.removeSelectedValuesFromOptions.bind(this);
this.isSelectedValue = this.isSelectedValue.bind(this);
this.fadeOutSelection = this.fadeOutSelection.bind(this);
this.isDisablePreSelectedValues = this.isDisablePreSelectedValues.bind(this);
this.renderGroupByOptions = this.renderGroupByOptions.bind(this);
this.renderNormalOption = this.renderNormalOption.bind(this);
this.renderOption = this.renderOption.bind(this);
this.listenerCallback = this.listenerCallback.bind(this);
this.resetSelectedValues = this.resetSelectedValues.bind(this);
this.getSelectedItems = this.getSelectedItems.bind(this);
this.getSelectedItemsCount = this.getSelectedItemsCount.bind(this);
this.hideOnClickOutside = this.hideOnClickOutside.bind(this);
this.onCloseOptionList = this.onCloseOptionList.bind(this);
this.isVisible = this.isVisible.bind(this);
}
initialSetValue() {
const { showCheckbox, groupBy, singleSelect } = this.props;
const { options } = this.state;
if (!showCheckbox && !singleSelect) {
this.removeSelectedValuesFromOptions(false);
}
// if (singleSelect) {
// this.hideOnClickOutside();
// }
if (groupBy) {
this.groupByOptions(options);
}
}
resetSelectedValues() {
const { unfilteredOptions } = this.state;
return new Promise((resolve) => {
this.setState({
selectedValues: [],
preSelectedValues: [],
options: unfilteredOptions,
filteredOptions: unfilteredOptions,
}, () => {
// @ts-ignore
resolve();
this.initialSetValue();
});
});
}
getSelectedItems() {
return this.state.selectedValues;
}
getSelectedItemsCount() {
return this.state.selectedValues.length;
this.removeSelectedValuesFromOptions(false);
}
componentDidMount() {
@ -192,7 +127,7 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
this.setState({ options, filteredOptions: options, unfilteredOptions: options }, this.initialSetValue);
}
if (JSON.stringify(prevSelectedvalues) !== JSON.stringify(selectedValues)) {
this.setState({ selectedValues: Object.assign([], selectedValues), preSelectedValues: Object.assign([], selectedValues) }, this.initialSetValue);
this.setState({ selectedValues: Object.assign([], selectedValues) }, this.initialSetValue);
}
}
@ -213,51 +148,23 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
// Skipcheck flag - value will be true when the func called from on deselect anything.
removeSelectedValuesFromOptions(skipCheck) {
const { isObject, displayValue, groupBy } = this.props;
const { selectedValues = [], unfilteredOptions, options } = this.state;
if (!skipCheck && groupBy) {
this.groupByOptions(options);
}
const { displayValue } = this.props;
const { selectedValues = [], unfilteredOptions } = this.state;
if (!selectedValues.length && !skipCheck) {
return;
}
if (isObject) {
const optionList = unfilteredOptions.filter(item => {
return selectedValues.findIndex(
v => v[displayValue] === item[displayValue],
) === -1
? true
: false;
});
if (groupBy) {
this.groupByOptions(optionList);
}
this.setState(
{ options: optionList, filteredOptions: optionList },
this.filterOptionsByInput,
);
return;
}
const optionList = unfilteredOptions.filter(
item => selectedValues.indexOf(item) === -1,
);
const optionList = unfilteredOptions.filter(item => {
return selectedValues.findIndex(
v => v[displayValue] === item[displayValue],
) === -1
? true
: false;
});
this.setState(
{ options: optionList, filteredOptions: optionList },
this.filterOptionsByInput,
);
}
groupByOptions(options) {
const { groupBy } = this.props;
const groupedObject = options.reduce(function(r, a) {
const key = a[groupBy] || 'Others';
r[key] = r[key] || [];
r[key].push(a);
return r;
}, Object.create({}));
this.setState({ groupedObject });
return;
}
onChange(event) {
@ -281,20 +188,12 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
filterOptionsByInput() {
let { options } = this.state;
const { filteredOptions, inputValue } = this.state;
const { isObject, displayValue } = this.props;
if (isObject) {
options = filteredOptions.filter(i => this.matchValues(i[displayValue], inputValue));
} else {
options = filteredOptions.filter(i => this.matchValues(i, inputValue));
}
this.groupByOptions(options);
const { displayValue } = this.props;
options = filteredOptions.filter(i => this.matchValues(i[displayValue], inputValue));
this.setState({ options });
}
matchValues(value, search) {
if (this.props.caseSensitiveSearch) {
return value.indexOf(search) > -1;
}
if (value.toLowerCase) {
return value.toLowerCase().indexOf(search.toLowerCase()) > -1;
}
@ -309,8 +208,7 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
inputValue,
selectedValues,
} = this.state;
const { disablePreSelectedValues } = this.props;
if (e.keyCode === 8 && !inputValue && !disablePreSelectedValues && selectedValues.length) {
if (e.keyCode === 8 && !inputValue && selectedValues.length) {
this.onRemoveSelectedItem(selectedValues.length - 1);
}
if (!options.length) {
@ -350,220 +248,103 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
onRemoveSelectedItem(item) {
const { selectedValues } = this.state;
let { index = 0 } = this.state;
const { onRemove, showCheckbox, displayValue, isObject } = this.props;
if (isObject) {
index = selectedValues.findIndex(
i => i[displayValue] === item[displayValue],
);
} else {
index = selectedValues.indexOf(item);
}
const { onRemove, displayValue } = this.props;
index = selectedValues.findIndex(
i => i[displayValue] === item[displayValue],
);
selectedValues.splice(index, 1);
onRemove(selectedValues, item);
this.setState({ selectedValues }, () => {
if (!showCheckbox) {
this.removeSelectedValuesFromOptions(true);
}
this.removeSelectedValuesFromOptions(true);
});
if (!this.props.closeOnSelect) {
// @ts-ignore
this.searchBox.current.focus();
}
}
onSelectItem(item) {
const { selectedValues } = this.state;
const { selectionLimit, onSelect, singleSelect, showCheckbox } = this.props;
if (!this.state.keepSearchTerm){
this.setState({
inputValue: '',
});
}
if (singleSelect) {
this.onSingleSelect(item);
onSelect([item], item);
return;
}
const { onSelect } = this.props;
this.setState({
inputValue: '',
});
if (this.isSelectedValue(item)) {
this.onRemoveSelectedItem(item);
return;
}
if (selectionLimit === selectedValues.length) {
return;
}
selectedValues.push(item);
onSelect(selectedValues, item);
this.setState({ selectedValues }, () => {
if (!showCheckbox) {
this.removeSelectedValuesFromOptions(true);
} else {
this.filterOptionsByInput();
}
this.removeSelectedValuesFromOptions(true);
});
if (!this.props.closeOnSelect) {
// @ts-ignore
this.searchBox.current.focus();
}
}
onSingleSelect(item) {
this.setState({ selectedValues: [item], toggleOptionsList: false });
}
isSelectedValue(item) {
const { isObject, displayValue } = this.props;
const { displayValue } = this.props;
const { selectedValues } = this.state;
if (isObject) {
return (
selectedValues.filter(i => i[displayValue] === item[displayValue])
.length > 0
);
}
return selectedValues.filter(i => i === item).length > 0;
return (
selectedValues.filter(i => i[displayValue] === item[displayValue])
.length > 0
);
}
renderOptionList() {
const { groupBy, style, emptyRecordMsg, loading, loadingMessage = 'loading...' } = this.props;
const { emptyRecordMsg, loading, loadingMessage = 'loading...' } = this.props;
const { options } = this.state;
if (loading) {
return (
<ul className={'optionContainer'} style={style.optionContainer}>
{typeof loadingMessage === 'string' && <span style={style.loadingMessage} className={'notFound'}>{loadingMessage}</span>}
<ul className='optionContainer'>
{typeof loadingMessage === 'string' && <span className='notFound'>{loadingMessage}</span>}
{typeof loadingMessage !== 'string' && loadingMessage}
</ul>
);
}
return (
<ul className={'optionContainer'} style={style.optionContainer}>
{options.length === 0 && <span style={style.notFound} className={'notFound'}>{emptyRecordMsg}</span>}
{!groupBy ? this.renderNormalOption() : this.renderGroupByOptions()}
<ul className='optionContainer'>
{options.length === 0 && <span className='notFound'>{emptyRecordMsg}</span>}
{this.renderOption()}
</ul>
);
}
renderGroupByOptions() {
const { isObject = false, displayValue, showCheckbox, style, singleSelect } = this.props;
const { groupedObject } = this.state;
return Object.keys(groupedObject).map(obj => {
return (
<React.Fragment key={obj}>
<li className='groupHeading' style={style.groupHeading}>{obj}</li>
{groupedObject[obj].map((option, i) => {
const isSelected = this.isSelectedValue(option);
return (
<li
key={`option${i}`}
style={style.option}
className={`groupChildEle option ${isSelected ? 'selected' : ''} ${this.fadeOutSelection(option) ? 'disableSelection' : ''} ${this.isDisablePreSelectedValues(option) ? 'disableSelection' : ''}`}
onClick={() => this.onSelectItem(option)}
>
{showCheckbox && !singleSelect && (
<input
type='checkbox'
className={'checkbox'}
readOnly
checked={isSelected}
/>
)}
{this.props.optionValueDecorator(isObject ? option[displayValue] : (option || '').toString(), option)}
</li>
);
},
)}
</React.Fragment>
);
});
}
renderNormalOption() {
const { isObject = false, displayValue, showCheckbox, style, singleSelect } = this.props;
renderOption() {
const { displayValue } = this.props;
const { highlightOption } = this.state;
return this.state.options.map((option, i) => {
const isSelected = this.isSelectedValue(option);
return (
<li
key={`option${i}`}
style={style.option}
className={`option ${isSelected ? 'selected' : ''} ${highlightOption === i ? 'highlightOption highlight' : ''} ${this.fadeOutSelection(option) ? 'disableSelection' : ''} ${this.isDisablePreSelectedValues(option) ? 'disableSelection' : ''}`}
className={`option ${isSelected ? 'selected' : ''} ${highlightOption === i ? 'highlightOption highlight' : ''}`}
onClick={() => this.onSelectItem(option)}
>
{showCheckbox && !singleSelect && (
<input
type='checkbox'
readOnly
className={'checkbox'}
checked={isSelected}
/>
)}
{this.props.optionValueDecorator(isObject ? option[displayValue] : (option || '').toString(), option)}
{option[displayValue]}
</li>
);
});
}
renderSelectedList() {
const { isObject = false, displayValue, style, singleSelect } = this.props;
const { displayValue } = this.props;
const { selectedValues } = this.state;
return selectedValues.map((value, index) => (
<span className={`chip ${singleSelect && 'singleChip'} ${this.isDisablePreSelectedValues(value) && 'disableSelection'}`} key={index} style={style.chips}>
{this.props.selectedValueDecorator(!isObject ? (value || '').toString() : value[displayValue], value)}
{!this.isDisablePreSelectedValues(value) && (<i className='custom-close' onClick={() => this.onRemoveSelectedItem(value)}>
<span className='chip' key={index}>
{value[displayValue]}
<button onClick={() => this.onRemoveSelectedItem(value)}>
<Icon className='ml-1 size-4 hover:cursor-pointer' src={require('@phosphor-icons/core/regular/x-circle.svg')} />
</i>)}
</button>
</span>
));
}
isDisablePreSelectedValues(value) {
const { isObject, disablePreSelectedValues, displayValue } = this.props;
const { preSelectedValues } = this.state;
if (!disablePreSelectedValues || !preSelectedValues.length) {
return false;
}
if (isObject) {
return (
preSelectedValues.filter(i => i[displayValue] === value[displayValue])
.length > 0
);
}
return preSelectedValues.filter(i => i === value).length > 0;
}
fadeOutSelection(item) {
const { selectionLimit, showCheckbox, singleSelect } = this.props;
if (singleSelect) {
return;
}
const { selectedValues } = this.state;
if (selectionLimit === -1) {
return false;
}
if (selectionLimit !== selectedValues.length) {
return false;
}
if (selectionLimit === selectedValues.length) {
if (!showCheckbox) {
return true;
} else {
if (this.isSelectedValue(item)) {
return false;
}
return true;
}
}
}
toggelOptionList() {
toggleOptionList() {
this.setState({
toggleOptionsList: !this.state.toggleOptionsList,
highlightOption: this.props.avoidHighlightFirstOption ? -1 : 0,
highlightOption: 0,
});
}
onCloseOptionList() {
this.setState({
toggleOptionsList: false,
highlightOption: this.props.avoidHighlightFirstOption ? -1 : 0,
highlightOption: 0,
inputValue: '',
});
}
@ -573,7 +354,7 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
// @ts-ignore
clearTimeout(this.optionTimeout);
} else {
this.toggelOptionList();
this.toggleOptionList();
}
}
@ -583,53 +364,31 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
this.optionTimeout = setTimeout(this.onCloseOptionList, 250);
}
isVisible(elem) {
return !!elem && !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length);
}
hideOnClickOutside() {
const element = document.getElementsByClassName('multiselect-container')[0];
const outsideClickListener = event => {
if (element && !element.contains(event.target) && this.isVisible(element)) {
this.toggelOptionList();
}
};
document.addEventListener('click', outsideClickListener);
}
renderMultiselectContainer() {
const { inputValue, toggleOptionsList, selectedValues } = this.state;
const { placeholder, style, singleSelect, id, name, hidePlaceholder, disable, showArrow, className, customArrow, hideSelectedList } = this.props;
const { inputValue, toggleOptionsList } = this.state;
const { placeholder, id, name, disabled, className } = this.props;
return (
<div className={`multiselect-container multiSelectContainer ${disable ? 'disable_ms' : ''} ${className || ''}`} id={id || 'multiselectContainerReact'} style={style.multiselectContainer}>
<div className={clsx('multiselect-container', { 'multiselect-container--disabled': disabled }, className)} id={id}>
<div
className={`search-wrapper searchWrapper ${singleSelect ? 'singleSelect' : ''}`}
ref={this.searchWrapper} style={style.searchBox}
onClick={singleSelect ? this.toggelOptionList : () => {}}
className='searchWrapper'
ref={this.searchWrapper}
>
{!hideSelectedList && this.renderSelectedList()}
{this.renderSelectedList()}
<input
type='text'
ref={this.searchBox}
className={`searchBox ${singleSelect && selectedValues.length ? 'display-none' : ''}`}
id={`${id || 'search'}_input`}
name={`${name || 'search_name'}_input`}
className='searchBox'
name={`${name || 'search-name'}-input`}
onChange={this.onChange}
onKeyPress={this.onKeyPress}
value={inputValue}
onFocus={this.onFocus}
onBlur={this.onBlur}
placeholder={((singleSelect && selectedValues.length) || (hidePlaceholder && selectedValues.length)) ? '' : placeholder}
placeholder={placeholder}
onKeyDown={this.onArrowKeyNavigation}
style={style.inputField}
autoComplete='off'
disabled={singleSelect || disable}
disabled={disabled}
/>
{(singleSelect || showArrow) && (
<>
{customArrow ? <span className='icon_down_dir'>{customArrow}</span> : <img src='' className={'icon_cancel icon_down_dir'} />}
</>
)}
</div>
<div
className={`optionListContainer ${
@ -657,32 +416,15 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
Multiselect.defaultProps = {
options: [],
disablePreSelectedValues: false,
selectedValues: [],
isObject: true,
displayValue: 'model',
showCheckbox: false,
selectionLimit: -1,
placeholder: 'Select',
groupBy: '',
style: {},
emptyRecordMsg: 'No Options Available',
onSelect: () => {},
onRemove: () => {},
onKeyPressFn: () => {},
closeIcon: 'circle2',
singleSelect: false,
caseSensitiveSearch: false,
id: '',
name: '',
closeOnSelect: true,
avoidHighlightFirstOption: false,
hidePlaceholder: false,
showArrow: false,
keepSearchTerm: false,
customCloseIcon: '',
disabled: false,
className: '',
customArrow: undefined,
selectedValueDecorator: v => v,
optionValueDecorator: v => v,
} as IMultiselectProps;

View File

@ -2,7 +2,6 @@ import clsx from 'clsx';
import React, { useMemo, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import Icon from 'pl-fe/components/ui/icon';
import { Multiselect } from 'pl-fe/components/ui/multiselect';
import Select from 'pl-fe/components/ui/select';
@ -153,8 +152,7 @@ const Mutliselect: React.FC<IMultiselect> = (props) => {
onSelect={handleChange}
onRemove={handleChange}
displayValue='value'
disable={disabled}
customCloseIcon={<Icon className='ml-1 size-4 hover:cursor-pointer' src={require('@phosphor-icons/core/regular/x-circle.svg')} />}
disabled={disabled}
placeholder={intl.formatMessage(messages.selectPlaceholder)}
emptyRecordMsg={intl.formatMessage(messages.selectNoOptions)}
/>

View File

@ -11,12 +11,50 @@ select {
@apply flex text-sm items-center;
}
.plfe-multiselect {
.chip {
@apply bg-primary-600 my-1;
// Adapted from [multiselect-react-dropdown](https://github.com/srigar/multiselect-react-dropdown), licensed under MIT License.
.multiselect-container {
position: relative;
text-align: left;
width: 100%;
&--disabled {
pointer-events: none;
opacity: 0.5;
}
input {
border: none;
margin-top: 3px;
background: transparent;
&:focus {
outline: none;
}
}
.search-wrapper {
ul {
display: block;
padding: 0;
margin: 0;
border: 1px solid #ccc;
border-radius: 4px;
max-height: 250px;
overflow-y: auto;
}
li {
padding: 10px;
&:hover {
background: #0096fb;
color: #fff;
cursor: pointer;
}
}
.searchWrapper {
border: 1px solid;
position: relative;
@apply rounded-md border-gray-300 bg-white min-h-[38px] max-w-[400px] py-0 pl-3 pr-10 text-base focus:border-primary-500 focus:outline-none focus:ring-primary-500 disabled:opacity-50 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:focus:border-primary-500 dark:focus:ring-primary-500 sm:text-sm w-auto;
> input {
@ -24,6 +62,46 @@ select {
}
}
.chip {
padding: 4px 10px;
margin-right: 5px;
border-radius: 11px;
display: inline-flex;
align-items: center;
font-size: 13px;
line-height: 19px;
color: #fff;
white-space: nowrap;
@apply bg-primary-600 my-1;
}
.optionListContainer {
position: absolute;
width: 100%;
background: #fff;
border-radius: 4px;
margin-top: 1px;
z-index: 2;
}
.highlightOption {
@apply bg-primary-600;
color: #fff;
}
.displayBlock {
display: block;
}
.displayNone {
display: none;
}
.notFound {
padding: 10px;
display: block;
}
.optionContainer {
@apply border-gray-300 dark:border-gray-800 dark:bg-gray-900;
}
@ -31,8 +109,4 @@ select {
.option {
@apply hover:bg-primary-600;
}
.highlightOption {
@apply bg-primary-600;
}
}

12
pnpm-lock.yaml generated
View File

@ -310,9 +310,6 @@ importers:
mini-css-extract-plugin:
specifier: ^2.9.2
version: 2.9.2(webpack@5.101.0(esbuild@0.24.2))
multiselect-react-dropdown:
specifier: ^2.0.25
version: 2.0.25(react@18.3.1)
mutative:
specifier: ^1.1.0
version: 1.2.0
@ -4880,11 +4877,6 @@ packages:
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
multiselect-react-dropdown@2.0.25:
resolution: {integrity: sha512-z8kUSyBNOuV7vn4Dk35snzXWtIfTdSEEXhgDdLMvOmR+xJFx35vc1voUlSuOvrk3khb+hXC219Qs9szOvNm25Q==}
peerDependencies:
react: ^16.7.0 || ^17.0.0 || ^18.0.0
mutative@1.2.0:
resolution: {integrity: sha512-1muFw45Lwjso6TSBGiXfbjKS01fVSD/qaqBfTo/gXgp79e8KM4Sa1XP/S4iN2/DvSdIZgjFJI+JIhC7eKf3GTg==}
engines: {node: '>=14.0'}
@ -11734,10 +11726,6 @@ snapshots:
muggle-string@0.4.1: {}
multiselect-react-dropdown@2.0.25(react@18.3.1):
dependencies:
react: 18.3.1
mutative@1.2.0: {}
mz@2.7.0: