@weareconceptstudio/form 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/FormConfig.d.ts +5 -0
  2. package/dist/FormConfig.js +19 -0
  3. package/dist/checkbox/BaseCheckbox.d.ts +3 -0
  4. package/dist/checkbox/BaseCheckbox.js +51 -0
  5. package/dist/checkbox/index.d.ts +1 -1
  6. package/dist/checkbox/index.js +16 -5
  7. package/dist/color-picker/index.d.ts +5 -2
  8. package/dist/color-picker/index.js +4 -4
  9. package/dist/date-picker/index.d.ts +7 -2
  10. package/dist/date-picker/index.js +4 -4
  11. package/dist/error-message/index.d.ts +3 -0
  12. package/dist/error-message/index.js +9 -6
  13. package/dist/form/BaseForm.d.ts +22 -17
  14. package/dist/form/BaseForm.js +41 -55
  15. package/dist/form/Builder/index.d.ts +9 -0
  16. package/dist/form/Builder/index.js +46 -0
  17. package/dist/form/Builder/style.d.ts +2 -0
  18. package/dist/form/Builder/style.js +3 -0
  19. package/dist/form/Item/index.d.ts +12 -10
  20. package/dist/form/Item/index.js +26 -19
  21. package/dist/form/hooks/rules.d.ts +3 -0
  22. package/dist/form/hooks/rules.js +44 -0
  23. package/dist/form/hooks/useEvent.d.ts +1 -0
  24. package/dist/form/hooks/useEvent.js +12 -0
  25. package/dist/form/hooks/useFormInstance.js +4 -1
  26. package/dist/form/hooks/useInput.d.ts +12 -0
  27. package/dist/form/hooks/useInput.js +46 -0
  28. package/dist/form/index.d.ts +6 -16
  29. package/dist/form/index.js +3 -2
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.js +4 -0
  32. package/dist/input/BaseInput/index.d.ts +5 -3
  33. package/dist/input/BaseInput/index.js +28 -39
  34. package/dist/input/Input.d.ts +5 -2
  35. package/dist/input/Input.js +18 -16
  36. package/dist/input/Number/index.d.ts +5 -2
  37. package/dist/input/Number/index.js +27 -4
  38. package/dist/input/Password/icons.d.ts +1 -1
  39. package/dist/input/Password/icons.js +6 -6
  40. package/dist/input/Password/index.d.ts +5 -2
  41. package/dist/input/Password/index.js +8 -8
  42. package/dist/input/TextArea/index.d.ts +5 -2
  43. package/dist/input/TextArea/index.js +8 -9
  44. package/dist/input/index.d.ts +11 -2
  45. package/dist/input/index.js +6 -4
  46. package/dist/phone-number/index.d.ts +7 -0
  47. package/dist/phone-number/index.js +13 -0
  48. package/dist/radio/BaseRadio.d.ts +1 -1
  49. package/dist/radio/BaseRadio.js +20 -7
  50. package/dist/radio/Group.d.ts +1 -1
  51. package/dist/radio/Group.js +24 -4
  52. package/dist/radio/context.d.ts +1 -1
  53. package/dist/radio/context.js +3 -3
  54. package/dist/radio/index.js +2 -2
  55. package/dist/select/icons.d.ts +3 -0
  56. package/dist/select/icons.js +6 -0
  57. package/dist/select/index.d.ts +5 -2
  58. package/dist/select/index.js +57 -20
  59. package/dist/styles/formStyle.d.ts +2 -0
  60. package/dist/styles/formStyle.js +585 -0
  61. package/dist/styles/theme.d.ts +2 -0
  62. package/dist/styles/theme.js +113 -0
  63. package/dist/styles/variables.d.ts +2 -0
  64. package/dist/styles/variables.js +246 -0
  65. package/dist/translations/en.d.ts +52 -0
  66. package/dist/translations/en.js +52 -0
  67. package/dist/translations/index.d.ts +54 -0
  68. package/dist/translations/index.js +2 -0
  69. package/dist/upload/UploadButton/index.d.ts +3 -0
  70. package/dist/upload/UploadButton/index.js +41 -0
  71. package/dist/upload/UploadButton/style.d.ts +2 -0
  72. package/dist/upload/UploadButton/style.js +24 -0
  73. package/dist/upload/UploadList/ListItem/index.d.ts +5 -0
  74. package/dist/upload/UploadList/ListItem/index.js +11 -0
  75. package/dist/upload/UploadList/index.d.ts +3 -0
  76. package/dist/upload/UploadList/index.js +11 -0
  77. package/dist/upload/UploadList/style.d.ts +2 -0
  78. package/dist/upload/UploadList/style.js +13 -0
  79. package/dist/upload/index.d.ts +5 -2
  80. package/dist/upload/index.js +18 -5
  81. package/dist/upload/utils.d.ts +9 -0
  82. package/dist/upload/utils.js +64 -0
  83. package/package.json +31 -34
