gbs-add-block 0.0.48 → 0.0.49
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 +3 -3
- package/package.json +1 -1
- package/source/components/formrenderer/componentHandles/CheckBoxHandles.tsx +3 -0
- package/source/components/formrenderer/componentHandles/DatePickerHandles.tsx +1 -9
- package/source/components/formrenderer/componentHandles/InputHandles.tsx +56 -63
- package/source/components/formrenderer/componentHandles/MultiHandles.tsx +37 -11
- package/source/components/formrenderer/componentHandles/SelectHandles.tsx +37 -21
- package/source/components/formrenderer/helperFunctions.ts +15 -0
- package/source/components/formrenderer/index.tsx +7 -9
- package/source/components/formrenderer/types.ts +57 -28
- package/source/components/multiselect/index.tsx +22 -4
- package/source/components/multiselect/types.ts +2 -1
- package/source/components/select/index.tsx +16 -5
- package/source/components/select/style.ts +2 -3
- package/source/components/select/types.ts +1 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GBS Building Blocks 2.0 (v0.0.
|
|
1
|
+
# GBS Building Blocks 2.0 (v0.0.49)
|
|
2
2
|
|
|
3
3
|
Latest and upgraded version of GBS building blocks with headless UI and removed dependencies.
|
|
4
4
|
|
|
@@ -6,10 +6,10 @@ Latest and upgraded version of GBS building blocks with headless UI and removed
|
|
|
6
6
|
|
|
7
7
|
For detailed documentation on usage and props, Please visit: [Building Block Documentation v2.0](https://blackmax-designs.gitbook.io/building-block-v2.0)
|
|
8
8
|
|
|
9
|
-
## What's New 🎉 (Ver 0.0.
|
|
9
|
+
## What's New 🎉 (Ver 0.0.49)
|
|
10
10
|
|
|
11
|
+
- Update on form renderer
|
|
11
12
|
- Refactored Code for Better Readability
|
|
12
|
-
- Code Reusablity and hook update
|
|
13
13
|
|
|
14
14
|
## Authors
|
|
15
15
|
|
package/package.json
CHANGED
|
@@ -11,6 +11,8 @@ const CheckboxHandles = ({
|
|
|
11
11
|
}: CheckboxHandlesProps) => {
|
|
12
12
|
const [checked, setChecked] = useState<boolean>(!!item?.value);
|
|
13
13
|
|
|
14
|
+
// We Need this to set the value of the checkbox in the form
|
|
15
|
+
// if the value is not set in the form
|
|
14
16
|
useEffect(() => {
|
|
15
17
|
if (item?.name) {
|
|
16
18
|
handleSelect(checked, item.name);
|
|
@@ -40,6 +42,7 @@ const CheckboxHandles = ({
|
|
|
40
42
|
<input
|
|
41
43
|
type="checkbox"
|
|
42
44
|
checked={checked}
|
|
45
|
+
name={item?.name}
|
|
43
46
|
onChange={(e) => {
|
|
44
47
|
const newValue = e.target.checked;
|
|
45
48
|
setChecked(newValue);
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { DatePicker } from "../../datepicker";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
interface DatePickerHandlesProps {
|
|
6
|
-
item?: FormItem;
|
|
7
|
-
requirementError: string[];
|
|
8
|
-
setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
|
|
9
|
-
formRef?: React.RefObject<HTMLFormElement | null>;
|
|
10
|
-
onChangeEvent?: (event: any) => void;
|
|
11
|
-
}
|
|
3
|
+
import { DatePickerHandlesProps } from "../types";
|
|
12
4
|
|
|
13
5
|
export default function DatePickerHandles({
|
|
14
6
|
item,
|
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
import React, { useEffect, useState } from "react";
|
|
2
|
-
import {
|
|
1
|
+
import React, { useEffect, useState, useCallback } from "react";
|
|
2
|
+
import { validateInput } from "../helperFunctions";
|
|
3
3
|
import { Input } from "../../input";
|
|
4
|
-
import {
|
|
4
|
+
import { InputHandlesProps } from "../types";
|
|
5
5
|
import { evaluateExpression } from "@grampro/headless-helpers";
|
|
6
6
|
import { twMerge } from "tailwind-merge";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
item?: FormItem;
|
|
10
|
-
requirementError: string[];
|
|
11
|
-
setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
|
|
12
|
-
onChangeEvent?: (event: any) => void;
|
|
13
|
-
formRef: React.RefObject<HTMLFormElement | null>;
|
|
14
|
-
context: FormContext;
|
|
15
|
-
updateContext: (
|
|
16
|
-
componentName: string,
|
|
17
|
-
fieldName: string,
|
|
18
|
-
value: FieldValue
|
|
19
|
-
) => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const InputHandles = ({
|
|
8
|
+
const InputHandles: React.FC<InputHandlesProps> = ({
|
|
23
9
|
item,
|
|
24
10
|
requirementError,
|
|
25
11
|
setRequirementError,
|
|
26
12
|
onChangeEvent,
|
|
27
13
|
context,
|
|
28
14
|
updateContext,
|
|
29
|
-
}
|
|
15
|
+
}) => {
|
|
30
16
|
const [inputError, setInputError] = useState<string | null>(null);
|
|
31
17
|
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
|
32
18
|
const [isRequired, setIsRequired] = useState<boolean>(false);
|
|
33
19
|
|
|
34
|
-
//
|
|
20
|
+
// Evaluate expression with memoization
|
|
21
|
+
const evaluateCondition = useCallback(
|
|
22
|
+
(expression: string | boolean | undefined): boolean => {
|
|
23
|
+
if (typeof expression === "string") {
|
|
24
|
+
return evaluateExpression(expression, context);
|
|
25
|
+
}
|
|
26
|
+
return !!expression;
|
|
27
|
+
},
|
|
28
|
+
[context]
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Initialize context and handle dynamic states
|
|
35
32
|
useEffect(() => {
|
|
36
33
|
if (item?.name && item?.value !== undefined) {
|
|
37
34
|
updateContext("input", item.name, item.value);
|
|
38
35
|
}
|
|
39
|
-
}, [item?.name, item?.value, updateContext]);
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (typeof item?.disabled === "string") {
|
|
44
|
-
setIsDisabled(evaluateExpression(item.disabled, context));
|
|
45
|
-
} else if (typeof item?.disabled === "boolean") {
|
|
46
|
-
setIsDisabled(item.disabled);
|
|
47
|
-
}
|
|
37
|
+
const disabled = evaluateCondition(item?.disabled);
|
|
38
|
+
setIsDisabled(disabled);
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
}, [item, context]);
|
|
40
|
+
// Only set required if not disabled
|
|
41
|
+
const required = disabled ? false : evaluateCondition(item?.required);
|
|
42
|
+
setIsRequired(required);
|
|
43
|
+
}, [item, context, evaluateCondition, updateContext]);
|
|
55
44
|
|
|
56
|
-
const handleChange = (
|
|
57
|
-
|
|
45
|
+
const handleChange = useCallback(
|
|
46
|
+
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
47
|
+
const { value } = event.target;
|
|
58
48
|
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
if (item?.name) {
|
|
50
|
+
updateContext("input", item.name, value);
|
|
51
|
+
setRequirementError?.((prev) =>
|
|
52
|
+
prev.filter((error) => error !== item.name)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
);
|
|
65
|
-
}
|
|
56
|
+
const validationError = validateInput(value, item?.type);
|
|
57
|
+
setInputError(validationError);
|
|
66
58
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
59
|
+
onChangeEvent?.(event);
|
|
60
|
+
},
|
|
61
|
+
[
|
|
62
|
+
item?.name,
|
|
63
|
+
item?.type,
|
|
64
|
+
updateContext,
|
|
65
|
+
setRequirementError,
|
|
66
|
+
onChangeEvent,
|
|
67
|
+
validateInput,
|
|
68
|
+
]
|
|
69
|
+
);
|
|
75
70
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
// Merge input classes
|
|
72
|
+
const inputClassName = twMerge(
|
|
73
|
+
"border rounded-md px-4 py-[6px] w-full text-black",
|
|
74
|
+
inputError || (item?.name && requirementError.includes(item?.name))
|
|
75
|
+
? "border-red-500"
|
|
76
|
+
: "border-gray-300",
|
|
77
|
+
item?.stepProperty?.customClass?.inputClass
|
|
78
|
+
);
|
|
79
79
|
|
|
80
80
|
return (
|
|
81
81
|
<div className="w-full">
|
|
@@ -88,22 +88,15 @@ const InputHandles = ({
|
|
|
88
88
|
<Input
|
|
89
89
|
type={item?.type}
|
|
90
90
|
name={item?.name}
|
|
91
|
-
placeholder={item?.placeholder
|
|
91
|
+
placeholder={item?.placeholder ?? ""}
|
|
92
92
|
onChange={handleChange}
|
|
93
|
-
className={
|
|
94
|
-
`border rounded-md px-4 py-[6px] w-full text-black ${
|
|
95
|
-
inputError || (item?.name && requirementError.includes(item?.name))
|
|
96
|
-
? "border-red-500"
|
|
97
|
-
: "border-gray-300"
|
|
98
|
-
}`,
|
|
99
|
-
item?.stepProperty?.customClass?.inputClass
|
|
100
|
-
)}
|
|
93
|
+
className={inputClassName}
|
|
101
94
|
defaultValue={item?.value ?? ""}
|
|
102
95
|
disabled={isDisabled}
|
|
103
96
|
required={isRequired}
|
|
104
97
|
/>
|
|
105
98
|
{item?.name && requirementError.includes(item.name) && (
|
|
106
|
-
<p className="text-red-500 text-xs">{`${item
|
|
99
|
+
<p className="text-red-500 text-xs">{`${item.name} is required`}</p>
|
|
107
100
|
)}
|
|
108
101
|
{inputError && <p className="text-red-500 text-xs">{inputError}</p>}
|
|
109
102
|
</div>
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {
|
|
1
|
+
import React, { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import { MultiSelectHandlesProps } from "../types";
|
|
3
3
|
import { MultiSelect } from "../../multiselect";
|
|
4
|
-
|
|
5
|
-
interface MultiSelectHandlesProps {
|
|
6
|
-
item?: FormItem;
|
|
7
|
-
requirementError: string[];
|
|
8
|
-
setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
|
|
9
|
-
formRef?: React.RefObject<HTMLFormElement | null>;
|
|
10
|
-
onChangeEvent?: (event: any) => void;
|
|
11
|
-
}
|
|
4
|
+
import { evaluateExpression } from "@grampro/headless-helpers";
|
|
12
5
|
|
|
13
6
|
export default function MultiHandles({
|
|
14
7
|
item,
|
|
@@ -16,7 +9,39 @@ export default function MultiHandles({
|
|
|
16
9
|
setRequirementError,
|
|
17
10
|
formRef,
|
|
18
11
|
onChangeEvent,
|
|
12
|
+
context,
|
|
13
|
+
updateContext,
|
|
19
14
|
}: MultiSelectHandlesProps) {
|
|
15
|
+
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
|
16
|
+
const [isRequired, setIsRequired] = useState<boolean>(false);
|
|
17
|
+
|
|
18
|
+
// Evaluate expression with memoization
|
|
19
|
+
const evaluateCondition = useCallback(
|
|
20
|
+
(expression: string | boolean | undefined): boolean => {
|
|
21
|
+
if (typeof expression === "string") {
|
|
22
|
+
return evaluateExpression(expression, context);
|
|
23
|
+
}
|
|
24
|
+
return !!expression;
|
|
25
|
+
},
|
|
26
|
+
[context]
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// Initialize context and handle dynamic states
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (item?.name && item?.value !== undefined) {
|
|
32
|
+
updateContext("select", item.name, item.value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const disabled = evaluateCondition(item?.disabled);
|
|
36
|
+
console.log("disabled", disabled);
|
|
37
|
+
|
|
38
|
+
setIsDisabled(disabled);
|
|
39
|
+
|
|
40
|
+
// Only set required if not disabled
|
|
41
|
+
const required = disabled ? false : evaluateCondition(item?.required);
|
|
42
|
+
setIsRequired(required);
|
|
43
|
+
}, [item, context, evaluateCondition, updateContext]);
|
|
44
|
+
|
|
20
45
|
const handleSelect = (value: string[], key: string) => {
|
|
21
46
|
if (formRef && formRef.current) {
|
|
22
47
|
const hiddenInput = formRef.current.querySelector(
|
|
@@ -42,7 +67,7 @@ export default function MultiHandles({
|
|
|
42
67
|
{item?.label && (
|
|
43
68
|
<label htmlFor={item.name} className="font-medium text-sm">
|
|
44
69
|
{item.label}
|
|
45
|
-
{
|
|
70
|
+
{isRequired && <span className="text-red-500">*</span>}
|
|
46
71
|
</label>
|
|
47
72
|
)}
|
|
48
73
|
<MultiSelect
|
|
@@ -62,6 +87,7 @@ export default function MultiHandles({
|
|
|
62
87
|
? `${item.name} is required`
|
|
63
88
|
: undefined
|
|
64
89
|
}
|
|
90
|
+
disabled={isDisabled}
|
|
65
91
|
/>
|
|
66
92
|
</div>
|
|
67
93
|
);
|
|
@@ -1,24 +1,7 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useCallback, useEffect, useState } from "react";
|
|
2
2
|
import { Select } from "../../select";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
value: string;
|
|
6
|
-
label: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface SelectHandlesProps {
|
|
10
|
-
item?: {
|
|
11
|
-
name?: string;
|
|
12
|
-
label?: string;
|
|
13
|
-
options?: Option[];
|
|
14
|
-
value?: string;
|
|
15
|
-
required?: boolean;
|
|
16
|
-
};
|
|
17
|
-
requirementError: string[];
|
|
18
|
-
setRequirementError: React.Dispatch<React.SetStateAction<string[]>>;
|
|
19
|
-
formRef: React.RefObject<HTMLFormElement | null>;
|
|
20
|
-
onChangeEvent?: (event: any) => void;
|
|
21
|
-
}
|
|
3
|
+
import { Option, SelectHandlesProps } from "../types";
|
|
4
|
+
import { evaluateExpression } from "@grampro/headless-helpers";
|
|
22
5
|
|
|
23
6
|
export default function SelectHandles({
|
|
24
7
|
item,
|
|
@@ -26,9 +9,41 @@ export default function SelectHandles({
|
|
|
26
9
|
setRequirementError,
|
|
27
10
|
formRef,
|
|
28
11
|
onChangeEvent,
|
|
12
|
+
context,
|
|
13
|
+
updateContext,
|
|
29
14
|
}: SelectHandlesProps) {
|
|
30
15
|
const [options] = useState<Option[]>(item?.options || []);
|
|
16
|
+
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
|
17
|
+
const [isRequired, setIsRequired] = useState<boolean>(false);
|
|
18
|
+
|
|
19
|
+
// Evaluate expression with memoization
|
|
20
|
+
const evaluateCondition = useCallback(
|
|
21
|
+
(expression: string | boolean | undefined): boolean => {
|
|
22
|
+
if (typeof expression === "string") {
|
|
23
|
+
return evaluateExpression(expression, context);
|
|
24
|
+
}
|
|
25
|
+
return !!expression;
|
|
26
|
+
},
|
|
27
|
+
[context]
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// Initialize context and handle dynamic states
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (item?.name && item?.value !== undefined) {
|
|
33
|
+
updateContext("select", item.name, item.value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const disabled = evaluateCondition(item?.disabled);
|
|
37
|
+
console.log("disabled", disabled);
|
|
38
|
+
|
|
39
|
+
setIsDisabled(disabled);
|
|
40
|
+
|
|
41
|
+
// Only set required if not disabled
|
|
42
|
+
const required = disabled ? false : evaluateCondition(item?.required);
|
|
43
|
+
setIsRequired(required);
|
|
44
|
+
}, [item, context, evaluateCondition, updateContext]);
|
|
31
45
|
|
|
46
|
+
// Handle select change event
|
|
32
47
|
const handleSelect = (value: string | undefined, key: string) => {
|
|
33
48
|
if (!formRef?.current) return;
|
|
34
49
|
|
|
@@ -55,7 +70,7 @@ export default function SelectHandles({
|
|
|
55
70
|
{item?.label && (
|
|
56
71
|
<label htmlFor={item.name} className="font-medium text-sm">
|
|
57
72
|
{item.label}
|
|
58
|
-
{
|
|
73
|
+
{isRequired && <span className="text-red-500">*</span>}
|
|
59
74
|
</label>
|
|
60
75
|
)}
|
|
61
76
|
<Select
|
|
@@ -74,6 +89,7 @@ export default function SelectHandles({
|
|
|
74
89
|
? `${item.name} is required`
|
|
75
90
|
: undefined
|
|
76
91
|
}
|
|
92
|
+
disabled={isDisabled}
|
|
77
93
|
/>
|
|
78
94
|
</div>
|
|
79
95
|
);
|
|
@@ -20,3 +20,18 @@ export const formDataToObject = (formData: FormData) => {
|
|
|
20
20
|
});
|
|
21
21
|
return obj;
|
|
22
22
|
};
|
|
23
|
+
|
|
24
|
+
export const validateInput = (value: string, type?: string): string | null => {
|
|
25
|
+
if (!value) return null;
|
|
26
|
+
|
|
27
|
+
switch (type) {
|
|
28
|
+
case "email":
|
|
29
|
+
return validateEmail(value) ? null : "Invalid email format";
|
|
30
|
+
case "tel":
|
|
31
|
+
return validatePhoneNumber(value)
|
|
32
|
+
? null
|
|
33
|
+
: "Invalid phone number. Must be 10 digits.";
|
|
34
|
+
default:
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React, { useRef, useState } from "react";
|
|
8
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
9
9
|
import { useFormContext } from "@grampro/headless-helpers";
|
|
10
10
|
import { FormElement, FormItem, FormRendererProps } from "./types";
|
|
11
11
|
import InputHandles from "./componentHandles/InputHandles";
|
|
@@ -73,17 +73,13 @@ const FormRenderer = ({
|
|
|
73
73
|
return (
|
|
74
74
|
<SelectHandles
|
|
75
75
|
key={index}
|
|
76
|
-
item={
|
|
77
|
-
name: item.name,
|
|
78
|
-
label: item.label,
|
|
79
|
-
options: item.options,
|
|
80
|
-
value: item.value,
|
|
81
|
-
required: Boolean(item.required),
|
|
82
|
-
}}
|
|
76
|
+
item={item}
|
|
83
77
|
requirementError={requirementError}
|
|
84
78
|
setRequirementError={setRequirementError}
|
|
85
79
|
formRef={formRef}
|
|
86
80
|
onChangeEvent={item.onChangeEvent}
|
|
81
|
+
context={context}
|
|
82
|
+
updateContext={updateContext}
|
|
87
83
|
/>
|
|
88
84
|
);
|
|
89
85
|
|
|
@@ -96,6 +92,8 @@ const FormRenderer = ({
|
|
|
96
92
|
setRequirementError={setRequirementError}
|
|
97
93
|
formRef={formRef}
|
|
98
94
|
onChangeEvent={item.onChangeEvent}
|
|
95
|
+
context={context}
|
|
96
|
+
updateContext={updateContext}
|
|
99
97
|
/>
|
|
100
98
|
);
|
|
101
99
|
|
|
@@ -131,7 +129,7 @@ const FormRenderer = ({
|
|
|
131
129
|
<div key={index} className="w-full mt-1">
|
|
132
130
|
<button
|
|
133
131
|
type={item.button_type}
|
|
134
|
-
className="w-full bg-black text-white py-2 rounded-xl hover:bg-gray-800
|
|
132
|
+
className="w-full bg-black text-white py-2 rounded-xl hover:bg-gray-800"
|
|
135
133
|
>
|
|
136
134
|
{item.value}
|
|
137
135
|
</button>
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export type Option = {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
};
|
|
1
5
|
export interface FormElement extends HTMLFormElement {
|
|
2
6
|
[key: string]: any;
|
|
3
7
|
}
|
|
@@ -26,39 +30,15 @@ export type FormItem = {
|
|
|
26
30
|
};
|
|
27
31
|
};
|
|
28
32
|
|
|
29
|
-
interface DependencyConfig {
|
|
30
|
-
source: string;
|
|
31
|
-
parent?: string;
|
|
32
|
-
dataStructure: any;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
33
|
export type FormRendererProps = {
|
|
36
34
|
onSubmit?: (formData: FormData) => void;
|
|
37
35
|
sourceData?: FormItem[] | undefined;
|
|
38
36
|
formFormationClass?: string;
|
|
39
37
|
formParentClass?: string;
|
|
40
|
-
dependencyConfig?: Record<string, DependencyConfig>;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export type SelectHandlesProps = {
|
|
44
|
-
item?: FormItem;
|
|
45
|
-
requirementError: string[];
|
|
46
|
-
setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
|
|
47
|
-
formRef?: React.RefObject<HTMLFormElement | null>;
|
|
48
|
-
dependencyMap?: Record<
|
|
49
|
-
string,
|
|
50
|
-
{
|
|
51
|
-
source: string;
|
|
52
|
-
parent?: string;
|
|
53
|
-
dataStructure: any;
|
|
54
|
-
}
|
|
55
|
-
>;
|
|
56
38
|
};
|
|
57
39
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
label: string;
|
|
61
|
-
};
|
|
40
|
+
// Handler Props Starts here
|
|
41
|
+
export type FieldValue = string | boolean | number | null;
|
|
62
42
|
|
|
63
43
|
export type CheckboxHandlesProps = {
|
|
64
44
|
item?: FormItem;
|
|
@@ -77,10 +57,59 @@ export type CheckboxHandlesProps = {
|
|
|
77
57
|
) => void;
|
|
78
58
|
};
|
|
79
59
|
|
|
80
|
-
export type FieldValue = string | boolean | number | null;
|
|
81
|
-
|
|
82
60
|
export interface FormContext {
|
|
83
61
|
[componentName: string]: {
|
|
84
62
|
[fieldName: string]: FieldValue;
|
|
85
63
|
};
|
|
86
64
|
}
|
|
65
|
+
|
|
66
|
+
export type InputHandlesProps = {
|
|
67
|
+
item?: FormItem;
|
|
68
|
+
requirementError: string[];
|
|
69
|
+
setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
|
|
70
|
+
onChangeEvent?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
71
|
+
formRef: React.RefObject<HTMLFormElement | null>;
|
|
72
|
+
context: FormContext;
|
|
73
|
+
updateContext: (
|
|
74
|
+
componentName: string,
|
|
75
|
+
fieldName: string,
|
|
76
|
+
value: FieldValue
|
|
77
|
+
) => void;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type DatePickerHandlesProps = {
|
|
81
|
+
item?: FormItem;
|
|
82
|
+
requirementError: string[];
|
|
83
|
+
setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
|
|
84
|
+
formRef?: React.RefObject<HTMLFormElement | null>;
|
|
85
|
+
onChangeEvent?: (event: any) => void;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export type SelectHandlesProps = {
|
|
89
|
+
item?: FormItem;
|
|
90
|
+
requirementError: string[];
|
|
91
|
+
setRequirementError: React.Dispatch<React.SetStateAction<string[]>>;
|
|
92
|
+
formRef: React.RefObject<HTMLFormElement | null>;
|
|
93
|
+
onChangeEvent?: (event: any) => void;
|
|
94
|
+
context: FormContext;
|
|
95
|
+
updateContext: (
|
|
96
|
+
componentName: string,
|
|
97
|
+
fieldName: string,
|
|
98
|
+
value: FieldValue
|
|
99
|
+
) => void;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type MultiSelectHandlesProps = {
|
|
103
|
+
item?: FormItem;
|
|
104
|
+
requirementError: string[];
|
|
105
|
+
setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
|
|
106
|
+
formRef?: React.RefObject<HTMLFormElement | null>;
|
|
107
|
+
onChangeEvent?: (event: any) => void;
|
|
108
|
+
context: FormContext;
|
|
109
|
+
updateContext: (
|
|
110
|
+
componentName: string,
|
|
111
|
+
fieldName: string,
|
|
112
|
+
value: FieldValue
|
|
113
|
+
) => void;
|
|
114
|
+
};
|
|
115
|
+
// Handler Props Ends here
|
|
@@ -17,6 +17,7 @@ import type { MultiSelectHandle, MultiSelectProps } from "./types";
|
|
|
17
17
|
import {
|
|
18
18
|
useMultiSelectState,
|
|
19
19
|
useClickOutside,
|
|
20
|
+
applyScrollbarStyles,
|
|
20
21
|
} from "@grampro/headless-helpers";
|
|
21
22
|
import Icon from "../icon/Icon";
|
|
22
23
|
import { check, search, upDown, x } from "../icon/iconPaths";
|
|
@@ -34,8 +35,11 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
|
34
35
|
selectedItems = [],
|
|
35
36
|
name,
|
|
36
37
|
error,
|
|
38
|
+
disabled = false,
|
|
37
39
|
} = props;
|
|
38
40
|
|
|
41
|
+
applyScrollbarStyles();
|
|
42
|
+
|
|
39
43
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
40
44
|
const selectRef = useRef<HTMLDivElement>(null);
|
|
41
45
|
|
|
@@ -78,6 +82,12 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
|
78
82
|
return display;
|
|
79
83
|
}, [selected, workingDataSource, placeholder, truncate]);
|
|
80
84
|
|
|
85
|
+
const handleClear = () => {
|
|
86
|
+
if (disabled) return;
|
|
87
|
+
clearSelected();
|
|
88
|
+
if (onSelect) onSelect([]);
|
|
89
|
+
};
|
|
90
|
+
|
|
81
91
|
useImperativeHandle(
|
|
82
92
|
ref,
|
|
83
93
|
() => ({
|
|
@@ -109,8 +119,11 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
|
109
119
|
>
|
|
110
120
|
<button
|
|
111
121
|
onClick={togglePopover}
|
|
112
|
-
className=
|
|
122
|
+
className={`flex-grow text-sm text-left font-medium ${
|
|
123
|
+
disabled ? "opacity-50 cursor-not-allowed" : ""
|
|
124
|
+
}`}
|
|
113
125
|
type="button"
|
|
126
|
+
disabled={disabled}
|
|
114
127
|
>
|
|
115
128
|
{getSelectedDisplay()}
|
|
116
129
|
</button>
|
|
@@ -118,14 +131,19 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
|
118
131
|
{selected.length > 0 && (
|
|
119
132
|
<button
|
|
120
133
|
className="flex items-center px-2"
|
|
121
|
-
onClick={
|
|
134
|
+
onClick={handleClear}
|
|
122
135
|
type="button"
|
|
123
136
|
>
|
|
124
137
|
<Icon elements={x} svgClass={iconClass["grey-common"]} />
|
|
125
138
|
</button>
|
|
126
139
|
)}
|
|
127
140
|
<button onClick={togglePopover} type="button">
|
|
128
|
-
<Icon
|
|
141
|
+
<Icon
|
|
142
|
+
elements={upDown}
|
|
143
|
+
svgClass={`${iconClass["grey-common"]} ${
|
|
144
|
+
disabled ? "opacity-50" : ""
|
|
145
|
+
}`}
|
|
146
|
+
/>
|
|
129
147
|
</button>
|
|
130
148
|
</div>
|
|
131
149
|
</div>
|
|
@@ -138,7 +156,7 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
|
138
156
|
readOnly
|
|
139
157
|
/>
|
|
140
158
|
|
|
141
|
-
{showPopover && (
|
|
159
|
+
{showPopover && !disabled && (
|
|
142
160
|
<div className={popUp["pop-up-style"]}>
|
|
143
161
|
{showSearch && (
|
|
144
162
|
<div className="flex p-2 gap-1 items-center sticky top-0 bg-white border-b dark:bg-black">
|
|
@@ -8,11 +8,12 @@ export type MultiSelectProps = {
|
|
|
8
8
|
items?: ItemsProps[] | string;
|
|
9
9
|
lazy?: boolean;
|
|
10
10
|
showSearch?: boolean;
|
|
11
|
-
onSelect?: (selectedItem: string[]) => void;
|
|
11
|
+
onSelect?: (selectedItem: string[]) => void | [];
|
|
12
12
|
truncate?: boolean;
|
|
13
13
|
selectedItems?: string[];
|
|
14
14
|
name?: string;
|
|
15
15
|
error?: string;
|
|
16
|
+
disabled?: boolean;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
export type MultiSelectHandle = {
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
useSelectState,
|
|
23
23
|
useSelectData,
|
|
24
24
|
useClickOutside,
|
|
25
|
+
applyScrollbarStyles,
|
|
25
26
|
} from "@grampro/headless-helpers";
|
|
26
27
|
|
|
27
28
|
const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
@@ -36,8 +37,11 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
|
36
37
|
selectedItem: initialSelectedItem,
|
|
37
38
|
error = undefined,
|
|
38
39
|
onFiltering,
|
|
40
|
+
disabled = false,
|
|
39
41
|
} = props;
|
|
40
42
|
|
|
43
|
+
applyScrollbarStyles();
|
|
44
|
+
|
|
41
45
|
const {
|
|
42
46
|
showPopover,
|
|
43
47
|
setShowPopover,
|
|
@@ -90,6 +94,7 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
|
90
94
|
);
|
|
91
95
|
|
|
92
96
|
const handleClear = () => {
|
|
97
|
+
if (disabled) return;
|
|
93
98
|
clearSelected();
|
|
94
99
|
if (onSelect) onSelect(undefined);
|
|
95
100
|
};
|
|
@@ -110,14 +115,20 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
|
110
115
|
<button
|
|
111
116
|
className={`${error ? primary["error-border"] : "border"} ${
|
|
112
117
|
selectStyle["select-button"]
|
|
113
|
-
}`}
|
|
118
|
+
} ${disabled ? "opacity-50 cursor-not-allowed" : ""}`}
|
|
114
119
|
onClick={togglePopover}
|
|
115
120
|
type="button"
|
|
121
|
+
disabled={disabled}
|
|
116
122
|
>
|
|
117
123
|
{selectedDisplay || placeholder}
|
|
118
|
-
<Icon
|
|
124
|
+
<Icon
|
|
125
|
+
elements={upDown}
|
|
126
|
+
svgClass={`${iconClass["grey-common"]} ${
|
|
127
|
+
disabled ? "opacity-50" : ""
|
|
128
|
+
}`}
|
|
129
|
+
/>
|
|
119
130
|
</button>
|
|
120
|
-
{selectedDisplay && (
|
|
131
|
+
{selectedDisplay && !disabled && (
|
|
121
132
|
<button
|
|
122
133
|
className={selectStyle["selectedDisplay-Button"]}
|
|
123
134
|
onClick={handleClear}
|
|
@@ -130,7 +141,7 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
|
130
141
|
|
|
131
142
|
<input type="hidden" name={name} value={selectedItem || ""} readOnly />
|
|
132
143
|
|
|
133
|
-
{showPopover && (
|
|
144
|
+
{showPopover && !disabled && (
|
|
134
145
|
<div className={popUp["pop-up-style"]}>
|
|
135
146
|
{showSearch && (
|
|
136
147
|
<div className={selectStyle["input-parent"]}>
|
|
@@ -141,7 +152,7 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
|
141
152
|
name="search"
|
|
142
153
|
id="search"
|
|
143
154
|
placeholder="Search a value"
|
|
144
|
-
className="w-full outline-none
|
|
155
|
+
className="w-full outline-none"
|
|
145
156
|
ref={inputRef}
|
|
146
157
|
value={searchTerm}
|
|
147
158
|
onChange={(e) => {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
export const selectStyle = {
|
|
2
2
|
"select-button":
|
|
3
|
-
"flex items-center px-4 py-2 w-full justify-between rounded-lg font-medium text-sm
|
|
3
|
+
"flex items-center px-4 py-2 w-full justify-between rounded-lg font-medium text-sm",
|
|
4
4
|
"filter-button":
|
|
5
5
|
"flex items-center w-full px-2 py-1 text-left hover:bg-blue-100 gap-2 rounded-lg mt-1 text-sm dark:hover:bg-blue-600",
|
|
6
|
-
"input-parent":
|
|
7
|
-
"flex p-2 gap-1 items-center sticky top-0 bg-white border-b dark:bg-black",
|
|
6
|
+
"input-parent": "flex p-2 gap-1 items-center sticky top-0 bg-white border-b",
|
|
8
7
|
"selectedDisplay-Button":
|
|
9
8
|
"absolute right-8 top-0 h-full flex items-center px-2 z-20",
|
|
10
9
|
};
|