dynamic-modal 1.1.13 → 1.1.15

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 (32) hide show
  1. package/dist/components/input-upload/input-upload.js +4 -4
  2. package/dist/components/make-custom-upload/make-custom-upload.d.ts +4 -0
  3. package/dist/components/make-custom-upload/make-custom-upload.js +97 -0
  4. package/dist/components/make-watcher/make-watcher.d.ts +4 -0
  5. package/dist/components/make-watcher/make-watcher.js +62 -0
  6. package/dist/components/portal/portal.js +1 -1
  7. package/dist/hooks/use-enable-if.js +6 -4
  8. package/dist/hooks/use-render-if.js +6 -4
  9. package/dist/interfaces/component-state.d.ts +2 -0
  10. package/dist/interfaces/custom-upload.d.ts +12 -0
  11. package/dist/interfaces/custom-upload.js +2 -0
  12. package/dist/interfaces/make-custom-upload.d.ts +7 -0
  13. package/dist/interfaces/make-custom-upload.js +2 -0
  14. package/dist/interfaces/make-watcher.d.ts +8 -0
  15. package/dist/interfaces/make-watcher.js +2 -0
  16. package/dist/interfaces/modal.d.ts +4 -1
  17. package/dist/interfaces/portal.d.ts +1 -0
  18. package/dist/modal.js +7 -3
  19. package/package.json +1 -1
  20. package/src/components/input-upload/input-upload.tsx +7 -4
  21. package/src/components/make-custom-upload/make-custom-upload.tsx +100 -0
  22. package/src/components/make-watcher/make-watcher.tsx +48 -0
  23. package/src/components/portal/portal.tsx +3 -1
  24. package/src/hooks/use-enable-if.ts +6 -6
  25. package/src/hooks/use-render-if.ts +6 -6
  26. package/src/interfaces/component-state.ts +8 -0
  27. package/src/interfaces/custom-upload.ts +13 -0
  28. package/src/interfaces/make-custom-upload.ts +9 -0
  29. package/src/interfaces/make-watcher.ts +10 -0
  30. package/src/interfaces/modal.ts +5 -0
  31. package/src/interfaces/portal.ts +1 -0
  32. package/src/modal.tsx +28 -2
@@ -5,9 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const react_1 = __importDefault(require("react"));
8
- const InputUpload = ({ onChange, readAsArrayBuffer, ...props }) => {
8
+ const InputUpload = ({ onChange, readAsArrayBuffer, read, helpText, label, ...props }) => {
9
9
  const onChangeHandler = (event) => {
10
- if (props.read && event.target.files) {
10
+ if (read && event.target.files) {
11
11
  readFileBlob(event.target.files[0], false)
12
12
  .then((result) => onChange(result))
13
13
  .catch((err) => {
@@ -42,8 +42,8 @@ const InputUpload = ({ onChange, readAsArrayBuffer, ...props }) => {
42
42
  });
43
43
  };
44
44
  return (react_1.default.createElement("div", { className: "flex flex-col w-full gap-1 text-center" },
45
- props.label && (react_1.default.createElement("label", { className: "block mb-2 text-sm font-medium text-gray-900 dark:text-white", htmlFor: `file-input-${props.id}` }, props.label)),
45
+ label && (react_1.default.createElement("label", { className: "block mb-2 text-sm font-medium text-gray-900 dark:text-white", htmlFor: `file-input-${props.id}` }, label)),
46
46
  react_1.default.createElement("input", { className: "file:transition-all file:delay-150 block w-full text-sm text-slate-500\r\n file:mr-4 file:py-2 file:px-4 file:rounded-md\r\n file:border-0 file:text-sm file:font-semibold\r\n file:bg-gray-100 file:text-blue-600\r\n hover:file:bg-blue-700 hover:file:text-white cursor-pointer disabled:cursor-not-allowed", "aria-describedby": `file-input-${props.id}-help`, id: `file-input-${props.id}`, type: "file", onChange: onChangeHandler, ...props }),
47
- react_1.default.createElement("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-300 text-start", id: `file-input-${props.id}-help` }, props.helpText?.toUpperCase())));
47
+ react_1.default.createElement("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-300 text-start", id: `file-input-${props.id}-help` }, helpText?.toUpperCase())));
48
48
  };
