Files
cunningham/packages/react/src/components/Forms/Select/mono-common.tsx
Nathan Vasse 29b58ed1a6 (react) use css float for multi select
Previously the pills were not stacking under the actions div, now we can
do it using the forgotten CSS float feature.
2023-06-19 15:04:05 +02:00

206 lines
6.3 KiB
TypeScript

import React, { HTMLAttributes, useEffect } from "react";
import { UseSelectReturnValue } from "downshift";
import classNames from "classnames";
import { useCunningham } from ":/components/Provider";
import { Field } from ":/components/Forms/Field";
import { LabelledBox } from ":/components/Forms/LabelledBox";
import { Button } from ":/components/Button";
import { Option, SelectProps } from ":/components/Forms/Select/mono";
export function getOptionsFilter(inputValue?: string) {
return (option: Option) => {
return (
!inputValue ||
option.label.toLowerCase().includes(inputValue.toLowerCase()) ||
option.value?.toLowerCase().includes(inputValue.toLowerCase())
);
};
}
export const optionToString = (option: Option | null) => {
return option ? option.label : "";
};
export const optionToValue = (option: Option) => {
return option.value ?? option.label;
};
export interface SubProps extends SelectProps {
defaultSelectedItem?: Option;
downshiftProps: {
initialSelectedItem?: Option;
onSelectedItemChange?: any;
};
}
export interface SelectAuxProps extends SubProps {
options: Option[];
labelAsPlaceholder: boolean;
downshiftReturn: {
isOpen: boolean;
wrapperProps?: HTMLAttributes<HTMLDivElement>;
selectedItem?: Option | null;
getLabelProps: any;
toggleButtonProps: any;
getMenuProps: any;
getItemProps: any;
highlightedIndex: number;
selectItem: UseSelectReturnValue<Option>["selectItem"];
};
}
/**
* This component is used by searchable and non-searchable select components.
* It contains the common logic between the two.
*/
export const SelectMonoAux = ({
children,
state = "default",
text,
rightText,
fullWidth,
options,
name,
label,
hideLabel,
labelAsPlaceholder,
downshiftProps,
downshiftReturn,
value,
disabled,
clearable = true,
}: SelectAuxProps) => {
const { t } = useCunningham();
const labelProps = downshiftReturn.getLabelProps();
// When component is controlled, this useEffect will update the local selected item.
useEffect(() => {
if (downshiftProps.initialSelectedItem !== undefined) {
return;
}
const optionToSelect = options.find(
(option) => optionToValue(option) === value
);
downshiftReturn.selectItem(optionToSelect ?? null);
}, [value]);
return (
<Field
state={state}
text={text}
rightText={rightText}
fullWidth={fullWidth}
>
<div
className={classNames(
"c__select",
"c__select--mono",
"c__select--" + state,
{
"c__select--disabled": disabled,
}
)}
>
{/* We disabled linting for this specific line because we consider that the onClick props is only used for */}
{/* mouse users, so this do not engender any issue for accessibility. */}
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
<div
className={classNames("c__select__wrapper", {
"c__select__wrapper--focus": downshiftReturn.isOpen && !disabled,
})}
{...downshiftReturn.wrapperProps}
>
{downshiftReturn.selectedItem && (
<input
type="hidden"
name={name}
value={optionToValue(downshiftReturn.selectedItem)}
/>
)}
<LabelledBox
label={label}
hideLabel={hideLabel}
labelAsPlaceholder={labelAsPlaceholder}
htmlFor={labelProps.htmlFor}
labelId={labelProps.id}
>
<div className="c__select__inner">
<div className="c__select__inner__value">{children}</div>
<div className="c__select__inner__actions">
{clearable && !disabled && downshiftReturn.selectedItem && (
<>
<Button
color="tertiary"
size="small"
aria-label={t(
"components.forms.select.clear_button_aria_label"
)}
className="c__select__inner__actions__clear"
onClick={(e) => {
downshiftReturn.selectItem(null);
e.stopPropagation();
}}
icon={<span className="material-icons">close</span>}
/>
<div className="c__select__inner__actions__separator" />
</>
)}
<Button
color="tertiary"
size="small"
className="c__select__inner__actions__open"
icon={
<span
className={classNames("material-icons", {
opened: downshiftReturn.isOpen,
})}
>
arrow_drop_down
</span>
}
disabled={disabled}
{...downshiftReturn.toggleButtonProps}
/>
</div>
</div>
</LabelledBox>
</div>
<div
className={classNames("c__select__menu", {
"c__select__menu--opened": downshiftReturn.isOpen,
})}
{...downshiftReturn.getMenuProps()}
>
<ul>
{downshiftReturn.isOpen &&
options.map((item, index) => {
const isActive = index === downshiftReturn.highlightedIndex;
return (
<li
className={classNames("c__select__menu__item", {
"c__select__menu__item--highlight": isActive,
"c__select__menu__item--selected":
downshiftReturn.selectedItem === item,
"c__select__menu__item--disabled": item.disabled,
})}
key={`${item.value}${index}`}
{...downshiftReturn.getItemProps({
item,
index,
isActive,
disabled: item.disabled,
})}
>
<span>{item.label}</span>
</li>
);
})}
</ul>
</div>
</div>
</Field>
);
};