gbs-add-block 0.0.49 → 0.0.51
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 +2 -2
- package/package.json +1 -1
- package/source/components/datepicker/index.tsx +7 -2
- package/source/components/datepicker/types.ts +1 -0
- package/source/components/formrenderer/componentHandles/DatePickerHandles.tsx +34 -2
- package/source/components/formrenderer/componentHandles/MultiHandles.tsx +1 -2
- package/source/components/formrenderer/componentHandles/SelectHandles.tsx +0 -1
- package/source/components/formrenderer/helperFunctions.ts +48 -13
- package/source/components/formrenderer/index.tsx +59 -117
- package/source/components/formrenderer/types.ts +13 -1
- package/source/components/multiselect/index.tsx +1 -0
- package/source/components/select/index.tsx +7 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GBS Building Blocks 2.0 (v0.0.
|
|
1
|
+
# GBS Building Blocks 2.0 (v0.0.51)
|
|
2
2
|
|
|
3
3
|
Latest and upgraded version of GBS building blocks with headless UI and removed dependencies.
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ 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.51)
|
|
10
10
|
|
|
11
11
|
- Update on form renderer
|
|
12
12
|
- Refactored Code for Better Readability
|
package/package.json
CHANGED
|
@@ -29,6 +29,7 @@ export const DatePicker = ({
|
|
|
29
29
|
name,
|
|
30
30
|
error,
|
|
31
31
|
placeholder = "Select a date",
|
|
32
|
+
disabled = false,
|
|
32
33
|
}: DatePickerProps) => {
|
|
33
34
|
applyScrollbarStyles();
|
|
34
35
|
|
|
@@ -61,6 +62,7 @@ export const DatePicker = ({
|
|
|
61
62
|
setCurrentMonth,
|
|
62
63
|
setCurrentYear
|
|
63
64
|
);
|
|
65
|
+
|
|
64
66
|
useClickOutside(
|
|
65
67
|
dateRef as React.RefObject<HTMLElement>,
|
|
66
68
|
() => {
|
|
@@ -124,7 +126,10 @@ export const DatePicker = ({
|
|
|
124
126
|
onClick={toggleDatepicker}
|
|
125
127
|
className={`${
|
|
126
128
|
error ? primary["error-border"] : "border"
|
|
127
|
-
} p-2 rounded-lg w-full flex items-center gap-2 dark:text-white
|
|
129
|
+
} p-2 rounded-lg w-full flex items-center gap-2 dark:text-white ${
|
|
130
|
+
disabled ? "opacity-50 cursor-not-allowed" : ""
|
|
131
|
+
}`}
|
|
132
|
+
disabled={disabled}
|
|
128
133
|
>
|
|
129
134
|
<Icon
|
|
130
135
|
dimensions={{ width: "20", height: "20" }}
|
|
@@ -144,7 +149,7 @@ export const DatePicker = ({
|
|
|
144
149
|
readOnly
|
|
145
150
|
/>
|
|
146
151
|
|
|
147
|
-
{showDatepicker && (
|
|
152
|
+
{showDatepicker && !disabled && (
|
|
148
153
|
<div className="absolute z-10 bg-white border border-gray-300 shadow-lg mt-1 w-full rounded dark:bg-black dark:text-white px-2">
|
|
149
154
|
<div className="flex justify-between items-center p-2">
|
|
150
155
|
<button
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useCallback, useEffect, useState } from "react";
|
|
2
2
|
import { DatePicker } from "../../datepicker";
|
|
3
3
|
import { DatePickerHandlesProps } from "../types";
|
|
4
|
+
import { evaluateExpression } from "@grampro/headless-helpers";
|
|
4
5
|
|
|
5
6
|
export default function DatePickerHandles({
|
|
6
7
|
item,
|
|
@@ -8,7 +9,38 @@ export default function DatePickerHandles({
|
|
|
8
9
|
setRequirementError,
|
|
9
10
|
formRef,
|
|
10
11
|
onChangeEvent,
|
|
12
|
+
context,
|
|
13
|
+
updateContext,
|
|
11
14
|
}: DatePickerHandlesProps) {
|
|
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("datepicker", item.name, item.value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const disabled = evaluateCondition(item?.disabled);
|
|
36
|
+
|
|
37
|
+
setIsDisabled(disabled);
|
|
38
|
+
|
|
39
|
+
// Only set required if not disabled
|
|
40
|
+
const required = disabled ? false : evaluateCondition(item?.required);
|
|
41
|
+
setIsRequired(required);
|
|
42
|
+
}, [item, context, evaluateCondition, updateContext]);
|
|
43
|
+
|
|
12
44
|
const handleSelectDate = (value: string[], key: string) => {
|
|
13
45
|
if (formRef && formRef.current) {
|
|
14
46
|
const hiddenInput = formRef.current.querySelector(
|
|
@@ -34,7 +66,7 @@ export default function DatePickerHandles({
|
|
|
34
66
|
{item?.label && (
|
|
35
67
|
<label htmlFor={item.name} className="font-medium text-sm">
|
|
36
68
|
{item.label}
|
|
37
|
-
{
|
|
69
|
+
{isRequired && <span className="text-red-500">*</span>}
|
|
38
70
|
</label>
|
|
39
71
|
)}
|
|
40
72
|
<DatePicker
|
|
@@ -29,11 +29,10 @@ export default function MultiHandles({
|
|
|
29
29
|
// Initialize context and handle dynamic states
|
|
30
30
|
useEffect(() => {
|
|
31
31
|
if (item?.name && item?.value !== undefined) {
|
|
32
|
-
updateContext("select", item.name, item.value);
|
|
32
|
+
updateContext("multi-select", item.name, item.value);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const disabled = evaluateCondition(item?.disabled);
|
|
36
|
-
console.log("disabled", disabled);
|
|
37
36
|
|
|
38
37
|
setIsDisabled(disabled);
|
|
39
38
|
|
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
import InputHandles from "./componentHandles/InputHandles";
|
|
2
|
+
import SelectHandles from "./componentHandles/SelectHandles";
|
|
3
|
+
import MultiHandles from "./componentHandles/MultiHandles";
|
|
4
|
+
import DatePickerHandles from "./componentHandles/DatePickerHandles";
|
|
5
|
+
import CheckboxHandles from "./componentHandles/CheckBoxHandles";
|
|
6
|
+
import { FormItem } from "./types";
|
|
7
|
+
|
|
8
|
+
// Component Map
|
|
9
|
+
export const COMPONENT_MAP = {
|
|
10
|
+
input: InputHandles,
|
|
11
|
+
select: SelectHandles,
|
|
12
|
+
"multi-select": MultiHandles,
|
|
13
|
+
datepicker: DatePickerHandles,
|
|
14
|
+
checkbox: CheckboxHandles,
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
// Field Validation Helper
|
|
18
|
+
export const validateField = (
|
|
19
|
+
field: HTMLInputElement | null,
|
|
20
|
+
item: FormItem
|
|
21
|
+
): boolean => {
|
|
22
|
+
if (!item.required || !item.name || field?.disabled) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
// Explicitly check for null and empty value
|
|
26
|
+
if (!field) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return field.value.trim() !== "";
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Input Validation functions starts here
|
|
33
|
+
// You can add more validation functions here or
|
|
34
|
+
// use zod for more complex validation
|
|
1
35
|
export const validateEmail = (email: string) => {
|
|
2
36
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3
37
|
return emailRegex.test(email);
|
|
@@ -8,19 +42,6 @@ export const validatePhoneNumber = (phone: string) => {
|
|
|
8
42
|
return phoneRegex.test(phone);
|
|
9
43
|
};
|
|
10
44
|
|
|
11
|
-
// In case of return as objects
|
|
12
|
-
export const formDataToObject = (formData: FormData) => {
|
|
13
|
-
const obj: Record<string, any> = {};
|
|
14
|
-
formData.forEach((value, key) => {
|
|
15
|
-
try {
|
|
16
|
-
obj[key] = JSON.parse(value as string);
|
|
17
|
-
} catch {
|
|
18
|
-
obj[key] = value;
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
return obj;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
45
|
export const validateInput = (value: string, type?: string): string | null => {
|
|
25
46
|
if (!value) return null;
|
|
26
47
|
|
|
@@ -35,3 +56,17 @@ export const validateInput = (value: string, type?: string): string | null => {
|
|
|
35
56
|
return null;
|
|
36
57
|
}
|
|
37
58
|
};
|
|
59
|
+
// Input Validation functions ends here
|
|
60
|
+
|
|
61
|
+
// In case of return as objects
|
|
62
|
+
export const formDataToObject = (formData: FormData) => {
|
|
63
|
+
const obj: Record<string, any> = {};
|
|
64
|
+
formData.forEach((value, key) => {
|
|
65
|
+
try {
|
|
66
|
+
obj[key] = JSON.parse(value as string);
|
|
67
|
+
} catch {
|
|
68
|
+
obj[key] = value;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return obj;
|
|
72
|
+
};
|
|
@@ -5,145 +5,87 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React, {
|
|
8
|
+
import React, { useRef, useState, FormEvent } from "react";
|
|
9
9
|
import { useFormContext } from "@grampro/headless-helpers";
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import SelectHandles from "./componentHandles/SelectHandles";
|
|
13
|
-
import MultiHandles from "./componentHandles/MultiHandles";
|
|
14
|
-
import DatePickerHandles from "./componentHandles/DatePickerHandles";
|
|
15
|
-
import CheckboxHandles from "./componentHandles/CheckBoxHandles";
|
|
10
|
+
import { FormItem, FormRendererProps } from "./types";
|
|
11
|
+
import { COMPONENT_MAP, validateField } from "./helperFunctions";
|
|
16
12
|
|
|
17
|
-
const FormRenderer = ({
|
|
13
|
+
const FormRenderer: React.FC<FormRendererProps> = ({
|
|
18
14
|
onSubmit,
|
|
19
|
-
sourceData,
|
|
20
|
-
formFormationClass = "grid grid-cols-1
|
|
15
|
+
sourceData = [],
|
|
16
|
+
formFormationClass = "grid grid-cols-1 gap-2",
|
|
21
17
|
formParentClass = "w-96",
|
|
22
|
-
}
|
|
23
|
-
const formRef = useRef<
|
|
18
|
+
}) => {
|
|
19
|
+
const formRef = useRef<HTMLFormElement>(null);
|
|
24
20
|
const [requirementError, setRequirementError] = useState<string[]>([]);
|
|
25
|
-
|
|
26
21
|
const { context, updateContext } = useFormContext();
|
|
27
22
|
|
|
28
|
-
|
|
23
|
+
// Handle form submission
|
|
24
|
+
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
|
29
25
|
event.preventDefault();
|
|
30
|
-
const requirementErrorItems: string[] = [];
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
const requirementErrorItems = sourceData
|
|
28
|
+
.filter((item) => item.required && item.name)
|
|
29
|
+
.reduce((errors: string[], item) => {
|
|
34
30
|
const field = formRef.current?.querySelector(
|
|
35
31
|
`[name="${item.name}"]`
|
|
36
32
|
) as HTMLInputElement | null;
|
|
37
33
|
|
|
38
|
-
if (!field
|
|
39
|
-
|
|
34
|
+
if (!validateField(field, item)) {
|
|
35
|
+
errors.push(item.name!);
|
|
40
36
|
}
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
return errors;
|
|
38
|
+
}, []);
|
|
43
39
|
|
|
44
40
|
setRequirementError(requirementErrorItems);
|
|
45
41
|
|
|
46
|
-
if (requirementErrorItems.length
|
|
42
|
+
if (requirementErrorItems.length === 0 && onSubmit && formRef.current) {
|
|
43
|
+
onSubmit(new FormData(formRef.current));
|
|
44
|
+
}
|
|
45
|
+
};
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
// Render form component based on the type
|
|
48
|
+
const renderFormComponent = (item: FormItem, index: number) => {
|
|
49
|
+
if (item.component === "button") {
|
|
50
|
+
return (
|
|
51
|
+
<div key={index} className="w-full mt-1">
|
|
52
|
+
<button
|
|
53
|
+
type={item.button_type}
|
|
54
|
+
className="w-full bg-black text-white py-2 rounded-xl hover:bg-gray-800"
|
|
55
|
+
>
|
|
56
|
+
{item.value}
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const Component = item.component && COMPONENT_MAP[item.component];
|
|
63
|
+
if (!Component) return null;
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Component
|
|
67
|
+
key={index}
|
|
68
|
+
item={item}
|
|
69
|
+
requirementError={requirementError}
|
|
70
|
+
setRequirementError={setRequirementError}
|
|
71
|
+
formRef={formRef}
|
|
72
|
+
onChangeEvent={item.onChangeEvent}
|
|
73
|
+
context={context}
|
|
74
|
+
updateContext={updateContext}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
50
77
|
};
|
|
51
78
|
|
|
79
|
+
// If no source data is found, return a message
|
|
80
|
+
if (!sourceData.length) {
|
|
81
|
+
return <div>No Source Data Found</div>;
|
|
82
|
+
}
|
|
83
|
+
|
|
52
84
|
return (
|
|
53
85
|
<form ref={formRef} onSubmit={handleSubmit} className={formParentClass}>
|
|
54
|
-
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
switch (item.component) {
|
|
58
|
-
case "input":
|
|
59
|
-
return (
|
|
60
|
-
<InputHandles
|
|
61
|
-
key={index}
|
|
62
|
-
item={item}
|
|
63
|
-
requirementError={requirementError}
|
|
64
|
-
setRequirementError={setRequirementError}
|
|
65
|
-
onChangeEvent={item.onChangeEvent}
|
|
66
|
-
formRef={formRef}
|
|
67
|
-
context={context}
|
|
68
|
-
updateContext={updateContext}
|
|
69
|
-
/>
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
case "select":
|
|
73
|
-
return (
|
|
74
|
-
<SelectHandles
|
|
75
|
-
key={index}
|
|
76
|
-
item={item}
|
|
77
|
-
requirementError={requirementError}
|
|
78
|
-
setRequirementError={setRequirementError}
|
|
79
|
-
formRef={formRef}
|
|
80
|
-
onChangeEvent={item.onChangeEvent}
|
|
81
|
-
context={context}
|
|
82
|
-
updateContext={updateContext}
|
|
83
|
-
/>
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
case "multi-select":
|
|
87
|
-
return (
|
|
88
|
-
<MultiHandles
|
|
89
|
-
key={index}
|
|
90
|
-
item={item}
|
|
91
|
-
requirementError={requirementError}
|
|
92
|
-
setRequirementError={setRequirementError}
|
|
93
|
-
formRef={formRef}
|
|
94
|
-
onChangeEvent={item.onChangeEvent}
|
|
95
|
-
context={context}
|
|
96
|
-
updateContext={updateContext}
|
|
97
|
-
/>
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
case "datepicker":
|
|
101
|
-
return (
|
|
102
|
-
<DatePickerHandles
|
|
103
|
-
key={index}
|
|
104
|
-
item={item}
|
|
105
|
-
requirementError={requirementError}
|
|
106
|
-
setRequirementError={setRequirementError}
|
|
107
|
-
formRef={formRef}
|
|
108
|
-
onChangeEvent={item.onChangeEvent}
|
|
109
|
-
/>
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
case "checkbox":
|
|
113
|
-
return (
|
|
114
|
-
<CheckboxHandles
|
|
115
|
-
key={index}
|
|
116
|
-
item={item}
|
|
117
|
-
requirementError={requirementError}
|
|
118
|
-
setRequirementError={setRequirementError}
|
|
119
|
-
formRef={formRef}
|
|
120
|
-
onChangeEvent={item.onChangeEvent}
|
|
121
|
-
sourceData={sourceData}
|
|
122
|
-
context={context}
|
|
123
|
-
updateContext={updateContext}
|
|
124
|
-
/>
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
case "button":
|
|
128
|
-
return (
|
|
129
|
-
<div key={index} className="w-full mt-1">
|
|
130
|
-
<button
|
|
131
|
-
type={item.button_type}
|
|
132
|
-
className="w-full bg-black text-white py-2 rounded-xl hover:bg-gray-800"
|
|
133
|
-
>
|
|
134
|
-
{item.value}
|
|
135
|
-
</button>
|
|
136
|
-
</div>
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
default:
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
})}
|
|
143
|
-
</div>
|
|
144
|
-
) : (
|
|
145
|
-
<div>No Source Data Found</div>
|
|
146
|
-
)}
|
|
86
|
+
<div className={formFormationClass}>
|
|
87
|
+
{sourceData.map(renderFormComponent)}
|
|
88
|
+
</div>
|
|
147
89
|
</form>
|
|
148
90
|
);
|
|
149
91
|
};
|
|
@@ -9,7 +9,13 @@ export interface FormElement extends HTMLFormElement {
|
|
|
9
9
|
export type FormItem = {
|
|
10
10
|
id?: string;
|
|
11
11
|
name?: string;
|
|
12
|
-
component?:
|
|
12
|
+
component?:
|
|
13
|
+
| "input"
|
|
14
|
+
| "select"
|
|
15
|
+
| "multi-select"
|
|
16
|
+
| "datepicker"
|
|
17
|
+
| "checkbox"
|
|
18
|
+
| "button";
|
|
13
19
|
type?: string;
|
|
14
20
|
required?: boolean | string;
|
|
15
21
|
placeholder?: string;
|
|
@@ -83,6 +89,12 @@ export type DatePickerHandlesProps = {
|
|
|
83
89
|
setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
|
|
84
90
|
formRef?: React.RefObject<HTMLFormElement | null>;
|
|
85
91
|
onChangeEvent?: (event: any) => void;
|
|
92
|
+
context: FormContext;
|
|
93
|
+
updateContext: (
|
|
94
|
+
componentName: string,
|
|
95
|
+
fieldName: string,
|
|
96
|
+
value: FieldValue
|
|
97
|
+
) => void;
|
|
86
98
|
};
|
|
87
99
|
|
|
88
100
|
export type SelectHandlesProps = {
|
|
@@ -139,7 +139,13 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
|
139
139
|
{error && <p className={primary["error-primary"]}>{error}</p>}
|
|
140
140
|
</div>
|
|
141
141
|
|
|
142
|
-
<input
|
|
142
|
+
<input
|
|
143
|
+
type="hidden"
|
|
144
|
+
name={name}
|
|
145
|
+
value={selectedItem || ""}
|
|
146
|
+
readOnly
|
|
147
|
+
disabled={disabled}
|
|
148
|
+
/>
|
|
143
149
|
|
|
144
150
|
{showPopover && !disabled && (
|
|
145
151
|
<div className={popUp["pop-up-style"]}>
|