dynamic-modal 1.1.24 → 1.1.26
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.
- package/README.md +127 -0
- package/dist/components/make-select/make-select.js +5 -2
- package/dist/src/components/make-select/make-select.js +3 -1
- package/dist/src/hooks/use-conditional-field.js +2 -1
- package/dist/src/interfaces/component-state.d.ts +11 -1
- package/dist/src/interfaces/custom-upload.d.ts +4 -2
- package/dist/src/interfaces/field.d.ts +1 -0
- package/dist/src/interfaces/input-upload.d.ts +5 -3
- package/dist/src/interfaces/make-button.d.ts +6 -3
- package/dist/src/interfaces/make-custom-upload.d.ts +4 -1
- package/dist/src/interfaces/make-description.d.ts +5 -2
- package/dist/src/interfaces/make-field-group.d.ts +5 -2
- package/dist/src/interfaces/make-input.d.ts +6 -4
- package/dist/src/interfaces/make-select.d.ts +4 -1
- package/dist/src/interfaces/make-table.d.ts +4 -2
- package/dist/src/interfaces/make-textarea.d.ts +4 -1
- package/dist/src/interfaces/make-toggle.d.ts +4 -1
- package/dist/src/interfaces/make-upload.d.ts +4 -1
- package/dist/src/interfaces/make-watcher.d.ts +5 -1
- package/dist/src/interfaces/modal.d.ts +16 -0
- package/dist/src/modal.js +35 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -441,6 +441,34 @@ dynamically.
|
|
|
441
441
|
}
|
|
442
442
|
```
|
|
443
443
|
|
|
444
|
+
### `watcher`
|
|
445
|
+
|
|
446
|
+
Use `watcher` when you want to display a derived read-only value built from
|
|
447
|
+
other fields in the same modal.
|
|
448
|
+
|
|
449
|
+
`watcher` listens to the fields listed in `watchList`, joins their current
|
|
450
|
+
values, and renders the result using your custom `Input` component in disabled
|
|
451
|
+
mode.
|
|
452
|
+
|
|
453
|
+
Example:
|
|
454
|
+
|
|
455
|
+
```ts
|
|
456
|
+
{
|
|
457
|
+
elementType: 'watcher',
|
|
458
|
+
label: 'Full name preview',
|
|
459
|
+
watchList: ['firstName', 'middleName', 'lastName'],
|
|
460
|
+
style: {
|
|
461
|
+
width: '100%',
|
|
462
|
+
},
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Typical use cases:
|
|
467
|
+
|
|
468
|
+
- preview a full name from multiple inputs
|
|
469
|
+
- build a quick summary field for the user
|
|
470
|
+
- show a composed display value without storing it as a real form field
|
|
471
|
+
|
|
444
472
|
## Advanced conditions with async actions
|
|
445
473
|
|
|
446
474
|
`renderIf` and `enableIf` can also use async logic instead of static value maps.
|
|
@@ -462,6 +490,52 @@ renderIf: {
|
|
|
462
490
|
|
|
463
491
|
The same shape works for `enableIf`.
|
|
464
492
|
|
|
493
|
+
## Variants and combinations (`renderIf`, `enableIf`, `liveData`)
|
|
494
|
+
|
|
495
|
+
The library supports these variants:
|
|
496
|
+
|
|
497
|
+
| Feature | Variant | Shape |
|
|
498
|
+
| --- | --- | --- |
|
|
499
|
+
| `renderIf` | static criteria | `renderIf: { fieldName: ['value1', 'value2'] }` |
|
|
500
|
+
| `renderIf` | wildcard | `renderIf: { fieldName: ['*'] }` |
|
|
501
|
+
| `renderIf` | async action | `renderIf: { condition: ['fieldName'], action: async (...) => boolean }` |
|
|
502
|
+
| `enableIf` | static criteria | `enableIf: { fieldName: ['value1', 'value2'] }` |
|
|
503
|
+
| `enableIf` | wildcard | `enableIf: { fieldName: ['*'] }` |
|
|
504
|
+
| `enableIf` | async action | `enableIf: { condition: ['fieldName'], action: async (...) => boolean }` |
|
|
505
|
+
| `liveData` | single trigger field | `liveData: { condition: ['fieldName'], action: async (...) => IOption[] }` |
|
|
506
|
+
| `liveData` | multiple trigger fields | `liveData: { condition: ['fieldA', 'fieldB'], action: async (...) => IOption[] }` |
|
|
507
|
+
|
|
508
|
+
Supported combinations by field type:
|
|
509
|
+
|
|
510
|
+
- `input`, `textarea`, `toggle`, `upload`, `custom-upload`: `renderIf` + `enableIf`
|
|
511
|
+
- `select`: `renderIf` + `enableIf` + `liveData`
|
|
512
|
+
- `table`: `renderIf` + `liveData`
|
|
513
|
+
- `watcher`: no `renderIf`/`enableIf`/`liveData` contract in its interface
|
|
514
|
+
|
|
515
|
+
Behavior note about multiple observed fields:
|
|
516
|
+
|
|
517
|
+
- In static mode (`Record<field, values>`), conditions are evaluated per field-change event.
|
|
518
|
+
- In async mode (`condition: [...]`), `action` receives the changed field value as first argument and the whole form as second argument.
|
|
519
|
+
- For `liveData`, when options refresh, the target field value is reset to its default (`defaultValue`) or `[]` in multi-select mode.
|
|
520
|
+
|
|
521
|
+
Minimal combination example (`select` with all three):
|
|
522
|
+
|
|
523
|
+
```ts
|
|
524
|
+
{
|
|
525
|
+
elementType: 'select',
|
|
526
|
+
label: 'Options',
|
|
527
|
+
name: 'optionId',
|
|
528
|
+
options: [],
|
|
529
|
+
validation: { required: true, message: 'Required' },
|
|
530
|
+
renderIf: { typeId: ['*'] },
|
|
531
|
+
enableIf: { statusId: ['approved'] },
|
|
532
|
+
liveData: {
|
|
533
|
+
condition: ['typeId', 'statusId'],
|
|
534
|
+
action: async (changedValue, formData) => readOptions(changedValue, formData),
|
|
535
|
+
},
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
465
539
|
## Examples by use case
|
|
466
540
|
|
|
467
541
|
### 1. Basic modal
|
|
@@ -582,6 +656,40 @@ reservedData: {
|
|
|
582
656
|
|
|
583
657
|
That data will be merged into the object returned by `out`.
|
|
584
658
|
|
|
659
|
+
### 6. Compose a read-only value with `watcher`
|
|
660
|
+
|
|
661
|
+
Use `watcher` when you want the modal to display a value derived from multiple
|
|
662
|
+
fields while the user types.
|
|
663
|
+
|
|
664
|
+
```ts
|
|
665
|
+
fields: [
|
|
666
|
+
{
|
|
667
|
+
elementType: 'input',
|
|
668
|
+
label: 'First name',
|
|
669
|
+
name: 'firstName',
|
|
670
|
+
validation: { required: true, message: 'Required' },
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
elementType: 'input',
|
|
674
|
+
label: 'Last name',
|
|
675
|
+
name: 'lastName',
|
|
676
|
+
validation: { required: true, message: 'Required' },
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
elementType: 'watcher',
|
|
680
|
+
label: 'Preview',
|
|
681
|
+
watchList: ['firstName', 'lastName'],
|
|
682
|
+
style: { width: '100%' },
|
|
683
|
+
},
|
|
684
|
+
];
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
Important notes:
|
|
688
|
+
|
|
689
|
+
- `watcher` is display-only
|
|
690
|
+
- it does not submit its own value in the modal result
|
|
691
|
+
- it is useful for previews, concatenations, and human-readable summaries
|
|
692
|
+
|
|
585
693
|
## Configuration reference
|
|
586
694
|
|
|
587
695
|
### Modal-level config
|
|
@@ -598,6 +706,13 @@ Common properties of `IModalConfigProps`:
|
|
|
598
706
|
- `minHeightBody`: minimum body height
|
|
599
707
|
- `useSubmit`: if `false`, action button uses manual validation mode
|
|
600
708
|
- `useBlur`: enables backdrop blur style
|
|
709
|
+
- `layout`: section-level customization for:
|
|
710
|
+
- `container`
|
|
711
|
+
- `header` (`showDivider?: boolean`)
|
|
712
|
+
- `title`
|
|
713
|
+
- `body`
|
|
714
|
+
- `footer` (`showDivider?: boolean`)
|
|
715
|
+
Each section supports `className` and `style`.
|
|
601
716
|
- `actions.action`: main action button props
|
|
602
717
|
- `actions.cancel`: optional cancel button props
|
|
603
718
|
- `actions.containerStyle`: style for the action buttons container
|
|
@@ -611,11 +726,23 @@ Most form fields share:
|
|
|
611
726
|
- `placeholder`
|
|
612
727
|
- `defaultValue`
|
|
613
728
|
- `style`
|
|
729
|
+
- `customProperties`
|
|
614
730
|
- `disabled`
|
|
615
731
|
- `validation`
|
|
616
732
|
- `renderIf`
|
|
617
733
|
- `enableIf`
|
|
618
734
|
|
|
735
|
+
Most field interfaces now also accept native HTML attributes according to the
|
|
736
|
+
element type (`input`, `textarea`, `button`, `select`, etc.). These extra props
|
|
737
|
+
are forwarded with the rest of the field config.
|
|
738
|
+
|
|
739
|
+
`watcher` uses:
|
|
740
|
+
|
|
741
|
+
- `label`
|
|
742
|
+
- `style`
|
|
743
|
+
- `customProperties`
|
|
744
|
+
- `watchList`
|
|
745
|
+
|
|
619
746
|
Validation supports:
|
|
620
747
|
|
|
621
748
|
- `required`
|
|
@@ -48,14 +48,17 @@ const MakeSelect = ({ element, control, watch, setValue, unregister, }) => {
|
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
const shouldResolveLiveData = Boolean(elementLiveData?.condition?.includes(name));
|
|
52
|
+
if (shouldResolveLiveData) {
|
|
52
53
|
setLiveSearching(true);
|
|
53
54
|
checkLiveData(formData, { name, type })
|
|
54
55
|
.then((options) => {
|
|
55
56
|
if (options === undefined || options === null)
|
|
56
57
|
return;
|
|
57
58
|
setLiveData(options);
|
|
58
|
-
setValue(elementName,
|
|
59
|
+
setValue(elementName, inputProps.isMulti
|
|
60
|
+
? inputProps.defaultValue ?? []
|
|
61
|
+
: inputProps.defaultValue ?? '');
|
|
59
62
|
})
|
|
60
63
|
.finally(() => {
|
|
61
64
|
setLiveSearching(false);
|
|
@@ -38,7 +38,9 @@ const MakeSelect = ({ element, control, watch, setValue, unregister, }) => {
|
|
|
38
38
|
setLiveSearching,
|
|
39
39
|
unregister,
|
|
40
40
|
setValue,
|
|
41
|
-
liveDataResetValue: inputProps.
|
|
41
|
+
liveDataResetValue: inputProps.isMulti
|
|
42
|
+
? (inputProps.defaultValue ?? [])
|
|
43
|
+
: (inputProps.defaultValue ?? ''),
|
|
42
44
|
});
|
|
43
45
|
if (!render)
|
|
44
46
|
return null;
|
|
@@ -21,7 +21,8 @@ const useConditionalField = ({ watch, elementName, renderIf, enableIf, liveDataC
|
|
|
21
21
|
setRender(renderStatus);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
const shouldResolveLiveData = Boolean(liveDataConfig?.condition?.includes(meta.name));
|
|
25
|
+
if (shouldResolveLiveData && setLiveData) {
|
|
25
26
|
const requestId = ++liveRequestId.current;
|
|
26
27
|
setLiveSearching?.(true);
|
|
27
28
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FC, PropsWithChildren } from 'react';
|
|
1
|
+
import { CSSProperties, FC, PropsWithChildren, ReactNode } from 'react';
|
|
2
2
|
import { FieldError } from 'react-hook-form';
|
|
3
3
|
import { IOption } from './option';
|
|
4
4
|
import { IMakeInput } from './make-input';
|
|
@@ -14,7 +14,17 @@ export interface IComponentAditionalProps {
|
|
|
14
14
|
error?: FieldError;
|
|
15
15
|
liveSearching?: boolean;
|
|
16
16
|
}
|
|
17
|
+
export interface IModalSectionComponentProps {
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
className?: string;
|
|
20
|
+
style?: CSSProperties;
|
|
21
|
+
}
|
|
17
22
|
export interface IComponentState {
|
|
23
|
+
ModalContainer?: FC<IModalSectionComponentProps>;
|
|
24
|
+
ModalHeader?: FC<IModalSectionComponentProps>;
|
|
25
|
+
ModalTitle?: FC<IModalSectionComponentProps>;
|
|
26
|
+
ModalBody?: FC<IModalSectionComponentProps>;
|
|
27
|
+
ModalFooter?: FC<IModalSectionComponentProps>;
|
|
18
28
|
ModalButtonCancel: FC<Omit<IMakeButton, 'elementType'>>;
|
|
19
29
|
ModalButtonAction: FC<Omit<IMakeButton, 'elementType'>>;
|
|
20
30
|
Button: FC<Omit<IMakeButton, 'elementType'>>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { ChangeEvent, CSSProperties } from 'react';
|
|
2
|
-
|
|
1
|
+
import { ChangeEvent, CSSProperties, InputHTMLAttributes } from 'react';
|
|
2
|
+
type ICustomUploadHtmlProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'id' | 'style' | 'name' | 'disabled' | 'value' | 'onChange' | 'type'>;
|
|
3
|
+
export interface ICustomUpload extends ICustomUploadHtmlProps {
|
|
3
4
|
id?: string;
|
|
4
5
|
value?: string;
|
|
5
6
|
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
@@ -10,3 +11,4 @@ export interface ICustomUpload {
|
|
|
10
11
|
name: string;
|
|
11
12
|
disabled?: boolean;
|
|
12
13
|
}
|
|
14
|
+
export {};
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { ChangeEvent, CSSProperties } from 'react';
|
|
1
|
+
import { ChangeEvent, CSSProperties, InputHTMLAttributes } from 'react';
|
|
2
|
+
type IInputUploadHtmlProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'id' | 'style' | 'name' | 'disabled' | 'value' | 'onChange' | 'type'>;
|
|
2
3
|
export interface IFileResult {
|
|
3
4
|
name: string;
|
|
4
5
|
size: number;
|
|
5
6
|
data: string;
|
|
6
7
|
}
|
|
7
|
-
export interface IInputUpload {
|
|
8
|
+
export interface IInputUpload extends IInputUploadHtmlProps {
|
|
8
9
|
id?: string;
|
|
9
|
-
value?: string;
|
|
10
|
+
value?: string | number | readonly string[];
|
|
10
11
|
onChange: (event: ChangeEvent<HTMLInputElement> | IFileResult | FileList | null) => void;
|
|
11
12
|
accept?: string;
|
|
12
13
|
label?: string;
|
|
@@ -17,3 +18,4 @@ export interface IInputUpload {
|
|
|
17
18
|
disabled?: boolean;
|
|
18
19
|
read?: boolean;
|
|
19
20
|
}
|
|
21
|
+
export {};
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { CSSProperties, ReactNode } from 'react';
|
|
2
|
-
import { IFieldProps } from './field';
|
|
1
|
+
import { ButtonHTMLAttributes, CSSProperties, ReactNode } from 'react';
|
|
3
2
|
import { FieldValues, UseFormGetValues } from 'react-hook-form';
|
|
4
|
-
|
|
3
|
+
import { IFieldProps } from './field';
|
|
4
|
+
type IButtonHtmlProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'id' | 'disabled' | 'className' | 'style' | 'type' | 'onClick' | 'children' | 'color'>;
|
|
5
|
+
export interface IMakeButton extends IButtonHtmlProps {
|
|
5
6
|
id?: string;
|
|
6
7
|
elementType: 'button';
|
|
7
8
|
disabled?: boolean;
|
|
8
9
|
className?: string;
|
|
9
10
|
style?: CSSProperties;
|
|
11
|
+
customProperties?: Record<string, any>;
|
|
10
12
|
variant?: string;
|
|
11
13
|
text?: string;
|
|
12
14
|
type?: 'button' | 'submit' | 'reset';
|
|
@@ -18,3 +20,4 @@ export interface IMakeButtonProps extends IFieldProps {
|
|
|
18
20
|
element: Omit<IMakeButton, 'elementType'>;
|
|
19
21
|
getValues: UseFormGetValues<FieldValues>;
|
|
20
22
|
}
|
|
23
|
+
export {};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { InputHTMLAttributes } from 'react';
|
|
1
2
|
import { IField, IFieldProps } from './field';
|
|
2
|
-
|
|
3
|
+
type ICustomUploadHtmlProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'name' | 'id' | 'style' | 'onChange' | 'value' | 'defaultValue' | 'disabled' | 'type'>;
|
|
4
|
+
export interface IMakeCustomUpload extends IField, ICustomUploadHtmlProps {
|
|
3
5
|
elementType: 'custom-upload';
|
|
4
6
|
}
|
|
5
7
|
export interface IMakeCustomUploadProps extends IFieldProps {
|
|
6
8
|
element: Omit<IMakeCustomUpload, 'elementType'>;
|
|
7
9
|
}
|
|
10
|
+
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { CSSProperties, FC } from 'react';
|
|
1
|
+
import { CSSProperties, FC, HTMLAttributes } from 'react';
|
|
2
2
|
import { IFieldProps } from './field';
|
|
3
|
-
|
|
3
|
+
type IDescriptionHtmlProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'>;
|
|
4
|
+
export interface IMakeDescription extends IDescriptionHtmlProps {
|
|
4
5
|
elementType: 'text';
|
|
6
|
+
customProperties?: Record<string, any>;
|
|
5
7
|
text?: string;
|
|
6
8
|
containerStyle?: CSSProperties;
|
|
7
9
|
textStyle?: CSSProperties;
|
|
@@ -11,3 +13,4 @@ export interface IMakeDescription {
|
|
|
11
13
|
export interface IMakeDescriptionProps extends IFieldProps {
|
|
12
14
|
element: Omit<IMakeDescription, 'elementType'>;
|
|
13
15
|
}
|
|
16
|
+
export {};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { CSSProperties } from 'react';
|
|
1
|
+
import { CSSProperties, HTMLAttributes } from 'react';
|
|
2
2
|
import { IFieldProps } from './field';
|
|
3
3
|
import { IModalField } from './modal';
|
|
4
|
-
|
|
4
|
+
type IFieldGroupHtmlProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'>;
|
|
5
|
+
export interface IMakeFieldGroup extends IFieldGroupHtmlProps {
|
|
5
6
|
elementType: 'group';
|
|
7
|
+
customProperties?: Record<string, any>;
|
|
6
8
|
groups: Array<IModalField>;
|
|
7
9
|
style?: CSSProperties;
|
|
8
10
|
title?: string;
|
|
@@ -10,3 +12,4 @@ export interface IMakeFieldGroup {
|
|
|
10
12
|
export interface IMakeFieldGroupProps extends IFieldProps {
|
|
11
13
|
element: IMakeFieldGroup;
|
|
12
14
|
}
|
|
15
|
+
export {};
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { HTMLInputTypeAttribute } from 'react';
|
|
1
|
+
import { HTMLInputTypeAttribute, InputHTMLAttributes } from 'react';
|
|
2
2
|
import { IField, IFieldProps } from './field';
|
|
3
|
-
|
|
3
|
+
type IInputHtmlProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'name' | 'id' | 'style' | 'onChange' | 'value' | 'placeholder' | 'defaultValue' | 'disabled' | 'type' | 'min' | 'max'>;
|
|
4
|
+
export interface IMakeInput extends IField, IInputHtmlProps {
|
|
4
5
|
elementType: 'input';
|
|
5
6
|
placeholder?: string;
|
|
6
|
-
min?: string;
|
|
7
|
-
max?: string;
|
|
7
|
+
min?: string | number;
|
|
8
|
+
max?: string | number;
|
|
8
9
|
type?: HTMLInputTypeAttribute;
|
|
9
10
|
}
|
|
10
11
|
export interface IMakeInputProps extends IFieldProps {
|
|
11
12
|
element: Omit<IMakeInput, 'elementType'>;
|
|
12
13
|
}
|
|
14
|
+
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { SelectHTMLAttributes } from 'react';
|
|
1
2
|
import { IField, IFieldProps } from './field';
|
|
2
3
|
import { IModalLiveDataCondition } from './modal';
|
|
3
4
|
import { IOption } from './option';
|
|
4
|
-
|
|
5
|
+
type ISelectHtmlProps = Omit<SelectHTMLAttributes<HTMLSelectElement>, 'name' | 'id' | 'style' | 'onChange' | 'value' | 'defaultValue' | 'disabled'>;
|
|
6
|
+
export interface IMakeSelect extends IField, ISelectHtmlProps {
|
|
5
7
|
elementType: 'select';
|
|
6
8
|
options: Array<IOption>;
|
|
7
9
|
liveData?: IModalLiveDataCondition;
|
|
@@ -11,3 +13,4 @@ export interface IMakeSelect extends IField {
|
|
|
11
13
|
export interface IMakeSelectProps extends IFieldProps {
|
|
12
14
|
element: Omit<IMakeSelect, 'elementType'>;
|
|
13
15
|
}
|
|
16
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CSSProperties, JSX } from 'react';
|
|
1
|
+
import { CSSProperties, JSX, TableHTMLAttributes } from 'react';
|
|
2
2
|
import { IField, IFieldProps } from './field';
|
|
3
3
|
import { IModalLiveDataCondition } from './modal';
|
|
4
4
|
export interface ITableColumn extends Pick<IField, 'style'> {
|
|
@@ -9,7 +9,8 @@ export interface ITableColumn extends Pick<IField, 'style'> {
|
|
|
9
9
|
Icon?: (props: any) => JSX.Element;
|
|
10
10
|
action?: (row: any) => void;
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
type ITableHtmlProps = Omit<TableHTMLAttributes<HTMLTableElement>, 'style'>;
|
|
13
|
+
export interface IMakeTable extends Omit<IField, 'label' | 'defaultValue' | 'validation' | 'disabled' | 'enableIf' | 'id'>, ITableHtmlProps {
|
|
13
14
|
elementType: 'table';
|
|
14
15
|
selectValueName: string;
|
|
15
16
|
selectTitleName: string;
|
|
@@ -24,3 +25,4 @@ export interface IMakeTable extends Omit<IField, 'label' | 'defaultValue' | 'val
|
|
|
24
25
|
export interface IMakeTableProps extends Omit<IFieldProps, 'unregister'> {
|
|
25
26
|
element: Omit<IMakeTable, 'elementType'>;
|
|
26
27
|
}
|
|
28
|
+
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { TextareaHTMLAttributes } from 'react';
|
|
1
2
|
import { IField, IFieldProps } from './field';
|
|
2
|
-
|
|
3
|
+
type ITextareaHtmlProps = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'name' | 'id' | 'style' | 'onChange' | 'value' | 'placeholder' | 'defaultValue' | 'disabled' | 'cols' | 'rows'>;
|
|
4
|
+
export interface IMakeTextarea extends IField, ITextareaHtmlProps {
|
|
3
5
|
elementType: 'textarea';
|
|
4
6
|
cols?: number;
|
|
5
7
|
rows?: number;
|
|
@@ -7,3 +9,4 @@ export interface IMakeTextarea extends IField {
|
|
|
7
9
|
export interface IMakeTextareaProps extends IFieldProps {
|
|
8
10
|
element: Omit<IMakeTextarea, 'elementType'>;
|
|
9
11
|
}
|
|
12
|
+
export {};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { InputHTMLAttributes } from 'react';
|
|
1
2
|
import { IField, IFieldProps } from './field';
|
|
2
|
-
|
|
3
|
+
type IToggleHtmlProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'name' | 'id' | 'style' | 'onChange' | 'value' | 'checked' | 'defaultValue' | 'disabled' | 'type'>;
|
|
4
|
+
export interface IMakeToggle extends IField, IToggleHtmlProps {
|
|
3
5
|
elementType: 'toggle';
|
|
4
6
|
}
|
|
5
7
|
export interface IMakeToggleProps extends IFieldProps {
|
|
6
8
|
element: Omit<IMakeToggle, 'elementType'>;
|
|
7
9
|
}
|
|
10
|
+
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { InputHTMLAttributes } from 'react';
|
|
1
2
|
import { IField, IFieldProps } from './field';
|
|
2
|
-
|
|
3
|
+
type IUploadHtmlProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'name' | 'id' | 'style' | 'onChange' | 'value' | 'defaultValue' | 'disabled' | 'type' | 'accept'>;
|
|
4
|
+
export interface IMakeUpload extends Omit<IField, 'defaultValue'>, IUploadHtmlProps {
|
|
3
5
|
elementType: 'upload';
|
|
4
6
|
helpText?: string;
|
|
5
7
|
read: boolean;
|
|
@@ -10,3 +12,4 @@ export interface IMakeUpload extends Omit<IField, 'defaultValue'> {
|
|
|
10
12
|
export interface IMakeUploadProps extends IFieldProps {
|
|
11
13
|
element: Omit<IMakeUpload, 'elementType'>;
|
|
12
14
|
}
|
|
15
|
+
export {};
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { InputHTMLAttributes } from 'react';
|
|
1
2
|
import { IField, IFieldProps } from './field';
|
|
2
|
-
|
|
3
|
+
type IWatcherBaseProps = Pick<IField, 'style' | 'label' | 'customProperties'>;
|
|
4
|
+
type IWatcherHtmlProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'name' | 'id' | 'style' | 'onChange' | 'value' | 'defaultValue' | 'disabled' | 'type'>;
|
|
5
|
+
export interface IMakeWatcher extends IWatcherBaseProps, IWatcherHtmlProps {
|
|
3
6
|
elementType: 'watcher';
|
|
4
7
|
watchList: Array<string>;
|
|
5
8
|
}
|
|
6
9
|
export interface IMakeWatcherProps extends IFieldProps {
|
|
7
10
|
element: Omit<IMakeWatcher, 'elementType'>;
|
|
8
11
|
}
|
|
12
|
+
export {};
|
|
@@ -23,9 +23,25 @@ export type IModalLiveDataCondition = {
|
|
|
23
23
|
action: (data: string, ...args: any[]) => Promise<Array<IOption>>;
|
|
24
24
|
condition: Array<string>;
|
|
25
25
|
};
|
|
26
|
+
export interface IModalSectionStyleConfig {
|
|
27
|
+
className?: string;
|
|
28
|
+
style?: CSSProperties;
|
|
29
|
+
}
|
|
30
|
+
export interface IModalLayoutConfig {
|
|
31
|
+
container?: IModalSectionStyleConfig;
|
|
32
|
+
header?: IModalSectionStyleConfig & {
|
|
33
|
+
showDivider?: boolean;
|
|
34
|
+
};
|
|
35
|
+
title?: IModalSectionStyleConfig;
|
|
36
|
+
body?: IModalSectionStyleConfig;
|
|
37
|
+
footer?: IModalSectionStyleConfig & {
|
|
38
|
+
showDivider?: boolean;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
26
41
|
export interface IModalConfigProps {
|
|
27
42
|
reservedData?: Record<string, any>;
|
|
28
43
|
title: string;
|
|
44
|
+
layout?: IModalLayoutConfig;
|
|
29
45
|
fields: Array<IModalField>;
|
|
30
46
|
out: (data: any) => void;
|
|
31
47
|
onClose?: () => void;
|
package/dist/src/modal.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { createElement as _createElement } from "react";
|
|
4
4
|
import { useContext, useEffect, useMemo, useState } from 'react';
|
|
5
5
|
import { useForm } from 'react-hook-form';
|
|
@@ -35,12 +35,8 @@ const renderModalField = (field, index, { fieldProps, getValues }) => {
|
|
|
35
35
|
const fieldKey = getFieldKey(field, index);
|
|
36
36
|
return elementType === 'input' ? (_createElement(MakeInput, { ...fieldProps, key: fieldKey, element: element })) : elementType === 'select' ? (_createElement(MakeSelect, { ...fieldProps, key: fieldKey, element: element })) : elementType === 'textarea' ? (_createElement(MakeTextarea, { ...fieldProps, key: fieldKey, element: element })) : elementType === 'toggle' ? (_createElement(MakeToggle, { ...fieldProps, key: fieldKey, element: element })) : elementType === 'text' ? (_createElement(MakeDescription, { ...fieldProps, key: fieldKey, element: element })) : elementType === 'upload' ? (_createElement(MakeUpload, { ...fieldProps, key: fieldKey, element: element })) : elementType === 'custom-upload' ? (_createElement(MakeCustomUpload, { ...fieldProps, key: fieldKey, element: element })) : elementType === 'watcher' ? (_createElement(MakeWatcher, { ...fieldProps, key: fieldKey, element: element })) : elementType === 'button' ? (_createElement(MakeButton, { ...fieldProps, key: fieldKey, element: element, getValues: getValues })) : elementType === 'table' ? (_createElement(MakeTable, { ...fieldProps, key: fieldKey, element: element })) : null;
|
|
37
37
|
};
|
|
38
|
-
const ModalFields = ({ modalReady, fieldProps, getValues, }) => {
|
|
39
|
-
|
|
40
|
-
overflowY: modalReady.overFlowBody ? 'auto' : undefined,
|
|
41
|
-
height: modalReady.overFlowBody,
|
|
42
|
-
minHeight: modalReady.minHeightBody,
|
|
43
|
-
}, children: modalReady.fields.map((element, index) => {
|
|
38
|
+
const ModalFields = ({ modalReady, fieldProps, getValues, BodyComponent, bodyClassName, bodyStyle, }) => {
|
|
39
|
+
const bodyContent = (_jsx(_Fragment, { children: modalReady.fields.map((element, index) => {
|
|
44
40
|
if (element.elementType !== 'group') {
|
|
45
41
|
return renderModalField(element, index, { fieldProps, getValues });
|
|
46
42
|
}
|
|
@@ -50,15 +46,36 @@ const ModalFields = ({ modalReady, fieldProps, getValues, }) => {
|
|
|
50
46
|
const hideDiv = groupElements.every((component) => component === null);
|
|
51
47
|
return (_jsxs("div", { className: `flex flex-col w-full gap-2 ${hideDiv && 'hidden'}`, children: [element.title && (_jsx("h3", { className: "font-bold border-b-2 pb-2 mb-2", children: element.title })), _jsx("div", { className: "flex gap-4 w-full", style: element.style, children: groupElements })] }, getFieldKey(element, index)));
|
|
52
48
|
}) }));
|
|
49
|
+
const currentBodyClassName = `flex flex-col gap-4 py-4${bodyClassName ? ` ${bodyClassName}` : ''}`;
|
|
50
|
+
const currentBodyStyle = {
|
|
51
|
+
overflowY: modalReady.overFlowBody ? 'auto' : undefined,
|
|
52
|
+
height: modalReady.overFlowBody,
|
|
53
|
+
minHeight: modalReady.minHeightBody,
|
|
54
|
+
...bodyStyle,
|
|
55
|
+
};
|
|
56
|
+
if (BodyComponent) {
|
|
57
|
+
return (_jsx(BodyComponent, { className: currentBodyClassName, style: currentBodyStyle, children: bodyContent }));
|
|
58
|
+
}
|
|
59
|
+
return (_jsx("div", { className: currentBodyClassName, style: currentBodyStyle, children: bodyContent }));
|
|
53
60
|
};
|
|
54
|
-
const ModalActions = ({ modalReady, closeHandler, manualSubmit, }) => {
|
|
61
|
+
const ModalActions = ({ modalReady, closeHandler, manualSubmit, FooterComponent, footerClassName, footerStyle, showFooterDivider = true, }) => {
|
|
55
62
|
const { ModalButtonAction, ModalButtonCancel } = useContext(ComponentStateContext);
|
|
56
|
-
|
|
63
|
+
const footerContent = (_jsxs(_Fragment, { children: [modalReady.actions.cancel && (_jsx(ModalButtonCancel, { ...modalReady.actions.cancel, onClick: closeHandler })), getUseSubmit(modalReady) ? (_jsx(ModalButtonAction, { ...modalReady.actions.action, type: "submit" })) : (_jsx(ModalButtonAction, { ...modalReady.actions.action, onClick: manualSubmit, type: "button" }))] }));
|
|
64
|
+
const currentFooterClassName = `flex gap-4 items-center justify-center p-2${showFooterDivider ? ' border-t' : ''}${footerClassName ? ` ${footerClassName}` : ''}`;
|
|
65
|
+
const currentFooterStyle = {
|
|
66
|
+
...footerStyle,
|
|
67
|
+
...modalReady.actions.containerStyle,
|
|
68
|
+
};
|
|
69
|
+
if (FooterComponent) {
|
|
70
|
+
return (_jsx(FooterComponent, { className: currentFooterClassName, style: currentFooterStyle, children: footerContent }));
|
|
71
|
+
}
|
|
72
|
+
return (_jsx("div", { className: currentFooterClassName, style: currentFooterStyle, children: footerContent }));
|
|
57
73
|
};
|
|
58
74
|
export const Modal = ({ open, close, config }) => {
|
|
59
75
|
const [modalReady, setModalReady] = useState(undefined);
|
|
60
76
|
const [defaultLoaded, setDefaultLoaded] = useState(false);
|
|
61
77
|
const { control, handleSubmit, getValues, unregister, setValue, watch, trigger, reset, getFieldState, } = useForm();
|
|
78
|
+
const { ModalContainer, ModalHeader, ModalTitle, ModalBody, ModalFooter } = useContext(ComponentStateContext);
|
|
62
79
|
const formValueHandler = (element) => {
|
|
63
80
|
if ([
|
|
64
81
|
'group',
|
|
@@ -148,6 +165,14 @@ export const Modal = ({ open, close, config }) => {
|
|
|
148
165
|
}, [defaultLoaded, modalReady]);
|
|
149
166
|
if (!modalReady)
|
|
150
167
|
return null;
|
|
151
|
-
|
|
168
|
+
const { container: containerLayout, header: headerLayout, title: titleLayout, body: bodyLayout, footer: footerLayout, } = modalReady.layout ?? {};
|
|
169
|
+
const showHeaderDivider = headerLayout?.showDivider !== false;
|
|
170
|
+
const showFooterDivider = footerLayout?.showDivider !== false;
|
|
171
|
+
const headerClassName = `w-full text-center${showHeaderDivider ? ' border-b pb-4' : ''}${headerLayout?.className ? ` ${headerLayout.className}` : ''}`;
|
|
172
|
+
const titleClassName = `font-semibold${titleLayout?.className ? ` ${titleLayout.className}` : ''}`;
|
|
173
|
+
const titleContent = ModalTitle ? (_jsx(ModalTitle, { className: titleClassName, style: titleLayout?.style, children: modalReady.title })) : (_jsx("h2", { className: titleClassName, style: titleLayout?.style, children: modalReady.title }));
|
|
174
|
+
const headerContent = ModalHeader ? (_jsx(ModalHeader, { className: headerClassName, style: headerLayout?.style, children: titleContent })) : (_jsx("div", { className: headerClassName, style: headerLayout?.style, children: titleContent }));
|
|
175
|
+
const modalContent = (_jsxs("form", { className: "flex flex-col p-4 gap-4", autoComplete: "off", onSubmit: handleSubmit(actionHandler), children: [headerContent, _jsx(ModalFields, { modalReady: modalReady, fieldProps: fieldProps, getValues: getValues, BodyComponent: ModalBody, bodyClassName: bodyLayout?.className, bodyStyle: bodyLayout?.style }), _jsx(ModalActions, { modalReady: modalReady, closeHandler: closeHandler, manualSubmit: manualSubmit, FooterComponent: ModalFooter, footerClassName: footerLayout?.className, footerStyle: footerLayout?.style, showFooterDivider: showFooterDivider })] }));
|
|
176
|
+
return (_jsx(Portal, { closeTime: 200, portalOpen: open, portalTag: '#modal-portal', useBlur: modalReady.useBlur, children: ModalContainer ? (_jsx(ModalContainer, { className: `rounded bg-white relative w-auto h-auto min-h-[200px] min-w-[500px]${modalReady.useBlur ? ' shadow-md border border-gray-200' : ''}${containerLayout?.className ? ` ${containerLayout.className}` : ''}`, style: { ...modalReady.style, ...containerLayout?.style }, children: modalContent })) : (_jsx("div", { className: `rounded bg-white relative w-auto h-auto min-h-[200px] min-w-[500px]${modalReady.useBlur ? ' shadow-md border border-gray-200' : ''}${containerLayout?.className ? ` ${containerLayout.className}` : ''}`, style: { ...modalReady.style, ...containerLayout?.style }, children: modalContent })) }));
|
|
152
177
|
};
|
|
153
178
|
export default Modal;
|
package/package.json
CHANGED