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 CHANGED
@@ -1,4 +1,4 @@
1
- # GBS Building Blocks 2.0 (v0.0.49)
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.49)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbs-add-block",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "description": "React Component Library",
5
5
  "files": [
6
6
  "index.js",
@@ -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
@@ -8,4 +8,5 @@ export type DatePickerProps = {
8
8
  onDateChange?: (date: Date) => void;
9
9
  name?: string;
10
10
  error?: string;
11
+ disabled?: boolean;
11
12
  };
@@ -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
- {item.required && <span className="text-red-500">*</span>}
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
 
@@ -34,7 +34,6 @@ export default function SelectHandles({
34
34
  }
35
35
 
36
36
  const disabled = evaluateCondition(item?.disabled);
37
- console.log("disabled", disabled);
38
37
 
39
38
  setIsDisabled(disabled);
40
39
 
@@ -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, { useEffect, useRef, useState } from "react";
8
+ import React, { useRef, useState, FormEvent } from "react";
9
9
  import { useFormContext } from "@grampro/headless-helpers";
10
- import { FormElement, FormItem, FormRendererProps } from "./types";
11
- import InputHandles from "./componentHandles/InputHandles";
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 text-left gap-4",
15
+ sourceData = [],
16
+ formFormationClass = "grid grid-cols-1 gap-2",
21
17
  formParentClass = "w-96",
22
- }: FormRendererProps) => {
23
- const formRef = useRef<FormElement>(null);
18
+ }) => {
19
+ const formRef = useRef<HTMLFormElement>(null);
24
20
  const [requirementError, setRequirementError] = useState<string[]>([]);
25
-
26
21
  const { context, updateContext } = useFormContext();
27
22
 
28
- const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
23
+ // Handle form submission
24
+ const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
29
25
  event.preventDefault();
30
- const requirementErrorItems: string[] = [];
31
26
 
32
- sourceData?.forEach((item: FormItem) => {
33
- if (item.required && item.name) {
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 || !field.value.trim()) {
39
- requirementErrorItems.push(item.name);
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 > 0) return;
42
+ if (requirementErrorItems.length === 0 && onSubmit && formRef.current) {
43
+ onSubmit(new FormData(formRef.current));
44
+ }
45
+ };
47
46
 
48
- const formData = new FormData(formRef.current as HTMLFormElement);
49
- if (onSubmit) onSubmit(formData);
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
- {sourceData && sourceData?.length > 0 ? (
55
- <div className={formFormationClass}>
56
- {sourceData.map((item: FormItem, index: number) => {
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?: string;
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 = {
@@ -154,6 +154,7 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
154
154
  name={name}
155
155
  value={JSON.stringify(selected) || ""}
156
156
  readOnly
157
+ disabled={disabled}
157
158
  />
158
159
 
159
160
  {showPopover && !disabled && (
@@ -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 type="hidden" name={name} value={selectedItem || ""} readOnly />
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"]}>