@@ -0,0 +1,5 @@
1
+ export function FormConfig({ fontFamily, children }: {
2
+ fontFamily: any;
3
+ children: any;
4
+ }): React.JSX.Element;
5
+ import React from 'react';
@@ -0,0 +1,19 @@
1
+ import React, { useEffect } from 'react';
2
+ import { useTranslation } from '@weareconceptstudio/core';
3
+ import { ThemeProvider } from 'styled-components';
4
+ //* Translations
5
+ import translations from './translations';
6
+ //* Styles
7
+ import theme from './styles/theme';
8
+ import FormVariables from './styles/variables';
9
+ import FormStyle from './styles/formStyle';
10
+ export const FormConfig = ({ fontFamily, children }) => {
11
+ const { addTranslation } = useTranslation();
12
+ useEffect(() => {
13
+ addTranslation(translations, 'prepend');
14
+ }, []);
15
+ return (React.createElement(ThemeProvider, { theme: theme },
16
+ React.createElement(FormVariables, { fontFamily: fontFamily }),
17
+ React.createElement(FormStyle, null),
18
+ children));
19
+ };
@@ -0,0 +1,3 @@
1
+ export default BaseCheckbox;
2
+ declare const BaseCheckbox: React.ForwardRefExoticComponent<React.RefAttributes<any>>;
3
+ import React from 'react';
@@ -0,0 +1,51 @@
1
+ import React, { forwardRef, useRef, useImperativeHandle } from 'react';
2
+ import { useMergedState } from '@weareconceptstudio/core';
3
+ import classNames from 'classnames';
4
+ const BaseCheckbox = forwardRef((props, ref) => {
5
+ const { prefixCls = 'checkbox', className, style, checked, disabled, defaultChecked = false, type = 'checkbox', title, onChange, ...inputProps } = props;
6
+ const inputRef = useRef(null);
7
+ const holderRef = useRef(null);
8
+ const [rawValue, setRawValue] = useMergedState(defaultChecked, {
9
+ value: checked,
10
+ });
11
+ useImperativeHandle(ref, () => ({
12
+ focus: (options) => {
13
+ inputRef.current?.focus(options);
14
+ },
15
+ blur: () => {
16
+ inputRef.current?.blur();
17
+ },
18
+ input: inputRef.current,
19
+ nativeElement: holderRef.current,
20
+ }));
21
+ const classString = classNames(prefixCls, className, {
22
+ [`${prefixCls}-checked`]: rawValue,
23
+ [`${prefixCls}-disabled`]: disabled,
24
+ });
25
+ const handleChange = (e) => {
26
+ if (disabled) {
27
+ return;
28
+ }
29
+ if (!('checked' in props)) {
30
+ setRawValue(e.target.checked);
31
+ }
32
+ onChange?.({
33
+ target: {
34
+ ...props,
35
+ type,
36
+ checked: e.target.checked,
37
+ },
38
+ stopPropagation() {
39
+ e.stopPropagation();
40
+ },
41
+ preventDefault() {
42
+ e.preventDefault();
43
+ },
44
+ nativeEvent: e.nativeEvent,
45
+ });
46
+ };
47
+ return (React.createElement("span", { className: classString, title: title, style: style, ref: holderRef },
48
+ React.createElement("input", { type: type, className: `${prefixCls}-input`, ref: inputRef, onChange: handleChange, disabled: disabled, checked: !!rawValue, ...inputProps }),
49
+ React.createElement("span", { className: `${prefixCls}-inner` })));
50
+ });
51
+ export default BaseCheckbox;
@@ -1,3 +1,3 @@
1
1
  export default Checkbox;