49
49
  exports.default = InputUpload;
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+ import { IMakeCustomUploadProps } from '../../interfaces/make-custom-upload';
3
+ declare const MakeCustomUpload: FC<IMakeCustomUploadProps>;
4
+ export default MakeCustomUpload;
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ 'use client';
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const react_1 = __importStar(require("react"));
41
+ const react_hook_form_1 = require("react-hook-form");
42
+ const component_state_1 = require("../../context/component/component-state");
43
+ const use_enable_if_1 = __importDefault(require("../../hooks/use-enable-if"));
44
+ const use_render_if_1 = __importDefault(require("../../hooks/use-render-if"));
45
+ const MakeCustomUpload = ({ element, control, watch, unregister, }) => {
46
+ const { CustomUpload } = (0, react_1.useContext)(component_state_1.ComponentStateContext);
47
+ const { name: elementName, validation: { required, message, regex, ...otherValidation }, enableIf, renderIf, ...inputProps } = element;
48
+ const { checkEnable, enable, setEnable } = (0, use_enable_if_1.default)({
49
+ elementEnableIf: enableIf,
50
+ });
51
+ const { checkRender, render, setRender } = (0, use_render_if_1.default)({
52
+ elementRenderIf: renderIf,
53
+ });
54
+ const elementId = (0, react_1.useId)();
55
+ (0, react_1.useEffect)(() => {
56
+ const { unsubscribe } = watch((formData, { name, type }) => {
57
+ if (!name)
58
+ return;
59
+ if (enableIf) {
60
+ checkEnable(formData, { name, type }).then((enableStatus) => {
61
+ if (enableStatus === undefined || enableStatus === null)
62
+ return;
63
+ if (enable !== enableStatus)
64
+ setEnable(enableStatus);
65
+ });
66
+ }
67
+ if (renderIf) {
68
+ checkRender(formData, { name, type }).then((renderStatus) => {
69
+ if (renderStatus === undefined || renderStatus === null)
70
+ return;
71
+ if (render !== renderStatus) {
72
+ if (render && !renderStatus)
73
+ unregister(elementName);
74
+ setRender(!!renderStatus);
75
+ }
76
+ });
77
+ }
78
+ });
79
+ return () => unsubscribe();
80
+ }, [watch, render, enable]);
81
+ if (!render)
82
+ return null;
83
+ return (react_1.default.createElement(react_hook_form_1.Controller, { control: control, name: elementName, rules: {
84
+ required: {
85
+ value: required,
86
+ message: message ?? '',
87
+ },
88
+ pattern: regex
89
+ ? {
90
+ value: regex,
91
+ message: message ?? '',
92
+ }
93
+ : undefined,
94
+ ...otherValidation,
95
+ }, render: ({ field: { onChange, value }, fieldState: { invalid, error }, }) => (react_1.default.createElement(CustomUpload, { ...inputProps, name: elementName, id: elementId, onChange: onChange, value: value ?? '', invalid: invalid, error: error, disabled: element.disabled ?? !enable })) }));
96
+ };
97
+ exports.default = MakeCustomUpload;
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+ import { IMakeWatcherProps } from '../../interfaces/make-watcher';
3
+ declare const MakeWatcher: FC<IMakeWatcherProps>;
4
+ export default MakeWatcher;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ 'use client';
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const react_1 = __importStar(require("react"));
38
+ const component_state_1 = require("../../context/component/component-state");
39
+ const MakeWatcher = ({ element, watch }) => {
40
+ const [value, setValue] = (0, react_1.useState)('');
41
+ const { Input } = (0, react_1.useContext)(component_state_1.ComponentStateContext);
42
+ const { watchList, style } = element;
43
+ const elementId = (0, react_1.useId)();
44
+ (0, react_1.useEffect)(() => {
45
+ const { unsubscribe } = watch((formData, { name }) => {
46
+ if (!name)
47
+ return;
48
+ if (watchList.includes(name)) {
49
+ const watchValue = [];
50
+ watchList.forEach((watchName) => {
51
+ const fragment = formData[watchName];
52
+ if (fragment)
53
+ watchValue.push(fragment);
54
+ });
55
+ setValue(watchValue.join(' '));
56
+ }
57
+ });
58
+ return () => unsubscribe();
59
+ }, [watch]);
60
+ return (react_1.default.createElement(Input, { name: `watcher-${elementId}`, id: elementId, value: value ?? '', onChange: () => { }, invalid: false, disabled: true, style: style }));
61
+ };
62
+ exports.default = MakeWatcher;
@@ -59,6 +59,6 @@ const Portal = (props) => {
59
59
  }, [mounted, props.closeTime, props.portalOpen, props.portalTag]);
60
60
  if (!mounted && !ref.current)
61
61
  return null;
