[FEAT] update the Slector to support add element

This commit is contained in:
Edouard DUPIN 2024-09-16 21:53:29 +02:00
parent 6181022814
commit 619e9aa4b8
10 changed files with 332 additions and 9 deletions

1
.gitignore vendored
View File

@ -64,3 +64,4 @@ __pycache__
.design/
.vscode/
front/storybook-static

3
front/pnpm-lock.yaml generated
View File

@ -53,9 +53,6 @@ importers:
dayjs:
specifier: 1.11.13
version: 1.11.13
framer-motion:
specifier: 11.5.4
version: 11.5.4(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
history:
specifier: 5.3.0
version: 5.3.0

View File

@ -1,7 +1,6 @@
import { RefObject } from 'react';
import {
Input,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,

View File

@ -0,0 +1,117 @@
import { useState } from 'react';
import { Box } from '@chakra-ui/react';
import { FormSelect } from '@/components/form/FormSelect';
import { useFormidable } from '@/components/form/Formidable';
export default {
title: 'Components/FormSelect',
};
type BasicFormData = {
data?: number;
};
export const Default = () => {
const form = useFormidable<BasicFormData>({});
return (
<FormSelect
label="Simple Title"
form={form}
variableName={'data'}
keyInputValue="id"
options={[{ id: 111 }, { id: 222 }, { id: 333 }, { id: 123 }]}
/>
);
};
export const ChangeKeys = () => {
const form = useFormidable<BasicFormData>({});
return (
<FormSelect
label="Simple Title for (ChangeKeys)"
form={form}
variableName={'data'}
keyInputKey="key"
keyInputValue="plop"
options={[
{ key: 111, plop: 'first Item' },
{ key: 222, plop: 'Second Item' },
{ key: 333, plop: 'third item' },
]}
/>
);
};
export const ChangeName = () => {
const form = useFormidable<BasicFormData>({});
return (
<FormSelect
label="Simple Title for (ChangeName)"
form={form}
variableName={'data'}
options={[
{ id: 111, name: 'first Item' },
{ id: 222, name: 'Second Item' },
{ id: 333, name: 'third item' },
]}
/>
);
};
export const AddableItem = () => {
const form = useFormidable<BasicFormData>({});
const [data, setData] = useState([
{ id: 111, name: 'first Item' },
{ id: 222, name: 'Second Item' },
{ id: 333, name: 'third item' },
]);
return (
<FormSelect
label="Simple Title for (ChangeName)"
form={form}
variableName={'data'}
addNewItem={(data: string) => {
let upperId = 0;
setData((previous) => {
previous.forEach((element) => {
if (element['id'] > upperId) {
upperId = element['id'];
}
});
upperId++;
return [...previous, { id: upperId, name: data }];
});
return upperId;
}}
options={data}
/>
);
};
export const DarkBackground = {
render: () => {
const form = useFormidable<BasicFormData>({});
return (
<Box p="4" color="white" bg="gray.800">
<FormSelect
label="Simple Title for (DarkBackground)"
form={form}
variableName={'data'}
options={[
{ id: 111, name: 'first Item' },
{ id: 222, name: 'Second Item' },
{ id: 333, name: 'third item' },
]}
/>
</Box>
);
},
parameters: {
docs: {
description: {
story: 'some story **markdown**',
},
},
},
};

View File

@ -1,18 +1,32 @@
import { RefObject } from 'react';
import { Text } from '@chakra-ui/react';
import { FormGroup } from '@/components/form/FormGroup';
import { UseFormidableReturn } from '@/components/form/Formidable';
import { SelectSingle } from '@/components/select/SelectSingle';
export type FormSelectProps = {
// Generic Form input
form: UseFormidableReturn;
// Form: Name of the variable
variableName: string;
// Forward object reference
ref?: RefObject<any>;
// Form: Label of the input
label?: string;
// Form: Placeholder if nothing is selected
placeholder?: string;
// Form: Specify if the element is required or not
isRequired?: boolean;
options?: object[];
// List of object options
options: object[];
// in the option specify the value Key
keyInputKey?: string;
// in the option specify the value field
keyInputValue?: string;
// Add capability to add an item (no key but only value)
addNewItem?: (data: string) => number | string;
};
export const FormSelect = ({
@ -21,9 +35,18 @@ export const FormSelect = ({
ref,
placeholder,
options,
keyInputKey,
keyInputValue = 'name',
addNewItem,
...rest
}: FormSelectProps) => {
// if set add capability to add the search item
const onCreate = !addNewItem
? undefined
: (data: string) => {
const ret = addNewItem(data);
form.setValues({ [variableName]: ret });
};
return (
<FormGroup
isModify={form.isModify[variableName]}
@ -35,7 +58,9 @@ export const FormSelect = ({
value={form.values[variableName]}
options={options}
onChange={(value) => form.setValues({ [variableName]: value })}
keyKey={keyInputKey}
keyValue={keyInputValue}
onCreate={onCreate}
/>
</FormGroup>
);

View File

@ -0,0 +1,117 @@
import { useState } from 'react';
import { Box } from '@chakra-ui/react';
import { FormSelectMultiple } from '@/components/form/FormSelectMultiple';
import { useFormidable } from '@/components/form/Formidable';
export default {
title: 'Components/FormSelectMultipleMultiple',
};
type BasicFormData = {
data?: number[];
};
export const Default = () => {
const form = useFormidable<BasicFormData>({});
return (
<FormSelectMultiple
label="Simple Title"
form={form}
variableName={'data'}
keyInputValue="id"
options={[{ id: 111 }, { id: 222 }, { id: 333 }, { id: 123 }]}
/>
);
};
export const ChangeKeys = () => {
const form = useFormidable<BasicFormData>({});
return (
<FormSelectMultiple
label="Simple Title for (ChangeKeys)"
form={form}
variableName={'data'}
keyInputKey="key"
keyInputValue="plop"
options={[
{ key: 111, plop: 'first Item' },
{ key: 222, plop: 'Second Item' },
{ key: 333, plop: 'third item' },
]}
/>
);
};
export const ChangeName = () => {
const form = useFormidable<BasicFormData>({});
return (
<FormSelectMultiple
label="Simple Title for (ChangeName)"
form={form}
variableName={'data'}
options={[
{ id: 111, name: 'first Item' },
{ id: 222, name: 'Second Item' },
{ id: 333, name: 'third item' },
]}
/>
);
};
export const AddableItem = () => {
const form = useFormidable<BasicFormData>({});
const [data, setData] = useState([
{ id: 111, name: 'first Item' },
{ id: 222, name: 'Second Item' },
{ id: 333, name: 'third item' },
]);
return (
<FormSelectMultiple
label="Simple Title for (ChangeName)"
form={form}
variableName={'data'}
addNewItem={(data: string) => {
let upperId = 0;
setData((previous) => {
previous.forEach((element) => {
if (element['id'] > upperId) {
upperId = element['id'];
}
});
upperId++;
return [...previous, { id: upperId, name: data }];
});
return upperId;
}}
options={data}
/>
);
};
export const DarkBackground = {
render: () => {
const form = useFormidable<BasicFormData>({});
return (
<Box p="4" color="white" bg="gray.800">
<FormSelectMultiple
label="Simple Title for (DarkBackground)"
form={form}
variableName={'data'}
options={[
{ id: 111, name: 'first Item' },
{ id: 222, name: 'Second Item' },
{ id: 333, name: 'third item' },
]}
/>
</Box>
);
},
parameters: {
docs: {
description: {
story: 'some story **markdown**',
},
},
},
};

View File

@ -5,14 +5,26 @@ import { UseFormidableReturn } from '@/components/form/Formidable';
import { SelectMultiple } from '@/components/select/SelectMultiple';
export type FormSelectMultipleProps = {
// Generic Form input
form: UseFormidableReturn;
// Form: Name of the variable
variableName: string;
// Forward object reference
ref?: RefObject<any>;
// Form: Label of the input
label?: string;
// Form: Placeholder if nothing is selected
placeholder?: string;
// Form: Specify if the element is required or not
isRequired?: boolean;
options?: object[];
// List of object options
options: object[];
// in the option specify the value Key
keyInputKey?: string;
// in the option specify the value field
keyInputValue?: string;
// Add capability to add an item (no key but only value)
addNewItem?: (data: string) => number | string;
};
export const FormSelectMultiple = ({
@ -21,9 +33,20 @@ export const FormSelectMultiple = ({
ref,
placeholder,
options,
keyInputKey,
keyInputValue = 'name',
addNewItem,
...rest
}: FormSelectMultipleProps) => {
// if set add capability to add the search item
const onCreate = !addNewItem
? undefined
: (data: string) => {
const ret = addNewItem(data);
form.setValues({
[variableName]: [...(form.values[variableName] ?? []), ret],
});
};
return (
<FormGroup
isModify={form.isModify[variableName]}
@ -35,7 +58,9 @@ export const FormSelectMultiple = ({
values={form.values[variableName]}
options={options}
onChange={(value) => form.setValues({ [variableName]: value })}
keyKey={keyInputKey}
keyValue={keyInputValue}
onCreate={onCreate}
/>
</FormGroup>
);

View File

@ -1,8 +1,9 @@
import { useEffect, useRef } from 'react';
import { Box, Button, Flex, Text } from '@chakra-ui/react';
import { MdAdd } from 'react-icons/md';
import { isNullOrUndefined } from '@/utils/validator';
import { isNullOrUndefined, isNumber } from '@/utils/validator';
export type SelectListModel = {
id: any;
@ -21,7 +22,11 @@ const optionToOptionDisplay = (
const out: SelectListModel[] = [];
data.forEach((element) => {
if (search) {
if (!element.name.toLowerCase().includes(search.toLowerCase())) {
if (isNumber(element.name)) {
if (!element.name.toString().includes(search.toLowerCase())) {
return;
}
} else if (!element.name.toLowerCase().includes(search.toLowerCase())) {
return;
}
}
@ -39,12 +44,15 @@ export type SelectListProps = {
selected: SelectListModel[];
onSelectValue: (data: SelectListModel) => void;
search?: string;
// if set add capability to add the search item
onCreate?: (data: string) => void;
};
export const SelectList = ({
options,
selected,
onSelectValue,
search,
onCreate,
}: SelectListProps) => {
const displayedValue = optionToOptionDisplay(options, selected, search);
const scrollToRef = useRef<HTMLButtonElement | null>(null);
@ -92,6 +100,20 @@ export const SelectList = ({
</Text>
</Button>
))}
{onCreate && search && search.length > 0 && (
<Button
marginY="1px"
borderRadius="0px"
autoFocus={false}
_hover={{ backgroundColor: 'gray.400' }}
onClick={() => onCreate(search)}
>
<Flex marginRight="auto">
<MdAdd />
<Text autoFocus={false}>Create '{search}'</Text>
</Flex>
</Button>
)}
</Flex>
</Box>
);

View File

@ -23,6 +23,8 @@ export type SelectMultipleProps = {
keyKey?: string;
keyValue?: string;
ref?: RefObject<any>;
// if set add capability to add the search item
onCreate?: (data: string) => void;
};
export const SelectMultiple = ({
@ -32,6 +34,7 @@ export const SelectMultiple = ({
ref,
keyKey = 'id',
keyValue = keyKey,
onCreate,
}: SelectMultipleProps) => {
const [showList, setShowList] = useState(false);
const transformedOption = useMemo(() => {
@ -85,6 +88,12 @@ export const SelectMultiple = ({
return;
}
};
const createNewItem = !onCreate
? undefined
: (data: string) => {
onCreate(data);
setCurrentSearch(undefined);
};
return (
<Flex direction="column" width="full" gap="0px">
@ -137,6 +146,7 @@ export const SelectMultiple = ({
selected={selectedOptions}
search={currentSearch}
onSelectValue={selectValue}
onCreate={createNewItem}
/>
)}
</Flex>

View File

@ -17,6 +17,8 @@ export type SelectSingleProps = {
keyKey?: string;
keyValue?: string;
ref?: RefObject<any>;
// if set add capability to add the search item
onCreate?: (data: string) => void;
};
export const SelectSingle = ({
@ -26,6 +28,7 @@ export const SelectSingle = ({
ref,
keyKey = 'id',
keyValue = keyKey,
onCreate,
}: SelectSingleProps) => {
const [showList, setShowList] = useState(false);
const transformedOption = useMemo(() => {
@ -75,6 +78,13 @@ export const SelectSingle = ({
}
};
const createNewItem = !onCreate
? undefined
: (data: string) => {
onCreate(data);
setCurrentSearch(undefined);
};
return (
<Flex direction="column" width="full" gap="0px">
<Flex>
@ -82,7 +92,6 @@ export const SelectSingle = ({
ref={refFocus}
width="full"
onChange={(e) => onChangeInput(e.target.value)}
//onSubmit={onSubmit}
onFocus={() => setShowList(true)}
onBlur={() => setTimeout(() => setShowList(false), 200)}
value={
@ -114,6 +123,7 @@ export const SelectSingle = ({
selected={selectedOptions ? [selectedOptions] : []}
search={currentSearch}
onSelectValue={selectValue}
onCreate={createNewItem}
/>
)}
</Flex>