2
2
  declare const Checkbox: React.ForwardRefExoticComponent<React.RefAttributes<any>>;
3
- import * as React from "react";
3
+ import React from 'react';
@@ -1,8 +1,19 @@
1
- import * as React from 'react';
2
- const Checkbox = React.forwardRef(({ type = 'checkbox', children, ...restProps }, forwardRef) => {
3
- return (React.createElement("label", null,
4
- React.createElement("input", { type: type, ...forwardRef, ...restProps }),
5
- children !== undefined && React.createElement("span", null, children)));
1
+ import React, { forwardRef } from 'react';
2
+ import BaseCheckbox from './BaseCheckbox';
3
+ import classNames from 'classnames';
4
+ const Checkbox = forwardRef((props, forwardRef) => {
5
+ const { type = 'checkbox', prefixCls = 'checkbox', children, ...restProps } = props;
6
+ const checkboxProps = {
7
+ ref: forwardRef,
8
+ ...restProps,
9
+ };
10
+ const classString = classNames(`${prefixCls}-wrapper`, {
11
+ [`${prefixCls}-wrapper-checked`]: checkboxProps.checked,
12
+ [`${prefixCls}-wrapper-disabled`]: checkboxProps.disabled,
13
+ });
14
+ return (React.createElement("label", { className: classString },
15
+ React.createElement(BaseCheckbox, { ...checkboxProps }),
16
+ children !== undefined && React.createElement("span", { className: `${prefixCls}-content` }, children)));
6
17
  });
7
18
  if (process.env.NODE_ENV !== 'production') {
8
19
  Checkbox.displayName = 'Checkbox';
@@ -1,3 +1,6 @@
1
1
  export default ColorPicker;
2
- declare const ColorPicker: React.ForwardRefExoticComponent<React.RefAttributes<any>>;
3
- import * as React from "react";
2
+ declare function ColorPicker(props: any): React.JSX.Element;
3
+ declare namespace ColorPicker {
4
+ let displayName: string;
5
+ }
6
+ import React from 'react';
@@ -1,7 +1,7 @@
1
- import * as React from 'react';
2
- const ColorPicker = React.forwardRef((props, forwardRef) => {
3
- return (React.createElement("input", { type: 'color', ...forwardRef }));
4
- });
1
+ import React from 'react';
2
+ const ColorPicker = (props) => {
3
+ return React.createElement("input", { type: 'color' });
4
+ };
5
5
  if (process.env.NODE_ENV !== 'production') {
6
6
  ColorPicker.displayName = 'ColorPicker';
7
7
  }
@@ -1,3 +1,8 @@
1
1
  export default DatePicker;
2
- declare const DatePicker: React.ForwardRefExoticComponent<React.RefAttributes<any>>;
3
- import * as React from "react";
2
+ declare function DatePicker({ type }: {
3
+ type?: string;
4
+ }): React.JSX.Element;
5
+ declare namespace DatePicker {
6
+ let displayName: string;
7
+ }
8
+ import React from 'react';
@@ -1,11 +1,11 @@
1
- import * as React from 'react';
1
+ import React from 'react';
2
2
  /*
3
3
  types - default=date,datetime-local,month,time,week
4
4
  *
5
5
  */
6
- const DatePicker = React.forwardRef(({ type = 'date' }, forwardRef) => {
7
- return (React.createElement("input", { type: type, ...forwardRef }));
8
- });
6
+ const DatePicker = ({ type = 'date' }) => {
7
+ return React.createElement("input", { type: type });
8
+ };
9
9
  if (process.env.NODE_ENV !== 'production') {
10
10
  DatePicker.displayName = 'DatePicker';
11
11
  }
@@ -7,3 +7,6 @@ declare function ErrorMessage({ as, errors, name, message, render, ...rest }: {
7
7
  message: any;
8
8
  render: any;
9
9
  }): any;
10
+ declare namespace ErrorMessage {
11
+ let displayName: string;
12
+ }
@@ -1,4 +1,4 @@
1
- import * as React from 'react';
1
+ import React, { Fragment, isValidElement, cloneElement, createElement } from 'react';
2
2
  import { useFormContext, get } from 'react-hook-form';
3
3
  const ErrorMessage = ({ as, errors, name, message, render, ...rest }) => {
4
4
  const methods = useFormContext();
@@ -10,13 +10,16 @@ const ErrorMessage = ({ as, errors, name, message, render, ...rest }) => {
10
10
  const props = Object.assign({}, rest, {
11
11
  children: messageFromRegister || message,
12
12
  });
13
- return React.isValidElement(as)
14
- ? React.cloneElement(as, props)
13
+ return isValidElement(as)
14
+ ? cloneElement(as, props)
15
15
  : render
16
- ? (render({
16
+ ? render({
17
17
  message: messageFromRegister || message,
18
18
  messages: types,
19
- }))
20
- : React.createElement(as || React.Fragment, props);
19
+ })
20
+ : createElement(as || Fragment, props);
21
21
  };
22
+ if (process.env.NODE_ENV !== 'production') {
23
+ ErrorMessage.displayName = 'ErrorMessage';
24
+ }
22
25
  export default ErrorMessage;
@@ -1,18 +1,23 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { FieldValues } from 'react-hook-form';
3
+ interface BaseFormProps<T extends FieldValues> {
4
+ initialValues?: T;
5
+ values?: T;
6
+ children: ReactNode;
7
+ onSubmit: (values: T) => Promise<any>;
8
+ mode?: 'onChange' | 'onBlur' | 'onSubmit' | 'all';
9
+ reValidateMode?: 'onChange' | 'onBlur' | 'onSubmit';
10
+ errors?: any;
11
+ resetOptions?: Record<string, any>;
12
+ criteriaMode?: 'firstError' | 'all';
13
+ shouldFocusError?: boolean;
14
+ delayError?: number;
15
+ shouldUseNativeValidation?: boolean;
16
+ shouldUnregister?: boolean;
17
+ className?: string;
18
+ }
19
+ declare const BaseForm: {
20
+ <T extends FieldValues>({ initialValues: defaultValues, values, children, onSubmit: onFinish, mode, reValidateMode, errors, resetOptions, criteriaMode, shouldFocusError, delayError, shouldUseNativeValidation, shouldUnregister, className, }: BaseFormProps<T>): React.JSX.Element;
21
+ displayName: string;
22
+ };
1
23
  export default BaseForm;
2
- declare function BaseForm({ initialValues: defaultValues, values, children, onSubmit, layout, mode, reValidateMode, errors, resetOptions, criteriaMode, shouldFocusError, delayError, shouldUseNativeValidation, shouldUnregister, }: {
3
- initialValues: any;
4
- values: any;
5
- children: any;
6
- onSubmit: any;
7
- layout?: string;
8
- mode?: string;
9
- reValidateMode: any;
10
- errors: any;
11
- resetOptions: any;
12
- criteriaMode: any;
13
- shouldFocusError: any;
14
- delayError: any;
15
- shouldUseNativeValidation: any;
16
- shouldUnregister: any;
17
- }): React.JSX.Element;
18
- import * as React from "react";
@@ -1,66 +1,52 @@
1
- import * as React from 'react';
2
- import { FormProvider, useForm } from "react-hook-form";
3
- import styled from 'styled-components';
4
- // initialValues: object || async () => fetch('/api-endpoint')
5
- // values: object
6
- // children: React elements
7
- // onSubmit: func (values) => {}
8
- // layout: vertical | horizontal
9
- // mode: onChange | onBlur | onSubmit | onTouched | all = 'onSubmit'
10
- // reValidateMode: onChange | onBlur | onSubmit = 'onChange'
11
- // errors: Reactive errors to update the form errors.
12
- // resetOptions: Option to reset form state update while updating new form values.
13
- // criteriaMode: Display all validation errors or one at a time. (firstError | all)
14
- // shouldFocusError: Enable or disable built-in focus management.
15
- // delayError: Delay error from appearing instantly.
16
- // shouldUseNativeValidation: Use browser built-in form constraint API.
17
- // shouldUnregister: Enable and disable input unregister after unmount.
18
- /**
19
- * defaultValues are cached. To reset them, use the reset API.
20
- * defaultValues will be included in the submission result by default.
21
- * values will get updated when values props updates
22
- * resetOptions: {
23
- keepDirtyValues: true, // user-interacted input will be retained
24
- keepErrors: true, // input errors will be retained with value update
25
- },
26
- * criteriaMode:{
27
- When set to firstError (default), only the first error from each field will be gathered.
28
- When set to all, all errors from each field will be gathered.
29
- }
30
- * shouldFocusError: When set to true (default), and the user submits a form that fails validation, focus is set on the first field with an error.
31
- * delayError: number
32
- *
33
- */
34
- const StyledForm = styled.form `
35
- &.vertical {
36
- .form-item-row {
37
- flex-direction: column;
38
- }
39
- }
40
-
41
- .form-item-row {
42
- display: flex;
43
- }
44
-
45
- .form-item {
46
- margin-bottom: 24px;
47
- }
48
- `;
49
- const BaseForm = ({ initialValues: defaultValues, values, children, onSubmit, layout = 'vertical', mode = 'onChange', reValidateMode, errors, resetOptions, criteriaMode, shouldFocusError, delayError, shouldUseNativeValidation, shouldUnregister, }) => {
1
+ import React, { useCallback } from 'react';
2
+ import { FormProvider, useForm } from 'react-hook-form';
3
+ import { Text } from '@weareconceptstudio/core';
4
+ import classNames from 'classnames';
5
+ const BaseForm = ({ initialValues: defaultValues, values, children, onSubmit: onFinish, mode = 'onChange', reValidateMode = 'onChange', errors, resetOptions, criteriaMode, shouldFocusError, delayError = 0, shouldUseNativeValidation, shouldUnregister, className, }) => {
50
6
  const methods = useForm({
51
- defaultValues,
52
- values,
53
7
  mode,
54
- reValidateMode,
55
8
  errors,
9
+ values,
10
+ delayError,
56
11
  resetOptions,
57
12
  criteriaMode,
13
+ // @ts-ignore
14
+ defaultValues,
15
+ reValidateMode,
58
16
  shouldFocusError,
59
- delayError,
60
- shouldUseNativeValidation,
61
17
  shouldUnregister,
18
+ shouldUseNativeValidation,
62
19
  });
20
+ const onSubmit = useCallback((values) => {
21
+ return onFinish(values).catch(({ data, status, ...err }) => {
22
+ if (status === 422) {
23
+ for (const key in data.errors) {
24
+ if (data.errors.hasOwnProperty(key)) {
25
+ // @ts-ignore
26
+ methods.setError(key, {
27
+ type: 'manual',
28
+ message: data.errors[key][0],
29
+ });
30
+ }
31
+ }
32
+ }
33
+ else {
34
+ methods.setError('root.serverError', {
35
+ type: String(status),
36
+ message: data?.message || 'serverError',
37
+ });
38
+ }
39
+ });
40
+ }, [methods, onFinish]);
63
41
  return (React.createElement(FormProvider, { ...methods },
64
- React.createElement(StyledForm, { onSubmit: methods.handleSubmit(onSubmit), className: layout }, children)));
42
+ methods.formState.errors.root?.serverError && (React.createElement("div", { className: "global-error-wrap" },
43
+ React.createElement(Text, { className: 'backend-error', text: methods.formState.errors.root?.serverError.message }),
44
+ React.createElement(Text, { className: 'frontend-error', text: 'globalError' }))),
45
+ React.createElement("form", { onSubmit: methods.handleSubmit(onSubmit), className: classNames({
46
+ [className]: !!className,
47
+ }) }, children)));
65
48
  };
49
+ if (process.env.NODE_ENV !== 'production') {
50
+ BaseForm.displayName = 'BaseForm';
51
+ }
66
52
  export default BaseForm;
@@ -0,0 +1,9 @@
1
+ export default FormBuilder;
2
+ declare function FormBuilder({ className, onSubmit, formOptions, fields, children }: {
3
+ className?: string;
4
+ onSubmit: any;
5
+ formOptions?: {};
6
+ fields?: any[];
7
+ children: any;
8
+ }): React.JSX.Element;
9
+ import React from 'react';
@@ -0,0 +1,46 @@
1
+ import React, { useMemo, cloneElement, isValidElement } from 'react';
2
+ import classNames from 'classnames';
3
+ //* Components
4
+ import Form from '../';
5
+ import Input from '../../input';
6
+ import Checkbox from '../../checkbox';
7
+ import Radio from '../../radio';
8
+ import PhoneNumber from '../../phone-number';
9
+ import Select from '../../select';
10
+ import Upload from '../../upload';
11
+ import DatePicker from '../../date-picker';
12
+ import ColorPicker from '../../color-picker';
13
+ //* Style
14
+ import BuilderStyle from './style';
15
+ const formElements = {
16
+ 'input': React.createElement(Input, null),
17
+ 'password': React.createElement(Input.Password, null),
18
+ 'textarea': React.createElement(Input.TextArea, null),
19
+ 'number': React.createElement(Input.Number, null),
20
+ 'checkbox': React.createElement(Checkbox, null),
21
+ 'radio': React.createElement(Radio, null),
22
+ 'phone': React.createElement(PhoneNumber, null),
23
+ 'select': React.createElement(Select, null),
24
+ 'upload': React.createElement(Upload, null),
25
+ 'date': React.createElement(DatePicker, null),
26
+ 'color': React.createElement(ColorPicker, null),
27
+ };
28
+ const FormBuilder = ({ className = '', onSubmit, formOptions = {}, fields = [], children }) => {
29
+ const elements = useMemo(() => {
30
+ return fields.map((field, index) => {
31
+ if (field.fieldType == 'empty') {
32
+ return (React.createElement("div", { className: 'empty-form-item', ...field.fieldProps }));
33
+ }
34
+ else {
35
+ return isValidElement(formElements[field.fieldType]) ? (React.createElement(Form.Item, { key: index, ...field.labelProps }, cloneElement(formElements[field.fieldType], field.fieldProps))) : null;
36
+ }
37
+ });
38
+ }, [fields]);
39
+ const classString = classNames('form-builder', {
40
+ [className]: className,
41
+ });
42
+ return (React.createElement(Form, { ...formOptions, onSubmit: onSubmit },
43
+ React.createElement(BuilderStyle, { className: classString }, elements),
44
+ children));
45
+ };
46
+ export default FormBuilder;
@@ -0,0 +1,2 @@
1
+ export default BuilderStyle;
2
+ declare const BuilderStyle: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
@@ -0,0 +1,3 @@
1
+ import styled from 'styled-components';
2
+ const BuilderStyle = styled.div ``;
3
+ export default BuilderStyle;
@@ -1,11 +1,13 @@
1
- export default FormItem;
2
- declare function FormItem({ label, name, children, rules }: {
3
- label: any;
4
- name: any;
5
- children: any;
6
- rules?: {};
7
- }): React.JSX.Element;
8
- declare namespace FormItem {
9
- const displayName: string;
1
+ import React, { ReactNode } from 'react';
2
+ interface FormItemProps {
3
+ label?: string;
4
+ name: string;
5
+ children: ReactNode;
6
+ className?: string;
7
+ errorKey?: string;
8
+ required?: boolean;
9
+ requiredMessage?: string;
10
+ rules?: Array<any>;
10
11
  }
11
- import React from "react";
12
+ declare const FormItem: React.FC<FormItemProps>;
13
+ export default FormItem;
@@ -1,23 +1,30 @@
1
- import React from 'react';
2
- //* HOC's
3
- import { useFormContext } from "react-hook-form";
4
- //* Components
1
+ import React, { cloneElement, isValidElement } from 'react';
2
+ import lodashGet from 'lodash/get';
3
+ import classNames from 'classnames';
4
+ import { useTranslation } from '@weareconceptstudio/core';
5
5
  import ErrorMessage from '../../error-message';
6
- const FormItem = ({ label, name, children, rules = {} }) => {
7
- const { register, getValues } = useFormContext();
8
- const innerRef = register(name, rules);
9
- return (React.createElement("div", { className: "form-item" },
6
+ import { useInput } from '../hooks/useInput';
7
+ import { useRules } from '../hooks/rules';
8
+ const FormItem = ({ label, name, children, className, errorKey, required = true, requiredMessage, rules = [], }) => {
9
+ const { translate } = useTranslation();
10
+ const { formState } = useInput({
11
+ name,
12
+ rules: useRules({ rules, required, requiredMessage }),
13
+ errorKey: errorKey || label,
14
+ });
15
+ const hasError = !!lodashGet(formState.errors, name);
16
+ const classString = classNames('form-item', {
17
+ [className]: className,
18
+ 'has-error': hasError,
19
+ disabled: isValidElement(children) && children.props.disabled,
20
+ });
21
+ return (React.createElement("div", { className: classString },
10
22
  React.createElement("div", { className: "form-item-row" },
11
- React.createElement("div", { className: "form-item-label" },
12
- React.createElement("label", { htmlFor: name }, label)),
13
- React.createElement("div", { className: 'form-item-control' }, React.cloneElement(children, {
14
- ref: innerRef,
15
- name,
16
- value: getValues()[name]
17
- })),
18
- React.createElement(ErrorMessage, { name: name, as: 'span' }))));
23
+ label ? (React.createElement("div", { className: "form-item-label" },
24
+ React.createElement("label", { htmlFor: name, "data-required": required },
25
+ translate(label),
26
+ required ? React.createElement("span", { className: "required-symbol" }, "*") : null))) : null,
27
+ React.createElement("div", { className: "form-item-control" }, isValidElement(children) && cloneElement(children, { name })),
28
+ React.createElement(ErrorMessage, { name: name, as: React.createElement("div", { className: "error-message" }) }))));
19
29
  };
20
- if (process.env.NODE_ENV !== 'production') {
21
- FormItem.displayName = 'FormItem';
22
- }
23
30
  export default FormItem;
@@ -0,0 +1,3 @@
1
+ export function validationPatterns(type: any, pattern: any): any;
2
+ export function useRules(props: any): any[];
3
+ export function mapValidationRules(errorKey: any, rules: any): any;
@@ -0,0 +1,44 @@
1
+ import React, { useMemo } from 'react';
2
+ import { useTranslation } from '@weareconceptstudio/core';
3
+ export const validationPatterns = (type, pattern) => {
4
+ switch (type) {
5
+ case 'email':
6
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/g;
7
+ case 'number':
8
+ return /[0-9]/g;
9
+ default:
10
+ return pattern;
11
+ }
12
+ };
13
+ export const useRules = (props) => {
14
+ return useMemo(() => {
15
+ let r = [...props.rules];
16
+ if (props.required) {
17
+ r.unshift({ required: true, message: props.requiredMessage });
18
+ }
19
+ return r;
20
+ }, [props]);
21
+ };
22
+ export const mapValidationRules = (errorKey, rules) => {
23
+ const { translate } = useTranslation();
24
+ return rules?.reduce((acc, rule) => {
25
+ if (rule.required) {
26
+ acc.required = rule.message || translate('validateMessages.required', { errorKey: translate(errorKey) });
27
+ }
28
+ if (rule.pattern || rule.type) {
29
+ acc.pattern = { value: validationPatterns(rule.type, rule.pattern), message: rule.message || 'Invalid format' };
30
+ }
31
+ if (rule.minLength) {
32
+ acc.minLength = { value: rule.minLength, message: rule.message || `Minimum length is ${rule.minLength}` };
33
+ }
34
+ if (rule.maxLength) {
35
+ acc.maxLength = { value: rule.maxLength, message: rule.message || `Maximum length is ${rule.maxLength}` };
36
+ }
37
+ if (rule.validator) {
38
+ acc.validate = async (value) => {
39
+ return (await rule.validator(value)) || rule.message || 'Validation failed';
40
+ };
41
+ }
42
+ return acc;
43
+ }, {});
44
+ };
@@ -0,0 +1 @@
1
+ export declare const useEvent: <Args extends unknown[], Return>(fn: (...args: Args) => Return) => (...args: Args) => Return;
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ import { useCallback } from 'react';
3
+ const useLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
4
+ export const useEvent = (fn) => {
5
+ const ref = React.useRef(() => {
6
+ throw new Error('Cannot call an event handler while rendering.');
7
+ });
8
+ useLayoutEffect(() => {
9
+ ref.current = fn;
10
+ });
11
+ return useCallback((...args) => ref.current(...args), []);
12
+ };
@@ -1,6 +1,9 @@
1
- import * as React from 'react';
1
+ import React from 'react';
2
2
  import { useFormContext } from 'react-hook-form';
3
3
  export default function useFormInstance() {
4
4
  const methods = useFormContext();
5
+ if (methods == null) {
6
+ return null;
7
+ }
5
8
  return { ...methods };
6
9
  }
@@ -0,0 +1,12 @@
1
+ export declare const useInput: (props: any) => {
2
+ field: {
3
+ value: any;
4
+ onBlur: (...args: unknown[]) => void;
5
+ onChange: (...args: any[]) => void;
6
+ disabled?: boolean;
7
+ name: any;
8
+ ref: import("react-hook-form").RefCallBack;
9
+ };
10
+ fieldState: import("react-hook-form").ControllerFieldState;
11
+ formState: import("react-hook-form").UseFormStateReturn<import("react-hook-form").FieldValues>;
12
+ };
@@ -0,0 +1,46 @@
1
+ import { useController } from 'react-hook-form';
2
+ import { useEvent } from './useEvent';
3
+ import { mapValidationRules } from './rules';
4
+ const defaultFormat = (value) => (value == null ? '' : value);
5
+ const defaultParse = (value) => (value === '' ? null : value);
6
+ export const useInput = (props) => {
7
+ const { defaultValue, name, onBlur: initialOnBlur, onChange: initialOnChange, format = defaultFormat, parse = defaultParse, multiple, errorKey, rules = [] } = props;
8
+ const { field: controllerField, fieldState, formState, } = useController({
9
+ name,
10
+ defaultValue,
11
+ rules: mapValidationRules(errorKey, rules),
12
+ });
13
+ const onBlur = useEvent((...event) => {
14
+ controllerField.onBlur();
15
+ if (initialOnBlur) {
16
+ initialOnBlur(...event);
17
+ }
18
+ });
19
+ const onChange = useEvent((...event) => {
20
+ let eventOrValue;
21
+ if (props.type == 'checkbox' && event[0]?.target?.value === 'on') {
22
+ eventOrValue = event[0].target.checked;
23
+ }
24
+ else if (props.type == 'select') {
25
+ eventOrValue = multiple ? event[0].map((ev) => ev.value) : event[0]?.value;
26
+ }
27
+ else {
28
+ eventOrValue = event[0]?.target?.value ?? event[0];
29
+ }
30
+ controllerField.onChange(parse ? parse(eventOrValue) : eventOrValue);
31
+ if (initialOnChange) {
32
+ initialOnChange(...event);
33
+ }
34
+ });
35
+ const field = {
36
+ ...controllerField,
37
+ value: format ? format(controllerField.value) : controllerField.value,
38
+ onBlur,
39
+ onChange,
40
+ };
41
+ return {
42
+ field,
43
+ fieldState,
44
+ formState,
45
+ };
46
+ };