From c2a905a95a7adbd55a901cb82991b8970528d111 Mon Sep 17 00:00:00 2001 From: erfan Date: Wed, 3 Jul 2024 15:17:48 +0330 Subject: [PATCH] feat(form): number input with comma seperator --- .vscode/settings.json | 3 + apps/docs/src/app/app.tsx | 10 ++ .../fields/number/number.loading.tsx | 13 +++ .../src/components/fields/number/number.tsx | 17 +++ .../components/fields/number/number.types.ts | 21 ++++ .../fields/number/useNumber.loading.ts | 22 ++++ .../src/components/fields/number/useNumber.ts | 103 ++++++++++++++++++ .../modules/form/src/types/public.types.ts | 7 +- .../form/src/utils/selector/formSelector.tsx | 13 +++ 9 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 packages/core/src/modules/form/src/components/fields/number/number.loading.tsx create mode 100644 packages/core/src/modules/form/src/components/fields/number/number.tsx create mode 100644 packages/core/src/modules/form/src/components/fields/number/number.types.ts create mode 100644 packages/core/src/modules/form/src/components/fields/number/useNumber.loading.ts create mode 100644 packages/core/src/modules/form/src/components/fields/number/useNumber.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1ea4962 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "CodeGPT.apiKey": "CodeGPT Plus Beta" +} diff --git a/apps/docs/src/app/app.tsx b/apps/docs/src/app/app.tsx index ef530a9..99b3660 100644 --- a/apps/docs/src/app/app.tsx +++ b/apps/docs/src/app/app.tsx @@ -219,6 +219,16 @@ export function App() { ], }, }, + { + id: 'form-number', + groupType: 'form', + type: 'number', + props: { + id: 'number one', + formId: '20', + label: 'number field (Form Id: 20)', + }, + }, ]; const [tabs, setTabs] = useState([ diff --git a/packages/core/src/modules/form/src/components/fields/number/number.loading.tsx b/packages/core/src/modules/form/src/components/fields/number/number.loading.tsx new file mode 100644 index 0000000..2767556 --- /dev/null +++ b/packages/core/src/modules/form/src/components/fields/number/number.loading.tsx @@ -0,0 +1,13 @@ +import { Skeleton } from '@mui/material'; + +import { LoadingProps } from '@mui-builder/types/configs.type'; + +import useNumberFieldLoading from './useNumber.loading'; + +const NumberFieldLoading = (props: LoadingProps) => { + const { getNumberFieldLoadingProps } = useNumberFieldLoading(props); + + return ; +}; + +export default NumberFieldLoading; diff --git a/packages/core/src/modules/form/src/components/fields/number/number.tsx b/packages/core/src/modules/form/src/components/fields/number/number.tsx new file mode 100644 index 0000000..e5a84e5 --- /dev/null +++ b/packages/core/src/modules/form/src/components/fields/number/number.tsx @@ -0,0 +1,17 @@ +import { FC } from 'react'; + +import { TextField } from '@mui/material'; + +import { NumberFieldProps } from './number.types'; + +import useNumberField from './useNumber'; + +const NumberField: FC = (props) => { + const { getFieldProps, show } = useNumberField(props); + + if (show) return ; + + return null; +}; + +export default NumberField; diff --git a/packages/core/src/modules/form/src/components/fields/number/number.types.ts b/packages/core/src/modules/form/src/components/fields/number/number.types.ts new file mode 100644 index 0000000..265434f --- /dev/null +++ b/packages/core/src/modules/form/src/components/fields/number/number.types.ts @@ -0,0 +1,21 @@ +import { TextFieldProps } from '@mui/material'; + +import { Api } from '@mui-builder/types/api.types'; +import { Script } from '@mui-builder/types/script.types'; + +import { Dependesies, FormId, Id } from '../../../types/public.types'; +import { Rule } from '../../../types/validation.types'; + +export type NumberFieldProps = Omit & { + value: string; + onChange: (value: string) => void; +} & { + id: Id; + formId: FormId; + script?: Script; + dependesies?: Dependesies; + propsController?: Record; + api?: Api; + rule?: Rule; + show?: boolean; +}; diff --git a/packages/core/src/modules/form/src/components/fields/number/useNumber.loading.ts b/packages/core/src/modules/form/src/components/fields/number/useNumber.loading.ts new file mode 100644 index 0000000..323e203 --- /dev/null +++ b/packages/core/src/modules/form/src/components/fields/number/useNumber.loading.ts @@ -0,0 +1,22 @@ +import { LoadingProps } from '@mui-builder/types/configs.type'; + +const useNumberFieldLoading = (props: LoadingProps) => { + const { animation = 'wave', sx, ...otherProps } = props; + + const getNumberFieldLoadingProps = () => ({ + sx: { + display: 'inline-block', + transform: 'unset', + width: '255px', + height: '56px', + mx: 0.5, + ...sx, + }, + animation, + ...otherProps, + }); + + return { getNumberFieldLoadingProps }; +}; + +export default useNumberFieldLoading; diff --git a/packages/core/src/modules/form/src/components/fields/number/useNumber.ts b/packages/core/src/modules/form/src/components/fields/number/useNumber.ts new file mode 100644 index 0000000..d67781d --- /dev/null +++ b/packages/core/src/modules/form/src/components/fields/number/useNumber.ts @@ -0,0 +1,103 @@ +import { useController, useWatch } from 'react-hook-form'; + +import useQueryBuilder from '@mui-builder/utils/useQueryBuilder/useQueryBuilder'; +import UseScript from '@mui-builder/utils/useScript/useScript'; + +import axios from 'axios'; + +import { NumberFieldProps } from './number.types'; + +import useForms from '../../../hooks/useForms/useForms'; +import usePropsController from '../../../hooks/usePropsController/usePropsController'; +import useRule from '../../../hooks/useRule/useRule'; + +const UseNumberField = (props: NumberFieldProps) => { + const { + formId, + script, + api, + show = true, + dependesies, + helperText, + defaultValue, + onChange, + ...numberFieldProps + } = props; + const { configs, queries } = api || {}; + + const { forms } = useForms(); + const formMethod = forms?.[formId]; + const { setProps, propsController } = usePropsController(); + + const newProps = propsController?.[numberFieldProps?.id] || {}; + + // Handle Script + const { scriptResult } = UseScript({ + script, + formMethod, + forms, + formId, + setProps, + }); + + // Function to format the number with thousands separators + const formatNumber = (value: string) => { + return value.replace(/\B(?=(\d{3})+(?!\d))/g, ','); + }; + + // Handle changes to the input field + const handleChange = (event: React.ChangeEvent) => { + // Remove existing commas from the input value + const rawValue = event.target.value.replace(/,/g, ''); + // Check if the value is a valid number before calling onChange + if (!isNaN(Number(rawValue))) { + formMethod.setValue(numberFieldProps.id, rawValue); + } + }; + + // Handle Wtach Fields + useWatch({ + control: formMethod.control, + name: dependesies ?? [], + }); + + // API Call + useQueryBuilder({ + apiInstance: axios, + apiConfigs: configs ?? {}, + apiQuery: queries ?? {}, + formMethod, + formId, + forms, + }); + + // Controller + const { + field, + formState: { errors }, + } = useController({ + name: numberFieldProps.id, + control: formMethod.control, + disabled: numberFieldProps.disabled, + rules: useRule(numberFieldProps?.rule), + defaultValue, + }); + + const error = errors?.[numberFieldProps.id]; + + // Props + const getFieldProps = () => ({ + ...field, + ...numberFieldProps, + helperText: error?.message ?? helperText, + error: !!error, + ...scriptResult, + ...newProps, + value: formatNumber(field.value ?? ''), + onChange: (e: React.ChangeEvent) => handleChange(e), + }); + + return { getFieldProps, show }; +}; + +export default UseNumberField; diff --git a/packages/core/src/modules/form/src/types/public.types.ts b/packages/core/src/modules/form/src/types/public.types.ts index 55159e9..14dbedb 100644 --- a/packages/core/src/modules/form/src/types/public.types.ts +++ b/packages/core/src/modules/form/src/types/public.types.ts @@ -8,6 +8,7 @@ import { import { CheckboxProps } from '../components/fields/checkbox/checkbox.types'; import { TextProps } from '../components/fields/text/text.types'; import { Form } from '../hooks/useForms/useForms.types'; +import { NumberFieldProps } from '../components/fields/number/number.types'; export type FormId = string; @@ -19,13 +20,15 @@ export type FormTypes = | 'field-text' | 'action-submit' | 'auto-complete' - | 'checkbox'; + | 'checkbox' + | 'number'; export type FieldProps = | TextProps | SubmitFieldProps | AutoCompleteProps - | CheckboxProps; + | CheckboxProps + | NumberFieldProps export type Dependesies = string[]; diff --git a/packages/core/src/modules/form/src/utils/selector/formSelector.tsx b/packages/core/src/modules/form/src/utils/selector/formSelector.tsx index 358662c..44113a9 100644 --- a/packages/core/src/modules/form/src/utils/selector/formSelector.tsx +++ b/packages/core/src/modules/form/src/utils/selector/formSelector.tsx @@ -11,6 +11,8 @@ import { FormSelectorProps } from './formSelector.types'; import SubmitLoading from '../../components/actions/submit/submit.loading'; import TextLoading from '../../components/fields/text/text.loading'; +import { NumberFieldProps } from '../../components/fields/number/number.types'; +import NumberFieldLoading from '../../components/fields/number/number.loading'; const FormSelector: FC = ({ fieldType, @@ -67,6 +69,17 @@ const FormSelector: FC = ({ ); + case 'number': + SelectedComponent = lazy( + () => import('../../components/fields/number/number') + ); + + return ( + }> + + + ); + default: SelectedComponent = Fragment;