Previously the pills were not stacking under the actions div, now we can do it using the forgotten CSS float feature.
206 lines
6.3 KiB
TypeScript
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>
|
|
);
|
|
};
|