⚡️ feat: make the select to be controller component
This commit is contained in:
parent
d387229e80
commit
6d8723881c
@ -53,7 +53,10 @@ export type SelectOption = {
|
||||
export type SelectOrientation = 'horizontal' | 'vertical';
|
||||
|
||||
interface SelectProps
|
||||
extends Omit<ComponentPropsWithoutRef<'input'>, 'size'>,
|
||||
extends Omit<
|
||||
ComponentPropsWithoutRef<'input'>,
|
||||
'size' | 'value' | 'onChange'
|
||||
>,
|
||||
SelectTheme {
|
||||
/**
|
||||
* The options of the select
|
||||
@ -95,6 +98,18 @@ interface SelectProps
|
||||
* Show the values of the select if it's multiple
|
||||
*/
|
||||
hideValues?: boolean;
|
||||
/**
|
||||
* The value of the select
|
||||
*/
|
||||
value?: SelectOption | SelectOption[];
|
||||
/**
|
||||
* Callback function when reset the select
|
||||
*/
|
||||
onClear?: () => void;
|
||||
/**
|
||||
* Callback function when the value of the select changes
|
||||
*/
|
||||
onChange?: (value: SelectOption | SelectOption[]) => void;
|
||||
}
|
||||
|
||||
export const Select = ({
|
||||
@ -113,6 +128,9 @@ export const Select = ({
|
||||
helperText,
|
||||
hideValues = false,
|
||||
placeholder: placeholderProp = 'Select an option',
|
||||
value,
|
||||
onChange,
|
||||
onClear,
|
||||
}: SelectProps) => {
|
||||
const theme = selectTheme({ size, error, variant, orientation });
|
||||
|
||||
@ -148,9 +166,26 @@ export const Select = ({
|
||||
}
|
||||
}, [dropdownOpen]); // Re-calculate whenever the dropdown is opened
|
||||
|
||||
useEffect(() => {
|
||||
// If multiple selection is enabled, ensure the internal state is an array
|
||||
if (multiple) {
|
||||
if (Array.isArray(value)) {
|
||||
// Directly use the provided array
|
||||
setSelectedItems(value);
|
||||
} else {
|
||||
// Reset or set to empty array if the value is not an array
|
||||
setSelectedItems([]);
|
||||
}
|
||||
} else {
|
||||
// For single selection, directly set the selected item
|
||||
setSelectedItem(value as SelectOption);
|
||||
}
|
||||
}, [value, multiple]);
|
||||
|
||||
const handleSelectedItemChange = (selectedItem: SelectOption | null) => {
|
||||
setSelectedItem(selectedItem);
|
||||
setInputValue(selectedItem ? selectedItem.label : '');
|
||||
onChange?.(selectedItem as SelectOption);
|
||||
};
|
||||
|
||||
const {
|
||||
@ -159,6 +194,7 @@ export const Select = ({
|
||||
addSelectedItem,
|
||||
removeSelectedItem,
|
||||
selectedItems,
|
||||
setSelectedItems,
|
||||
reset,
|
||||
} = useMultipleSelection<SelectOption>({
|
||||
onSelectedItemsChange: multiple
|
||||
@ -208,9 +244,17 @@ export const Select = ({
|
||||
// If the item is not already selected, add it to the selected items
|
||||
if (!selectedItems.includes(selectedItem)) {
|
||||
addSelectedItem(selectedItem);
|
||||
// Callback for `onChange`
|
||||
const newSelectedItems = [...selectedItems, selectedItem];
|
||||
onChange?.(newSelectedItems);
|
||||
} else {
|
||||
// If the item is already selected, remove it from the selected items
|
||||
removeSelectedItem(selectedItem);
|
||||
// Callback for `onChange`
|
||||
const newSelectedItems = selectedItems.filter(
|
||||
(item) => selectedItem !== item,
|
||||
);
|
||||
onChange?.(newSelectedItems);
|
||||
}
|
||||
setInputValue('');
|
||||
}
|
||||
@ -237,6 +281,7 @@ export const Select = ({
|
||||
reset();
|
||||
setSelectedItem(null);
|
||||
setInputValue('');
|
||||
onClear?.();
|
||||
};
|
||||
|
||||
const renderLabels = useMemo(
|
||||
|
@ -31,7 +31,7 @@ import {
|
||||
import { renderDefaultTag, renderMinimalTag } from './renders/tag';
|
||||
import { renderToast, renderToastsWithCta } from './renders/toast';
|
||||
import { renderTooltips } from './renders/tooltip';
|
||||
import { renderDropdowns } from './renders/dropdown';
|
||||
import { DropdownExample } from './renders/dropdown';
|
||||
|
||||
type ValuePiece = Date | null;
|
||||
type Value = ValuePiece | [ValuePiece, ValuePiece];
|
||||
@ -274,7 +274,7 @@ const Page: React.FC = () => {
|
||||
{/* Dropdown */}
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Dropdown / Select</h1>
|
||||
{renderDropdowns()}
|
||||
{<DropdownExample />}
|
||||
</div>
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { PencilIcon } from 'components/shared/CustomIcon';
|
||||
import { SelectOption, Select } from 'components/shared/Select';
|
||||
|
||||
@ -30,48 +30,98 @@ export const DROPDOWN_ITEMS: SelectOption[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const renderDropdowns = () => (
|
||||
export const DropdownExample = () => {
|
||||
const [singleValue, setSingleValue] = useState<SelectOption>();
|
||||
const [multipleValue, setMultipleValue] = useState<SelectOption[]>([]);
|
||||
|
||||
const handleSelect = (
|
||||
type: 'single' | 'multiple',
|
||||
value: SelectOption | SelectOption[],
|
||||
) => {
|
||||
if (type === 'single') {
|
||||
setSingleValue(value as SelectOption);
|
||||
} else {
|
||||
setMultipleValue(value as SelectOption[]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="text-sm text-center text-gray-500 -mb-8">Single – Small</p>
|
||||
<div className="flex gap-4 flex-wrap justify-center">
|
||||
<Select size="sm" placeholder="Default" options={DROPDOWN_ITEMS} />
|
||||
<Select
|
||||
size="sm"
|
||||
placeholder="Default"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
<Select
|
||||
size="sm"
|
||||
placeholder="Clearable"
|
||||
clearable
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
<Select
|
||||
size="sm"
|
||||
searchable
|
||||
placeholder="Searchable"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
<Select
|
||||
size="sm"
|
||||
placeholder="Vertical"
|
||||
orientation="vertical"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-center text-gray-500 -mb-8">Single – Medium</p>
|
||||
<div className="flex gap-4 flex-wrap justify-center">
|
||||
<Select placeholder="Default" options={DROPDOWN_ITEMS} />
|
||||
<Select placeholder="Clearable" clearable options={DROPDOWN_ITEMS} />
|
||||
<Select searchable placeholder="Searchable" options={DROPDOWN_ITEMS} />
|
||||
<Select
|
||||
placeholder="Default"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
<Select
|
||||
placeholder="Clearable"
|
||||
clearable
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
<Select
|
||||
searchable
|
||||
placeholder="Searchable"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
<Select
|
||||
placeholder="Vertical"
|
||||
orientation="vertical"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-center text-gray-500 -mb-8">Multiple – Small</p>
|
||||
<p className="text-sm text-center text-gray-500 -mb-8">
|
||||
Multiple – Small
|
||||
</p>
|
||||
<div className="flex gap-4 flex-wrap justify-center">
|
||||
<Select
|
||||
multiple
|
||||
size="sm"
|
||||
placeholder="Default"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
multiple
|
||||
@ -79,6 +129,8 @@ export const renderDropdowns = () => (
|
||||
placeholder="Clearable"
|
||||
clearable
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
searchable
|
||||
@ -86,6 +138,8 @@ export const renderDropdowns = () => (
|
||||
size="sm"
|
||||
placeholder="Searchable"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
multiple
|
||||
@ -93,6 +147,8 @@ export const renderDropdowns = () => (
|
||||
placeholder="Vertical"
|
||||
orientation="vertical"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
multiple
|
||||
@ -101,28 +157,44 @@ export const renderDropdowns = () => (
|
||||
orientation="vertical"
|
||||
placeholder="Hide values"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-center text-gray-500 -mb-8">Multiple – Medium</p>
|
||||
<p className="text-sm text-center text-gray-500 -mb-8">
|
||||
Multiple – Medium
|
||||
</p>
|
||||
<div className="flex gap-4 flex-wrap justify-center">
|
||||
<Select multiple placeholder="Default" options={DROPDOWN_ITEMS} />
|
||||
<Select
|
||||
multiple
|
||||
placeholder="Default"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
multiple
|
||||
placeholder="Clearable"
|
||||
clearable
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
searchable
|
||||
multiple
|
||||
placeholder="Searchable"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
multiple
|
||||
placeholder="Vertical"
|
||||
orientation="vertical"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
multiple
|
||||
@ -130,6 +202,8 @@ export const renderDropdowns = () => (
|
||||
orientation="vertical"
|
||||
placeholder="Hide values"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-center text-gray-500 -mb-4">
|
||||
@ -141,6 +215,8 @@ export const renderDropdowns = () => (
|
||||
description="Single select component"
|
||||
helperText="This is a helper text"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
<Select
|
||||
label="Clearable"
|
||||
@ -148,6 +224,8 @@ export const renderDropdowns = () => (
|
||||
helperText="This is a helper text"
|
||||
clearable
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
<Select
|
||||
searchable
|
||||
@ -155,6 +233,8 @@ export const renderDropdowns = () => (
|
||||
description="Single select component"
|
||||
helperText="This is a helper text"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
<Select
|
||||
label="Vertical"
|
||||
@ -162,6 +242,8 @@ export const renderDropdowns = () => (
|
||||
helperText="This is a helper text"
|
||||
orientation="vertical"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={singleValue}
|
||||
onChange={(value) => handleSelect('single', value)}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-center text-gray-500 -mb-4">
|
||||
@ -174,6 +256,8 @@ export const renderDropdowns = () => (
|
||||
helperText="This is a helper text"
|
||||
multiple
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
label="Clearable"
|
||||
@ -182,6 +266,8 @@ export const renderDropdowns = () => (
|
||||
multiple
|
||||
clearable
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
searchable
|
||||
@ -190,6 +276,8 @@ export const renderDropdowns = () => (
|
||||
helperText="This is a helper text"
|
||||
multiple
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
<Select
|
||||
label="Vertical"
|
||||
@ -198,6 +286,8 @@ export const renderDropdowns = () => (
|
||||
multiple
|
||||
orientation="vertical"
|
||||
options={DROPDOWN_ITEMS}
|
||||
value={multipleValue}
|
||||
onChange={(value) => handleSelect('multiple', value)}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-center text-gray-500 -mb-4">
|
||||
@ -238,3 +328,4 @@ export const renderDropdowns = () => (
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user