Skip to content

Commit

Permalink
feat(form): number input with comma seperator (#53)
Browse files Browse the repository at this point in the history
* feat(form): number input with comma seperator

* feat: add seperator for number input

---------

Co-authored-by: mrbadri <mrbadri.dev@gmail.com>
  • Loading branch information
erfanmoghadasi and mrbadri authored Jul 8, 2024
1 parent 03b1e98 commit d20e56c
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"CodeGPT.apiKey": "CodeGPT Plus Beta"
}
16 changes: 14 additions & 2 deletions apps/docs/src/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,20 @@ export function App() {
],
},
},
{ id: 'form-password-1',
{
id: 'form-number',
groupType: 'form',
type: 'number',
props: {
id: 'number one',
seperator: ',',
defaultValue: '12345',
formId: '20',
label: 'number field (Form Id: 20)',
},
},
{
id: 'form-password-1',
groupType: 'form',
type: 'password',
props: {
Expand Down Expand Up @@ -265,7 +278,6 @@ export function App() {
// },
// }
// }

];

const [tabs, setTabs] = useState<TabData[]>([
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <Skeleton {...getNumberFieldLoadingProps()} />;
};

export default NumberFieldLoading;
Original file line number Diff line number Diff line change
@@ -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<NumberFieldProps> = (props) => {
const { getFieldProps, show } = useNumberField(props);

if (show) return <TextField {...getFieldProps()} />;

return null;
};

export default NumberField;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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<TextFieldProps, 'onChange'> & {
value: string;
onChange: (value: string) => void;
} & {
seperator?: string;
id: Id;
formId: FormId;
script?: Script;
dependesies?: Dependesies;
propsController?: Record<string, any>;
api?: Api;
rule?: Rule;
show?: boolean;
};
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
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,
seperator,
...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, seperator: string) => {
return value.replace(/\B(?=(\d{3})+(?!\d))/g, seperator);
};

// Handle changes to the input field
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
let rawValue = event.target.value;

// Remove existing commas from the input value
if (seperator) {
const regex = new RegExp(seperator);
rawValue = event.target.value.replace(regex, '');
}

// 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];
const value = seperator
? formatNumber(field.value ?? '', seperator)
: field.value;

// Props
const getFieldProps = () => ({
...field,
...numberFieldProps,
helperText: error?.message ?? helperText,
error: !!error,
...scriptResult,
...newProps,
value,
onChange: (e: React.ChangeEvent<HTMLInputElement>) => handleChange(e),
});

return { getFieldProps, show };
};

export default UseNumberField;
3 changes: 3 additions & 0 deletions packages/core/src/modules/form/src/types/public.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SkeletonOwnProps } from '@mui/material';
import { SubmitFieldProps } from '../components/actions/submit/submit.types';
import { AutoCompleteProps } from '../components/fields/autoComplete/autoComplete.types';
import { CheckboxProps } from '../components/fields/checkbox/checkbox.types';
import { NumberFieldProps } from '../components/fields/number/number.types';
import { SelectProps } from '../components/fields/select/select.types';
import { TextProps } from '../components/fields/text/text.types';
import { Form } from '../hooks/useForms/useForms.types';
Expand All @@ -18,6 +19,7 @@ export type FormTypes =
| 'action-submit'
| 'auto-complete'
| 'checkbox'
| 'number'
| 'password'
| 'select';

Expand All @@ -31,6 +33,7 @@ export type FieldProps =
| SubmitFieldProps
| AutoCompleteProps<Option>
| CheckboxProps
| NumberFieldProps
| SelectProps;

export type Dependesies = string[];
Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/modules/form/src/utils/selector/formSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { FC, Fragment, Suspense, lazy } from 'react';
import { SubmitFieldProps } from '../../components/actions/submit/submit.types';
import { AutoCompleteProps } from '../../components/fields/autoComplete/autoComplete.types';
import { CheckboxProps } from '../../components/fields/checkbox/checkbox.types';
import { NumberFieldProps } from '../../components/fields/number/number.types';
import { PasswordProps } from '../../components/fields/password/password.types';
import { SelectProps } from '../../components/fields/select/select.types';
import { TextProps } from '../../components/fields/text/text.types';
import { Option } from '../../types/public.types';
import { FormSelectorProps } from './formSelector.types';

import SubmitLoading from '../../components/actions/submit/submit.loading';
import NumberFieldLoading from '../../components/fields/number/number.loading';
import PasswordLoading from '../../components/fields/password/password.loading';
import TextLoading from '../../components/fields/text/text.loading';

Expand Down Expand Up @@ -66,6 +68,19 @@ const FormSelector: FC<FormSelectorProps> = ({
</Suspense>
);

case 'number':
SelectedComponent = lazy(
() => import('../../components/fields/number/number')
);

return (
<Suspense
key={fieldProps.id}
fallback={<NumberFieldLoading {...loading} />}
>
<SelectedComponent {...(fieldProps as NumberFieldProps)} />
</Suspense>
);
case 'password':
SelectedComponent = lazy(
() => import('../../components/fields/password/password')
Expand Down

0 comments on commit d20e56c

Please sign in to comment.