62
- return (0, react_dom_1.createPortal)(react_1.default.createElement("div", { className: "transition-all delay-100 fixed top-0 left-0 w-full h-full grid place-items-center bg-black bg-opacity-40 z-20" }, props.children), ref.current);
62
+ return (0, react_dom_1.createPortal)(react_1.default.createElement("div", { className: `transition-all delay-100 fixed top-0 left-0 w-full h-full grid place-items-center bg-black bg-opacity-40 z-20 ${props.useBlur && 'backdrop-blur-xs'}` }, props.children), ref.current);
63
63
  };
64
64
  exports.Portal = Portal;
@@ -9,10 +9,12 @@ const useEnableIf = ({ elementEnableIf }) => {
9
9
  });
10
10
  const checkEnable = async (formData, { name }) => {
11
11
  const targetFieldData = formData[name];
12
- if (targetFieldData === undefined ||
13
- targetFieldData === '' ||
14
- targetFieldData === null)
15
- return false;
12
+ // if (
13
+ // targetFieldData === undefined ||
14
+ // targetFieldData === '' ||
15
+ // targetFieldData === null
16
+ // )
17
+ // return false;
16
18
  const enableIfConfig = elementEnableIf;
17
19
  const isLiveEnable = !!enableIfConfig?.action && !!enableIfConfig.condition;
18
20
  if (isLiveEnable && enableIfConfig.condition.includes(name)) {
@@ -9,10 +9,12 @@ const useRenderIf = ({ elementRenderIf }) => {
9
9
  });
10
10
  const checkRender = async (formData, { name }) => {
11
11
  const targetFieldData = formData[name];
12
- if (targetFieldData === undefined ||
13
- targetFieldData === '' ||
14
- targetFieldData === null)
15
- return false;
12
+ // if (
13
+ // targetFieldData === undefined ||
14
+ // targetFieldData === '' ||
15
+ // targetFieldData === null
16
+ // )
17
+ // return false;
16
18
  const renderIfConfig = elementRenderIf;
17
19
  const isLiveRender = !!renderIfConfig?.action && !!renderIfConfig?.condition;
18
20
  if (isLiveRender && renderIfConfig.condition.includes(name)) {
@@ -6,6 +6,7 @@ import { IMakeButton } from './make-button';
6
6
  import { IMakeSelect } from './make-select';
7
7
  import { IMakeTextarea } from './make-textarea';
8
8
  import { IMakeToggle } from './make-toggle';
9
+ import { IMakeCustomUpload } from './make-custom-upload';
9
10
  export interface IComponentAditionalProps {
10
11
  onChange: (...event: any[]) => void;
11
12
  value: any;
@@ -18,6 +19,7 @@ export interface IComponentState {
18
19
  ModalButtonAction: FC<Omit<IMakeButton, 'elementType'>>;
19
20
  Button: FC<Omit<IMakeButton, 'elementType'>>;
20
21
  Input: FC<Omit<IMakeInput, 'elementType' | 'validation' | 'renderIf' | 'enableIf'> & IComponentAditionalProps>;
22
+ CustomUpload: FC<Omit<IMakeCustomUpload, 'elementType' | 'validation' | 'renderIf' | 'enableIf'> & IComponentAditionalProps>;
21
23
  Select: FC<Omit<IMakeSelect, 'elementType' | 'validation' | 'renderIf' | 'enableIf'> & IComponentAditionalProps & Record<'options', Array<IOption>>>;
22
24
  Textarea: FC<Omit<IMakeTextarea, 'elementType' | 'validation' | 'renderIf' | 'enableIf'> & IComponentAditionalProps>;
23
25
  Toggle: FC<Omit<IMakeToggle, 'elementType' | 'validation' | 'renderIf' | 'enableIf'> & IComponentAditionalProps>;
@@ -0,0 +1,12 @@
1
+ import { ChangeEvent, CSSProperties } from 'react';
2
+ export interface ICustomUpload {
3
+ id?: string;
4
+ value?: string;
5
+ onChange: (event: ChangeEvent<HTMLInputElement>) => void;
6
+ accept?: string;
7
+ label?: string;
8
+ helpText?: string;
9
+ style?: CSSProperties;
10
+ name: string;
11
+ disabled?: boolean;
12
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,7 @@
1
+ import { IField, IFieldProps } from './field';
2
+ export interface IMakeCustomUpload extends IField {
3
+ elementType: 'custom-upload';
4
+ }
5
+ export interface IMakeCustomUploadProps extends IFieldProps {
6
+ element: Omit<IMakeCustomUpload, 'elementType'>;
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ import { IField, IFieldProps } from './field';
2
+ export interface IMakeWatcher extends Pick<IField, 'style'> {
3
+ elementType: 'watcher';
4
+ watchList: Array<string>;
5
+ }
6
+ export interface IMakeWatcherProps extends IFieldProps {
7
+ element: Omit<IMakeWatcher, 'elementType'>;
8
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -8,7 +8,9 @@ import { IMakeFieldGroup } from './make-field-group';
8
8
  import { IMakeUpload } from './make-upload';
9
9
  import { IOption } from './option';
10
10
  import { IMakeButton } from './make-button';
11
- export type IModalField = IMakeSelect | IMakeInput | IMakeFieldGroup | IMakeTextarea | IMakeToggle | IMakeDescription | IMakeUpload | IMakeButton;
11
+ import { IMakeCustomUpload } from './make-custom-upload';
12
+ import { IMakeWatcher } from './make-watcher';
13
+ export type IModalField = IMakeSelect | IMakeInput | IMakeFieldGroup | IMakeTextarea | IMakeToggle | IMakeDescription | IMakeUpload | IMakeCustomUpload | IMakeWatcher | IMakeButton;
12
14
  export type IFormField = IMakeSelect | IMakeInput | IMakeTextarea | IMakeToggle;
13
15
  export interface IModalRenderAction {
14
16
  action: (data: string, ...args: any[]) => Promise<boolean>;
@@ -30,6 +32,7 @@ export interface IModalConfigProps {
30
32
  overFlowBody?: string | number;
31
33
  minHeightBody?: string | number;
32
34
  useSubmit?: boolean;
35
+ useBlur?: boolean;
33
36
  actions: {
34
37
  containerStyle?: CSSProperties;
35
38
  cancel?: Omit<IMakeButton, 'elementType'>;
@@ -4,4 +4,5 @@ export interface IPortal {
4
4
  closeTime: number;
5
5
  portalOpen: boolean;
6
6
  portalTag?: string;
7
+ useBlur?: boolean;
7
8
  }
package/dist/modal.js CHANGED
@@ -49,13 +49,15 @@ const make_description_1 = __importDefault(require("./components/make-descriptio
49
49
  const make_upload_1 = __importDefault(require("./components/make-upload/make-upload"));
50
50
  const make_button_1 = __importDefault(require("./components/make-button/make-button"));
51
51
  const component_state_1 = require("./context/component/component-state");
52
+ const make_custom_upload_1 = __importDefault(require("./components/make-custom-upload/make-custom-upload"));
53
+ const make_watcher_1 = __importDefault(require("./components/make-watcher/make-watcher"));
52
54
  const Modal = ({ open, close, config }) => {
53
55
  const { ModalButtonAction, ModalButtonCancel } = (0, react_1.useContext)(component_state_1.ComponentStateContext);
54
56
  const [modalReady, setModalReady] = (0, react_1.useState)(undefined);
55
57
  const [defaultLoaded, setDefaultLoaded] = (0, react_1.useState)(false);
56
58
  const { control, handleSubmit, getValues, unregister, setValue, watch, trigger, reset, getFieldState, } = (0, react_hook_form_1.useForm)();
57
59
  const formValueHandler = (element) => {
58
- if (['group', 'upload', 'text'].includes(element.elementType))
60
+ if (['group', 'upload', 'custom-upload', 'text'].includes(element.elementType))
59
61
  return;
60
62
  if (!element.defaultValue && element.renderIf) {
61
63
  unregister(element.name);
@@ -103,7 +105,7 @@ const Modal = ({ open, close, config }) => {
103
105
  setValue,
104
106
  unregister,
105
107
  };
106
- return elementType === 'input' ? (react_1.default.createElement(make_input_1.default, { ...props, key: `modal-input-${index}`, element: element })) : elementType === 'select' ? (react_1.default.createElement(make_select_1.default, { ...props, key: `modal-select-${index}`, element: element })) : elementType === 'textarea' ? (react_1.default.createElement(make_textarea_1.default, { ...props, key: `modal-textarea-${index}`, element: element })) : elementType === 'toggle' ? (react_1.default.createElement(make_toggle_1.default, { ...props, key: `modal-toggle-${index}`, element: element })) : elementType === 'text' ? (react_1.default.createElement(make_description_1.default, { ...props, key: `modal-text-${index}`, element: element })) : elementType === 'upload' ? (react_1.default.createElement(make_upload_1.default, { ...props, key: `modal-upload-${index}`, element: element })) : elementType === 'button' ? (react_1.default.createElement(make_button_1.default, { ...props, key: `modal-button-${index}`, element: element })) : null;
108
+ return elementType === 'input' ? (react_1.default.createElement(make_input_1.default, { ...props, key: `modal-input-${index}`, element: element })) : elementType === 'select' ? (react_1.default.createElement(make_select_1.default, { ...props, key: `modal-select-${index}`, element: element })) : elementType === 'textarea' ? (react_1.default.createElement(make_textarea_1.default, { ...props, key: `modal-textarea-${index}`, element: element })) : elementType === 'toggle' ? (react_1.default.createElement(make_toggle_1.default, { ...props, key: `modal-toggle-${index}`, element: element })) : elementType === 'text' ? (react_1.default.createElement(make_description_1.default, { ...props, key: `modal-text-${index}`, element: element })) : elementType === 'upload' ? (react_1.default.createElement(make_upload_1.default, { ...props, key: `modal-upload-${index}`, element: element })) : elementType === 'custom-upload' ? (react_1.default.createElement(make_custom_upload_1.default, { ...props, key: `modal-custom-upload-${index}`, element: element })) : elementType === 'watcher' ? (react_1.default.createElement(make_watcher_1.default, { ...props, key: `modal-watcher-${index}`, element: element })) : elementType === 'button' ? (react_1.default.createElement(make_button_1.default, { ...props, key: `modal-button-${index}`, element: element })) : null;
107
109
  };
108
110
  const closeHandler = () => {
109
111
  if (modalReady?.onClose)
@@ -140,7 +142,7 @@ const Modal = ({ open, close, config }) => {
140
142
  }, [config, modalReady, open]);
141
143
  if (!modalReady)
142
144
  return null;
143
- return (react_1.default.createElement(portal_1.Portal, { closeTime: 200, portalOpen: open, portalTag: '#modal-portal' },
145
+ return (react_1.default.createElement(portal_1.Portal, { closeTime: 200, portalOpen: open, portalTag: '#modal-portal', useBlur: modalReady.useBlur },
144
146
  react_1.default.createElement("div", { className: "rounded bg-white relative w-auto h-auto min-h-[200px] min-w-[500px]", style: modalReady.style },
145
147
  react_1.default.createElement("form", { className: "flex flex-col p-4 gap-4", autoComplete: "off", onSubmit: handleSubmit(actionHandler) },
146
148
  react_1.default.createElement("h2", { className: "text-bold text-center border-b pb-4 font-semibold" }, modalReady.title),
@@ -160,6 +162,8 @@ const Modal = ({ open, close, config }) => {
160
162
  'toggle',
161
163
  'multiselect',
162
164
  'upload',
165
+ 'custom-upload',
166
+ 'watcher',
163
167
  'button',
164
168
  'autocomplete',
165
169
  ].includes(sub.elementType))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dynamic-modal",
3
- "version": "1.1.13",
3
+ "version": "1.1.15",
4
4
  "description": "The dynamic-modal is a solution of creation different modals into project using a json configuration file",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -7,10 +7,13 @@ import { IFileResult, IInputUpload } from '../../interfaces/input-upload';
7
7
  const InputUpload: FC<IInputUpload> = ({
8
8
  onChange,
9
9
  readAsArrayBuffer,
10
+ read,
11
+ helpText,
12
+ label,
10
13
  ...props
11
14
  }: IInputUpload) => {
12
15
  const onChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
13
- if (props.read && event.target.files) {
16
+ if (read && event.target.files) {
14
17
  readFileBlob(event.target.files[0], false)
15
18
  .then((result) => onChange(result))
16
19
  .catch((err) => {
@@ -46,12 +49,12 @@ const InputUpload: FC<IInputUpload> = ({
46
49
 
47
50
  return (
48
51
  <div className="flex flex-col w-full gap-1 text-center">
49
- {props.label && (
52
+ {label && (
50
53
  <label
51
54
  className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
52
55
  htmlFor={`file-input-${props.id}`}
53
56
  >
54
- {props.label}
57
+ {label}
55
58
  </label>
56
59
  )}
57
60
  <input
@@ -70,7 +73,7 @@ const InputUpload: FC<IInputUpload> = ({
70
73
  className="mt-1 text-sm text-gray-500 dark:text-gray-300 text-start"
71
74
  id={`file-input-${props.id}-help`}
72
75
  >
73
- {props.helpText?.toUpperCase()}
76
+ {helpText?.toUpperCase()}
74
77
  </p>
75
78
  </div>
76
79
  );
@@ -0,0 +1,100 @@
1
+ 'use client';
2
+
3
+ import React, { FC, useContext, useEffect, useId } from 'react';
4
+ import { Controller } from 'react-hook-form';
5
+
6
+ import { ComponentStateContext } from '../../context/component/component-state';
7
+ import useEnableIf from '../../hooks/use-enable-if';
8
+ import useRenderIf from '../../hooks/use-render-if';
9
+ import { IMakeCustomUploadProps } from '../../interfaces/make-custom-upload';
10
+
11
+ const MakeCustomUpload: FC<IMakeCustomUploadProps> = ({
12
+ element,
13
+ control,
14
+ watch,
15
+ unregister,
16
+ }) => {
17
+ const { CustomUpload } = useContext(ComponentStateContext);
18
+
19
+ const {
20
+ name: elementName,
21
+ validation: { required, message, regex, ...otherValidation },
22
+ enableIf,
23
+ renderIf,
24
+ ...inputProps
25
+ } = element;
26
+
27
+ const { checkEnable, enable, setEnable } = useEnableIf({
28
+ elementEnableIf: enableIf,
29
+ });
30
+ const { checkRender, render, setRender } = useRenderIf({
31
+ elementRenderIf: renderIf,
32
+ });
33
+
34
+ const elementId = useId();
35
+
36
+ useEffect(() => {
37
+ const { unsubscribe } = watch((formData, { name, type }) => {
38
+ if (!name) return;
39
+
40
+ if (enableIf) {
41
+ checkEnable(formData, { name, type }).then((enableStatus) => {
42
+ if (enableStatus === undefined || enableStatus === null) return;
43
+ if (enable !== enableStatus) setEnable(enableStatus);
44
+ });
45
+ }
46
+
47
+ if (renderIf) {
48
+ checkRender(formData, { name, type }).then((renderStatus) => {
49
+ if (renderStatus === undefined || renderStatus === null) return;
50
+
51
+ if (render !== renderStatus) {
52
+ if (render && !renderStatus) unregister(elementName as string);
53
+ setRender(!!renderStatus);
54
+ }
55
+ });
56
+ }
57
+ });
58
+
59
+ return () => unsubscribe();
60
+ }, [watch, render, enable]);
61
+
62
+ if (!render) return null;
63
+
64
+ return (
65
+ <Controller
66
+ control={control}
67
+ name={elementName}
68
+ rules={{
69
+ required: {
70
+ value: required,
71
+ message: message ?? '',
72
+ },
73
+ pattern: regex
74
+ ? {
75
+ value: regex,
76
+ message: message ?? '',
77
+ }
78
+ : undefined,
79
+ ...otherValidation,
80
+ }}
81
+ render={({
82
+ field: { onChange, value },
83
+ fieldState: { invalid, error },
84
+ }) => (
85
+ <CustomUpload
86
+ {...inputProps}
87
+ name={elementName}
88
+ id={elementId}
89
+ onChange={onChange}
90
+ value={value ?? ''}
91
+ invalid={invalid}
92
+ error={error}
93
+ disabled={element.disabled ?? !enable}
94
+ />
95
+ )}
96
+ />
97
+ );
98
+ };
99
+
100
+ export default MakeCustomUpload;
@@ -0,0 +1,48 @@
1
+ 'use client';
2
+
3
+ import React, { FC, useContext, useEffect, useId, useState } from 'react';
4
+
5
+ import { ComponentStateContext } from '../../context/component/component-state';
6
+ import { IMakeWatcherProps } from '../../interfaces/make-watcher';
7
+
8
+ const MakeWatcher: FC<IMakeWatcherProps> = ({ element, watch }) => {
9
+ const [value, setValue] = useState<string>('');
10
+ const { Input } = useContext(ComponentStateContext);
11
+
12
+ const { watchList, style } = element;
13
+
14
+ const elementId = useId();
15
+
16
+ useEffect(() => {
17
+ const { unsubscribe } = watch((formData, { name }) => {
18
+ if (!name) return;
19
+
20
+ if (watchList.includes(name)) {
21
+ const watchValue: Array<string> = [];
22
+
23
+ watchList.forEach((watchName) => {
24
+ const fragment: string = formData[watchName];
25
+ if (fragment) watchValue.push(fragment);
26
+ });
27
+
28
+ setValue(watchValue.join(' '));
29
+ }
30
+ });
31
+
32
+ return () => unsubscribe();
33
+ }, [watch]);
34
+
35
+ return (
36
+ <Input
37
+ name={`watcher-${elementId}`}
38
+ id={elementId}
39
+ value={value ?? ''}
40
+ onChange={() => {}}
41
+ invalid={false}
42
+ disabled={true}
43
+ style={style}
44
+ />
45
+ );
46
+ };
47
+
48
+ export default MakeWatcher;
@@ -31,7 +31,9 @@ export const Portal: FC<IPortal> = (props) => {
31
31
  if (!mounted && !ref.current) return null;
32
32
 
33
33
  return createPortal(
34
- <div className="transition-all delay-100 fixed top-0 left-0 w-full h-full grid place-items-center bg-black bg-opacity-40 z-20">
34
+ <div
35
+ className={`transition-all delay-100 fixed top-0 left-0 w-full h-full grid place-items-center bg-black bg-opacity-40 z-20 ${props.useBlur && 'backdrop-blur-xs'}`}
36
+ >
35
37
  {props.children}
36
38
  </div>,
37
39
  ref.current!,
@@ -24,12 +24,12 @@ const useEnableIf = ({ elementEnableIf }: IRenderIfProps) => {
24
24
  ) => {
25
25
  const targetFieldData = formData[name] as string | number;
26
26
 
27
- if (
28
- targetFieldData === undefined ||
29
- targetFieldData === '' ||
30
- targetFieldData === null
31
- )
32
- return false;
27
+ // if (
28
+ // targetFieldData === undefined ||
29
+ // targetFieldData === '' ||
30
+ // targetFieldData === null
31
+ // )
32
+ // return false;
33
33
 
34
34
  const enableIfConfig = elementEnableIf;
35
35
  const isLiveEnable: boolean =
@@ -24,12 +24,12 @@ const useRenderIf = ({ elementRenderIf }: IRenderIfProps) => {
24
24
  ) => {
25
25
  const targetFieldData = formData[name] as string | number;
26
26
 
27
- if (
28
- targetFieldData === undefined ||
29
- targetFieldData === '' ||
30
- targetFieldData === null
31
- )
32
- return false;
27
+ // if (
28
+ // targetFieldData === undefined ||
29
+ // targetFieldData === '' ||
30
+ // targetFieldData === null
31
+ // )
32
+ // return false;
33
33
 
34
34
  const renderIfConfig = elementRenderIf!;
35
35
  const isLiveRender: boolean =
@@ -7,6 +7,7 @@ import { IMakeButton } from './make-button';
7
7
  import { IMakeSelect } from './make-select';
8
8
  import { IMakeTextarea } from './make-textarea';
9
9
  import { IMakeToggle } from './make-toggle';
10
+ import { IMakeCustomUpload } from './make-custom-upload';
10
11
 
11
12
  export interface IComponentAditionalProps {
12
13
  onChange: (...event: any[]) => void;
@@ -25,6 +26,13 @@ export interface IComponentState {
25
26
  Omit<IMakeInput, 'elementType' | 'validation' | 'renderIf' | 'enableIf'> &
26
27
  IComponentAditionalProps
27
28
  >;
29
+ CustomUpload: FC<
30
+ Omit<
31
+ IMakeCustomUpload,
32
+ 'elementType' | 'validation' | 'renderIf' | 'enableIf'
33
+ > &
34
+ IComponentAditionalProps
35
+ >;
28
36
  Select: FC<
29
37
  Omit<IMakeSelect, 'elementType' | 'validation' | 'renderIf' | 'enableIf'> &
30
38
  IComponentAditionalProps &
@@ -0,0 +1,13 @@
1
+ import { ChangeEvent, CSSProperties } from 'react';
2
+
3
+ export interface ICustomUpload {
4
+ id?: string;
5
+ value?: string;
6
+ onChange: (event: ChangeEvent<HTMLInputElement>) => void;
7
+ accept?: string;
8
+ label?: string;
9
+ helpText?: string;
10
+ style?: CSSProperties;
11
+ name: string;
12
+ disabled?: boolean;
13
+ }
@@ -0,0 +1,9 @@
1
+ import { IField, IFieldProps } from './field';
2
+
3
+ export interface IMakeCustomUpload extends IField {
4
+ elementType: 'custom-upload';
5
+ }
6
+
7
+ export interface IMakeCustomUploadProps extends IFieldProps {
8
+ element: Omit<IMakeCustomUpload, 'elementType'>;
9
+ }
@@ -0,0 +1,10 @@
1
+ import { IField, IFieldProps } from './field';
2
+
3
+ export interface IMakeWatcher extends Pick<IField, 'style'> {
4
+ elementType: 'watcher';
5
+ watchList: Array<string>;
6
+ }
7
+
8
+ export interface IMakeWatcherProps extends IFieldProps {
9
+ element: Omit<IMakeWatcher, 'elementType'>;
10
+ }
@@ -9,6 +9,8 @@ import { IMakeFieldGroup } from './make-field-group';
9
9
  import { IMakeUpload } from './make-upload';
10
10
  import { IOption } from './option';
11
11
  import { IMakeButton } from './make-button';
12
+ import { IMakeCustomUpload } from './make-custom-upload';
13
+ import { IMakeWatcher } from './make-watcher';
12
14
 
13
15
  export type IModalField =
14
16
  | IMakeSelect
@@ -18,6 +20,8 @@ export type IModalField =
18
20
  | IMakeToggle
19
21
  | IMakeDescription
20
22
  | IMakeUpload
23
+ | IMakeCustomUpload
24
+ | IMakeWatcher
21
25
  | IMakeButton;
22
26
 
23
27
  export type IFormField = IMakeSelect | IMakeInput | IMakeTextarea | IMakeToggle;
@@ -49,6 +53,7 @@ export interface IModalConfigProps {
49
53
  overFlowBody?: string | number;
50
54
  minHeightBody?: string | number;
51
55
  useSubmit?: boolean;
56
+ useBlur?: boolean;
52
57
  actions: {
53
58
  containerStyle?: CSSProperties;
54
59
  cancel?: Omit<IMakeButton, 'elementType'>;
@@ -5,4 +5,5 @@ export interface IPortal {
5
5
  closeTime: number;
6
6
  portalOpen: boolean;
7
7
  portalTag?: string;
8
+ useBlur?: boolean;
8
9
  }
package/src/modal.tsx CHANGED
@@ -26,6 +26,10 @@ import { IMakeDescription } from './interfaces/make-description';
26
26
  import { IMakeUpload } from './interfaces/make-upload';
27
27
  import { IMakeButton } from './interfaces/make-button';
28
28
  import { ComponentStateContext } from './context/component/component-state';
29
+ import MakeCustomUpload from './components/make-custom-upload/make-custom-upload';
30
+ import { IMakeCustomUpload } from './interfaces/make-custom-upload';
31
+ import MakeWatcher from './components/make-watcher/make-watcher';
32
+ import { IMakeWatcher } from './interfaces/make-watcher';
29
33
 
30
34
  export const Modal = ({ open, close, config }: IModal) => {
31
35
  const { ModalButtonAction, ModalButtonCancel } = useContext(
@@ -49,7 +53,10 @@ export const Modal = ({ open, close, config }: IModal) => {
49
53
  } = useForm();
50
54
 
51
55
  const formValueHandler = (element: IFormField) => {
52
- if (['group', 'upload', 'text'].includes(element.elementType)) return;
56
+ if (
57
+ ['group', 'upload', 'custom-upload', 'text'].includes(element.elementType)
58
+ )
59
+ return;
53
60
  if (!element.defaultValue && element.renderIf) {
54
61
  unregister(element.name);
55
62
  return;
@@ -149,6 +156,18 @@ export const Modal = ({ open, close, config }: IModal) => {
149
156
  key={`modal-upload-${index}`}
150
157
  element={element as IMakeUpload}
151
158
  />
159
+ ) : elementType === 'custom-upload' ? (
160
+ <MakeCustomUpload
161
+ {...props}
162
+ key={`modal-custom-upload-${index}`}
163
+ element={element as IMakeCustomUpload}
164
+ />
165
+ ) : elementType === 'watcher' ? (
166
+ <MakeWatcher
167
+ {...props}
168
+ key={`modal-watcher-${index}`}
169
+ element={element as IMakeWatcher}
170
+ />
152
171
  ) : elementType === 'button' ? (
153
172
  <MakeButton
154
173
  {...props}
@@ -203,7 +222,12 @@ export const Modal = ({ open, close, config }: IModal) => {
203
222
  if (!modalReady) return null;
204
223
 
205
224
  return (
206
- <Portal closeTime={200} portalOpen={open} portalTag={'#modal-portal'}>
225
+ <Portal
226
+ closeTime={200}
227
+ portalOpen={open}
228
+ portalTag={'#modal-portal'}
229
+ useBlur={modalReady.useBlur}
230
+ >
207
231
  <div
208
232
  className="rounded bg-white relative w-auto h-auto min-h-[200px] min-w-[500px]"
209
233
  style={modalReady.style}
@@ -252,6 +276,8 @@ export const Modal = ({ open, close, config }: IModal) => {
252
276
  'toggle',
253
277
  'multiselect',
254
278
  'upload',
279
+ 'custom-upload',
280
+ 'watcher',
255
281
  'button',
256
282
  'autocomplete',
257
283
  ].includes(sub.elementType),