Files
cunningham/packages/react/src/components/Forms/Select/multi-common.tsx
Nathan Panchout 748d070bd9 ♻️(react) update Button component props and refactor related components
This commit modifies the Button component to switch the `variant` and
`color` props, ensuring consistency across various components that
utilize the Button. The changes include updates in the Alert, Modal,
Tooltip, and other components to reflect this new prop structure.
Additionally, several test and story files have been adjusted to
accommodate these changes, enhancing the overall component architecture.
2025-09-23 15:58:43 +02:00

168 lines
5.8 KiB
TypeScript

import React, { HTMLAttributes, useRef } from "react";
import { useMultipleSelection } from "downshift";
import classNames from "classnames";
import { Field } from ":/components/Forms/Field";
import { LabelledBox } from ":/components/Forms/LabelledBox";
import { Button } from ":/components/Button";
import { useCunningham } from ":/components/Provider";
import { Option, SelectProps } from ":/components/Forms/Select";
import {
getOptionsFilter,
optionToValue,
} from ":/components/Forms/Select/mono-common";
import { SelectedItems } from ":/components/Forms/Select/multi-selected-items";
import { SelectMultiMenu } from ":/components/Forms/Select/multi-menu";
/**
* This method returns a comparator that can be used to filter out options for multi select.
* For an option to be visible it must:
* - Match the input value in terms of search
* - Not be selected already
*
* @param selectedOptions
* @param inputValue
*/
export function getMultiOptionsFilter(
selectedOptions: Option[],
inputValue?: string
) {
const optionsFilter = getOptionsFilter(inputValue);
return (option: Option) => {
return (
!selectedOptions.find(
(selectedOption) =>
optionToValue(selectedOption) === optionToValue(option)
) && optionsFilter(option)
);
};
}
export type SubProps = Omit<SelectProps, "onChange"> & {
onChange?: (event: { target: { value: string[] } }) => void;
onSelectedItemsChange: (selectedItems: Option[]) => void;
selectedItems: Option[];
};
export interface SelectMultiAuxProps extends SubProps {
options: Option[];
labelAsPlaceholder: boolean;
selectedItems: Option[];
clearable?: boolean;
downshiftReturn: {
isOpen: boolean;
getLabelProps: any;
toggleButtonProps: any;
getMenuProps: any;
getItemProps: any;
highlightedIndex: number;
wrapperProps?: HTMLAttributes<HTMLDivElement>;
};
useMultipleSelectionReturn: ReturnType<typeof useMultipleSelection<Option>>;
}
export const SelectMultiAux = ({ children, ...props }: SelectMultiAuxProps) => {
const { t } = useCunningham();
const labelProps = props.downshiftReturn.getLabelProps();
const ref = useRef<HTMLDivElement>(null);
// We need to remove onBlur from toggleButtonProps because it triggers a menu closing each time
// we tick a checkbox using the monoline style.
const { onBlur, ...toggleProps } = props.downshiftReturn.toggleButtonProps;
return (
<>
<Field {...props}>
<div
ref={ref}
className={classNames(
"c__select",
"c__select--multi",
"c__select--" + props.state,
"c__select--" + props.selectedItemsStyle,
{
"c__select--disabled": props.disabled,
"c__select--populated": props.selectedItems.length > 0,
"c__select--monoline": props.monoline,
"c__select--multiline": !props.monoline,
}
)}
>
<div
className={classNames("c__select__wrapper", {
"c__select__wrapper--focus":
props.downshiftReturn.isOpen && !props.disabled,
})}
{...props.downshiftReturn.wrapperProps}
{...toggleProps}
>
{props.selectedItems.map((selectedItem, index) => (
<input
key={`${optionToValue(selectedItem)}${index.toString()}`}
type="hidden"
name={props.name}
value={optionToValue(selectedItem)}
/>
))}
<LabelledBox
label={props.label}
labelAsPlaceholder={props.labelAsPlaceholder}
htmlFor={labelProps.htmlFor}
labelId={labelProps.id}
hideLabel={props.hideLabel}
disabled={props.disabled}
>
<div className="c__select__inner">
<div className="c__select__inner__actions">
{props.clearable &&
!props.disabled &&
props.selectedItems.length > 0 && (
<>
<Button
variant="tertiary"
color="neutral"
size="nano"
aria-label={t(
"components.forms.select.clear_all_button_aria_label"
)}
className="c__select__inner__actions__clear"
onClick={(e) => {
e.stopPropagation();
props.onSelectedItemsChange([]);
}}
icon={<span className="material-icons">close</span>}
type="button"
/>
<div className="c__select__inner__actions__separator" />
</>
)}
<Button
variant="tertiary"
color="neutral"
size="nano"
className="c__select__inner__actions__open"
icon={
<span
className={classNames("material-icons", {
opened: props.downshiftReturn.isOpen,
})}
>
arrow_drop_down
</span>
}
disabled={props.disabled}
type="button"
/>
</div>
<div className="c__select__inner__value">
<SelectedItems {...props} />
{children}
</div>
</div>
</LabelledBox>
</div>
</div>
</Field>
<SelectMultiMenu {...props} selectRef={ref} />
</>
);
};