✨ AutocompleteField
This commit is contained in:
parent
d777d15db6
commit
07d368fae2
@ -1,41 +1,7 @@
|
|||||||
@import "@Themes/constants.scss";
|
@import "@Themes/constants.scss";
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
cursor: pointer;
|
.label {
|
||||||
height: 56px;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
padding: var(--spacing-2, 16px) var(--spacing-sm, 8px);
|
|
||||||
gap: var(--spacing-2, 16px);
|
|
||||||
|
|
||||||
border-radius: var(--input-radius, 0px);
|
|
||||||
border: 1px solid var(--dropdown-input-border-default, #d7dce0);
|
|
||||||
background: var(--dropdown-input-background, #fff);
|
|
||||||
|
|
||||||
.content {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
padding: 0px var(--spacing-2, 16px);
|
padding: 0px var(--spacing-2, 16px);
|
||||||
align-items: center;
|
|
||||||
flex: 1 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: var(--dropdown-input-border-hovered);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
border-color: var(--dropdown-input-border-filled);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.open {
|
|
||||||
border-color: var(--dropdown-input-border-expanded);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
opacity: var(--opacity-disabled, 0.3);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
import useOpenable from "@Front/Hooks/useOpenable";
|
import useOpenable from "@Front/Hooks/useOpenable";
|
||||||
import { useState, useEffect, useCallback } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import DropdownMenu from "../Dropdown/DropdownMenu";
|
import DropdownMenu from "../Dropdown/DropdownMenu";
|
||||||
import { IOption } from "../Dropdown/DropdownMenu/DropdownOption";
|
import { IOption } from "../Dropdown/DropdownMenu/DropdownOption";
|
||||||
import SearchBar from "../SearchBar";
|
import SearchBar from "../SearchBar";
|
||||||
|
import Typography, { ETypo, ETypoColor } from "../Typography";
|
||||||
|
import classes from "./classes.module.scss";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
options: IOption[];
|
options: IOption[];
|
||||||
placeholder: string;
|
placeholder?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
label?: string;
|
||||||
|
onSelect?: (option: IOption) => void;
|
||||||
|
selectedOption?: IOption | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Autocomplete(props: IProps) {
|
export default function Autocomplete(props: IProps) {
|
||||||
const { options, placeholder, disabled } = props;
|
const { options, placeholder, disabled, label, selectedOption: selectedOptionProps } = props;
|
||||||
const [selectedOption, setSelectedOption] = useState<IOption | null>(null);
|
const [selectedOption, setSelectedOption] = useState<IOption | null>(selectedOptionProps ?? null);
|
||||||
const [searchValue, setSearchValue] = useState("");
|
const [searchValue, setSearchValue] = useState("");
|
||||||
const [filteredOptions, setFilteredOptions] = useState<IOption[]>(options);
|
const [filteredOptions, setFilteredOptions] = useState<IOption[]>(options);
|
||||||
const openable = useOpenable({ defaultOpen: false });
|
const openable = useOpenable({ defaultOpen: false });
|
||||||
@ -40,6 +46,10 @@ export default function Autocomplete(props: IProps) {
|
|||||||
[openable],
|
[openable],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedOption(selectedOptionProps ?? null);
|
||||||
|
}, [selectedOptionProps]);
|
||||||
|
|
||||||
const handleSelectOption = useCallback(
|
const handleSelectOption = useCallback(
|
||||||
(option: IOption) => {
|
(option: IOption) => {
|
||||||
setSelectedOption(option);
|
setSelectedOption(option);
|
||||||
@ -59,6 +69,13 @@ export default function Autocomplete(props: IProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu options={filteredOptions} openable={openable} onSelect={handleSelectOption} selectedOption={selectedOption}>
|
<DropdownMenu options={filteredOptions} openable={openable} onSelect={handleSelectOption} selectedOption={selectedOption}>
|
||||||
|
<div className={classes["root"]}>
|
||||||
|
{label && (
|
||||||
|
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>
|
||||||
|
{label}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<SearchBar placeholder={placeholder} disabled={disabled} onChange={handleSearchChange} value={searchValue} />
|
<SearchBar placeholder={placeholder} disabled={disabled} onChange={handleSearchChange} value={searchValue} />
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
@ -18,7 +18,7 @@ type IProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function Dropdown(props: IProps) {
|
export default function Dropdown(props: IProps) {
|
||||||
const { options, placeholder, disabled, onSelect, selectedOption: selectedOptionProps } = props;
|
const { options, placeholder, disabled, onSelect, selectedOption: selectedOptionProps, label } = props;
|
||||||
const [selectedOption, setSelectedOption] = useState<IOption | null>(selectedOptionProps ?? null);
|
const [selectedOption, setSelectedOption] = useState<IOption | null>(selectedOptionProps ?? null);
|
||||||
const openable = useOpenable({ defaultOpen: false });
|
const openable = useOpenable({ defaultOpen: false });
|
||||||
|
|
||||||
@ -37,9 +37,9 @@ export default function Dropdown(props: IProps) {
|
|||||||
return (
|
return (
|
||||||
<DropdownMenu options={options} openable={openable} onSelect={handleOnSelect} selectedOption={selectedOption}>
|
<DropdownMenu options={options} openable={openable} onSelect={handleOnSelect} selectedOption={selectedOption}>
|
||||||
<div className={classes["root"]}>
|
<div className={classes["root"]}>
|
||||||
{props.label && (
|
{label && (
|
||||||
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>
|
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>
|
||||||
{props.label}
|
{label}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
@import "@Themes/constants.scss";
|
||||||
|
|
||||||
|
.root {
|
||||||
|
.hidden-input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.errors-container {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
import Autocomplete from "../../Autocomplete";
|
||||||
|
import { IOption } from "../../Dropdown/DropdownMenu/DropdownOption";
|
||||||
|
import BaseField, { IProps as IBaseFieldProps, IState as IBaseFieldState } from "../BaseField";
|
||||||
|
import classes from "./classes.module.scss";
|
||||||
|
|
||||||
|
export type IProps = IBaseFieldProps & {
|
||||||
|
onSelect?: (option: IOption) => void;
|
||||||
|
options: IOption[];
|
||||||
|
selectedOption?: IOption | null;
|
||||||
|
label?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type IState = IBaseFieldState & {
|
||||||
|
selectedOption: IOption | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class AutocompleteField extends BaseField<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
selectedOption: this.props.selectedOption ?? null,
|
||||||
|
...this.getDefaultState(),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleOnChange = this.handleOnChange.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleOnChange = (option: IOption) => {
|
||||||
|
this.setState({ selectedOption: option });
|
||||||
|
this.props.onSelect?.(option);
|
||||||
|
};
|
||||||
|
|
||||||
|
public override componentDidUpdate(prevProps: IProps): void {
|
||||||
|
if (prevProps.selectedOption !== this.props.selectedOption) {
|
||||||
|
this.setState({ selectedOption: this.props.selectedOption ?? null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override render(): ReactNode {
|
||||||
|
return (
|
||||||
|
<div className={classes["root"]}>
|
||||||
|
<Autocomplete
|
||||||
|
options={this.props.options}
|
||||||
|
placeholder={this.props.placeholder}
|
||||||
|
onSelect={this.handleOnChange}
|
||||||
|
selectedOption={this.state.selectedOption}
|
||||||
|
label={this.props.label}
|
||||||
|
/>
|
||||||
|
{this.state.selectedOption && (
|
||||||
|
<input
|
||||||
|
className={classes["hidden-input"]}
|
||||||
|
name={this.props.name}
|
||||||
|
type="text"
|
||||||
|
defaultValue={this.state.selectedOption.id}
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{this.hasError() && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ export type IProps = IBaseFieldProps & {
|
|||||||
onSelect?: (option: IOption) => void;
|
onSelect?: (option: IOption) => void;
|
||||||
options: IOption[];
|
options: IOption[];
|
||||||
selectedOption?: IOption | null;
|
selectedOption?: IOption | null;
|
||||||
|
label?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IState = IBaseFieldState & {
|
type IState = IBaseFieldState & {
|
||||||
@ -46,6 +47,7 @@ export default class SelectField extends BaseField<IProps, IState> {
|
|||||||
placeholder={this.props.placeholder}
|
placeholder={this.props.placeholder}
|
||||||
onSelect={this.handleOnChange}
|
onSelect={this.handleOnChange}
|
||||||
selectedOption={this.state.selectedOption}
|
selectedOption={this.state.selectedOption}
|
||||||
|
label={this.props.label}
|
||||||
/>
|
/>
|
||||||
{this.state.selectedOption && (
|
{this.state.selectedOption && (
|
||||||
<input
|
<input
|
||||||
|
@ -105,6 +105,7 @@ export default function DesignSystem() {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
placeholder="Placeholder"
|
placeholder="Placeholder"
|
||||||
|
label="Label"
|
||||||
/>
|
/>
|
||||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Dropdown</Typography>
|
<Typography typo={ETypo.TEXT_LG_BOLD}>Dropdown</Typography>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
|
Loading…
x
Reference in New Issue
Block a user