2023-04-27 15:26:37 +02:00

150 lines
4.3 KiB
TypeScript

import ChevronIcon from "@Assets/Icons/chevron.svg";
import WindowStore from "@Front/Stores/WindowStore";
import classNames from "classnames";
import Image from "next/image";
import React, { FormEvent, ReactNode } from "react";
import Typography, { ITypo } from "../Typography";
import classes from "./classes.module.scss";
type IProps = {
selectedOption?: IOption;
onChange: (selectedOption: IOption) => void;
options: IOption[];
hasBorderRightCollapsed?: boolean;
placeholder?: string;
className?: string;
name?: string;
};
export type IOption = {
value: unknown;
label: string;
icon?: ReactNode;
};
type IState = {
isOpen: boolean;
listWidth: number;
listHeight: number;
selectedOption: IOption | null;
};
export default class Select extends React.Component<IProps, IState> {
private contentRef = React.createRef<HTMLUListElement>();
private rootRef = React.createRef<HTMLDivElement>();
private removeOnresize = () => {};
constructor(props: IProps) {
super(props);
this.state = {
isOpen: false,
listHeight: 0,
listWidth: 0,
selectedOption: null,
};
this.toggle = this.toggle.bind(this);
this.onSelect = this.onSelect.bind(this);
}
public override render(): JSX.Element {
const selectedOption = this.state.selectedOption ?? this.props.selectedOption;
return (
<div className={classNames(classes["root"], this.props.className)} ref={this.rootRef}>
{selectedOption && <input type="text" defaultValue={selectedOption.value as string} name={this.props.name} hidden />}
<label
className={classNames(classes["container-label"])}
data-open={this.state.isOpen}
onClick={this.toggle}
data-border-right-collapsed={this.props.hasBorderRightCollapsed}>
<div className={classNames(classes["container-input"])}>
{selectedOption && (
<>
<span className={classNames(classes["icon"], classes["token-icon"])}>{selectedOption?.icon}</span>
<Typography typo={ITypo.P_18}>
<span className={classes["text"]}>{selectedOption?.label}</span>
</Typography>
</>
)}
{!selectedOption && (
<div className={classes["placeholder"]} data-open={(selectedOption ? true : false).toString()}>
<Typography typo={ITypo.NAV_INPUT_16}>
<span className={classes["text"]}>{this.props.placeholder ?? ""}</span>
</Typography>
</div>
)}
</div>
<Image className={classes["chevron-icon"]} data-open={this.state.isOpen} src={ChevronIcon} alt="chevron icon" />
</label>
<ul
className={classes["container-ul"]}
data-open={this.state.isOpen}
ref={this.contentRef}
style={{
height: this.state.listHeight + "px",
}}>
{this.props.options.map((option, index) => (
<li key={`${index}-${option.value}`} className={classes["container-li"]} onClick={(e) => this.onSelect(option, e)}>
<div className={classes["token-icon"]}>{option.icon}</div>
<Typography typo={ITypo.P_18}>{option.label}</Typography>
</li>
))}
</ul>
{this.state.isOpen && <div className={classes["backdrop"]} onClick={this.toggle} />}
</div>
);
}
static getDerivedStateFromProps(props: IProps, state: IState) {
if (props.selectedOption?.value) {
return {
value: props.selectedOption?.value,
};
}
return null;
}
public override componentDidMount(): void {
this.onResize();
this.removeOnresize = WindowStore.getInstance().onResize(() => this.onResize());
}
public override componentWillUnmount() {
this.removeOnresize();
}
private onResize() {
let listHeight = 0;
let listWidth = 0;
listWidth = this.rootRef.current?.scrollWidth ?? 0;
if (this.state.listHeight) {
listHeight = this.contentRef.current?.scrollHeight ?? 0;
}
this.setState({ listHeight, listWidth });
}
private toggle(e: FormEvent) {
e.preventDefault();
let listHeight = 0;
let listWidth = this.rootRef.current?.scrollWidth ?? 0;
if (!this.state.listHeight) {
listHeight = this.contentRef.current?.scrollHeight ?? 0;
}
this.setState((state) => {
return { isOpen: !state.isOpen, listHeight, listWidth };
});
}
private onSelect(option: IOption, e: React.MouseEvent<HTMLLIElement, MouseEvent>) {
this.props.onChange && this.props.onChange(option);
this.setState({
selectedOption: option,
});
this.toggle(e);
}
}