gbs-add-block 0.0.38 → 0.0.40

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.38)
1
+ # GBS Building Blocks 2.0 (v0.0.40)
2
2
 
3
3
  Latest and upgraded version of GBS building blocks with headless UI and removed dependencies.
4
4
 
@@ -6,9 +6,9 @@ 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.38)
9
+ ## What's New 🎉 (Ver 0.0.40)
10
10
 
11
- - Updated Material Input Component
11
+ - Minor Update For Code Readability (Pre-Refactoring Job)
12
12
 
13
13
  ## Authors
14
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbs-add-block",
3
- "version": "0.0.38",
3
+ "version": "0.0.40",
4
4
  "description": "React Component Library",
5
5
  "files": [
6
6
  "index.js",
@@ -1,8 +1,8 @@
1
- const getDaysInMonth = (year: any, month: any) => {
1
+ const getDaysInMonth = (year: number, month: number) => {
2
2
  return new Date(year, month + 1, 0).getDate();
3
3
  };
4
4
 
5
- export const generateCalendarHelper = (year: any, month: any) => {
5
+ export const generateCalendarHelper = (year: number, month: number) => {
6
6
  const daysInMonth = getDaysInMonth(year, month);
7
7
  const firstDay = new Date(year, month, 1).getDay();
8
8
  const weeks = [];
@@ -10,6 +10,7 @@ import { generateCalendarHelper, months } from "./DatePickerHelper";
10
10
  import Icon from "../icon/Icon";
11
11
  import { calender, down, leftArrows, rightArrows } from "../icon/iconPaths";
12
12
  import type { DatePickerProps } from "./types";
13
+ import { primary } from "../globalStyle";
13
14
 
14
15
  export const DatePicker = ({
15
16
  selectedDateValue,
@@ -18,14 +19,20 @@ export const DatePicker = ({
18
19
  yearLimitStart = 50,
19
20
  yearLimitEnd = 0,
20
21
  onDateChange,
22
+ name,
23
+ error,
24
+ placeholder = "Select a date",
21
25
  }: DatePickerProps) => {
22
26
  const [showDatepicker, setShowDatepicker] = useState(false);
23
27
  const [showYearMonthPicker, setShowYearMonthPicker] = useState(false);
24
28
  const [currentMonth, setCurrentMonth] = useState(new Date().getMonth());
25
29
  const [currentYear, setCurrentYear] = useState(new Date().getFullYear());
26
- const [days, setDays] = useState<any>([]);
27
- const [selectedDate, setSelectedDate] = useState<any>(
28
- selectedDateValue ? selectedDateValue : new Date()
30
+ const [days, setDays] = useState<(Date | null)[][]>([]);
31
+ const [selectedDate, setSelectedDate] = useState<Date | null>(
32
+ selectedDateValue || null
33
+ );
34
+ const [tempSelectedDate, setTempSelectedDate] = useState<Date | null>(
35
+ selectedDateValue || new Date()
29
36
  );
30
37
  const [hasMounted, setHasMounted] = useState(false);
31
38
 
@@ -41,6 +48,7 @@ export const DatePicker = ({
41
48
  useEffect(() => {
42
49
  if (selectedDateValue) {
43
50
  setSelectedDate(selectedDateValue);
51
+ setTempSelectedDate(selectedDateValue);
44
52
  setCurrentMonth(selectedDateValue.getMonth());
45
53
  setCurrentYear(selectedDateValue.getFullYear());
46
54
  }
@@ -54,6 +62,7 @@ export const DatePicker = ({
54
62
  function handleClickOutside(event: MouseEvent) {
55
63
  if (dateRef.current && !dateRef.current.contains(event.target as Node)) {
56
64
  setShowDatepicker(false);
65
+ setTempSelectedDate(selectedDate || new Date());
57
66
  }
58
67
  }
59
68
 
@@ -61,7 +70,7 @@ export const DatePicker = ({
61
70
  return () => {
62
71
  document.removeEventListener("mousedown", handleClickOutside);
63
72
  };
64
- }, []);
73
+ }, [selectedDate]);
65
74
 
66
75
  useEffect(() => {
67
76
  setDays(generateCalendarHelper(currentYear, currentMonth));
@@ -70,19 +79,26 @@ export const DatePicker = ({
70
79
  const toggleDatepicker = () => {
71
80
  setShowDatepicker(!showDatepicker);
72
81
  setShowYearMonthPicker(false);
82
+ if (!showDatepicker) {
83
+ setTempSelectedDate(selectedDate || new Date());
84
+ const date = selectedDate || new Date();
85
+ setCurrentMonth(date.getMonth());
86
+ setCurrentYear(date.getFullYear());
87
+ }
73
88
  };
74
89
 
75
90
  const toggleYearMonthPicker = () => {
76
91
  setShowYearMonthPicker(!showYearMonthPicker);
77
92
  };
78
93
 
79
- const selectDate = (date: any) => {
94
+ const selectDate = (date: Date) => {
80
95
  setSelectedDate(date);
96
+ setTempSelectedDate(date);
81
97
  setShowDatepicker(false);
82
98
  if (onDateChange) onDateChange(date);
83
99
  };
84
100
 
85
- const selectYearMonth = (year: any, month: any) => {
101
+ const selectYearMonth = (year: number, month: number) => {
86
102
  setCurrentYear(year);
87
103
  setCurrentMonth(month);
88
104
  setShowYearMonthPicker(false);
@@ -108,10 +124,13 @@ export const DatePicker = ({
108
124
  };
109
125
 
110
126
  const goToToday = () => {
127
+ const today = new Date();
111
128
  setCurrentMonth(today.getMonth());
112
129
  setCurrentYear(today.getFullYear());
113
- setSelectedDate(new Date());
114
- if (onDateChange) onDateChange(new Date());
130
+ setSelectedDate(today);
131
+ setTempSelectedDate(today);
132
+ if (onDateChange) onDateChange(today);
133
+ setShowDatepicker(false);
115
134
  };
116
135
 
117
136
  if (!hasMounted) {
@@ -119,19 +138,32 @@ export const DatePicker = ({
119
138
  }
120
139
 
121
140
  return (
122
- <div className="relative inline-block w-80 dark:bg-black" ref={dateRef}>
141
+ <div className="relative inline-block w-full dark:bg-black" ref={dateRef}>
123
142
  <button
124
143
  type="button"
125
144
  onClick={toggleDatepicker}
126
- className="border p-2 rounded w-full flex items-center gap-2 dark:text-white"
145
+ className={`${
146
+ error ? primary["error-border"] : "border"
147
+ } p-2 rounded-lg w-full flex items-center gap-2 dark:text-white`}
127
148
  >
128
149
  <Icon
129
150
  dimensions={{ width: "20", height: "20" }}
130
151
  elements={calender}
131
152
  svgClass={"stroke-gray-500 fill-none dark:stroke-white"}
132
153
  />
133
- {selectedDate.toLocaleDateString()}
154
+ <span className={!selectedDate ? "text-gray-400" : ""}>
155
+ {selectedDate ? selectedDate.toLocaleDateString() : placeholder}
156
+ </span>
134
157
  </button>
158
+ {error && <p className={primary["error-primary"]}>{error}</p>}
159
+
160
+ {/* Hidden input to integrate with the form */}
161
+ <input
162
+ type="hidden"
163
+ name={name}
164
+ value={selectedDate?.toDateString() || ""}
165
+ readOnly
166
+ />
135
167
 
136
168
  {showDatepicker && (
137
169
  <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">
@@ -223,18 +255,20 @@ export const DatePicker = ({
223
255
  {day}
224
256
  </div>
225
257
  ))}
226
- {days.flat().map((day: any, index: any) => {
258
+ {days.flat().map((day: Date | null, index: number) => {
227
259
  if (!day) {
228
260
  return (
229
261
  <div key={index} className="text-center p-1 w-8 h-8"></div>
230
262
  );
231
263
  }
232
- const isDisabled: any =
233
- (minDate && day < minDate) || (maxDate && day > maxDate);
264
+ const isDisabled: boolean | undefined =
265
+ (minDate && day < minDate) ||
266
+ (maxDate && day > maxDate) ||
267
+ undefined;
234
268
  const isSelected =
235
- day.getDate() === selectedDate?.getDate() &&
236
- day.getMonth() === selectedDate?.getMonth() &&
237
- day.getFullYear() === selectedDate?.getFullYear();
269
+ day.getDate() === tempSelectedDate?.getDate() &&
270
+ day.getMonth() === tempSelectedDate?.getMonth() &&
271
+ day.getFullYear() === tempSelectedDate?.getFullYear();
238
272
 
239
273
  return (
240
274
  <button
@@ -5,5 +5,7 @@ export type DatePickerProps = {
5
5
  maxDate?: Date | null;
6
6
  yearLimitStart?: number;
7
7
  yearLimitEnd?: number;
8
- onDateChange?: any;
8
+ onDateChange?: (date: Date) => void;
9
+ name?: string;
10
+ error?: string;
9
11
  };
@@ -0,0 +1,72 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { CheckboxHandlesProps, FormItem } from "./types";
3
+
4
+ const CheckboxHandles = ({
5
+ item,
6
+ formRef,
7
+ requirementError,
8
+ setRequirementError,
9
+ }: CheckboxHandlesProps) => {
10
+ const [checked, setChecked] = useState<boolean>(!!item?.value);
11
+
12
+ useEffect(() => {
13
+ if (item?.name) {
14
+ handleSelect(checked, item.name);
15
+ }
16
+ }, []);
17
+
18
+ const handleSelect = (value: boolean, key: string) => {
19
+ if (formRef && formRef.current) {
20
+ const hiddenInput = formRef.current.querySelector(
21
+ `input[name="${key}"]`
22
+ ) as HTMLInputElement;
23
+
24
+ if (hiddenInput) {
25
+ hiddenInput.value = value.toString();
26
+ } else {
27
+ const input = document.createElement("input");
28
+ input.type = "hidden";
29
+ input.name = key;
30
+ input.value = value.toString();
31
+ formRef.current.appendChild(input);
32
+ }
33
+ }
34
+ };
35
+
36
+ return (
37
+ <div className="w-full flex items-center gap-2">
38
+ <input
39
+ type="checkbox"
40
+ checked={checked}
41
+ onChange={(e) => {
42
+ const newValue = e.target.checked;
43
+ setChecked(newValue);
44
+ if (item?.name) {
45
+ handleSelect(newValue, item.name);
46
+ setRequirementError?.((prevErrors) =>
47
+ prevErrors.filter((error) => error !== item.name)
48
+ );
49
+ }
50
+ }}
51
+ className={`w-5 h-5 cursor-pointer rounded-md border-2 transition-colors duration-200 ${
52
+ item?.name && requirementError.includes(item?.name)
53
+ ? "border-red-500"
54
+ : "border-gray-300"
55
+ }`}
56
+ />
57
+ {item?.label && (
58
+ <label htmlFor={item.name} className="font-medium text-sm">
59
+ {item.label}
60
+ {item.required && <span className="text-red-500">*</span>}
61
+ </label>
62
+ )}
63
+ {item?.name && requirementError.includes(item.name) && (
64
+ <p className="text-red-500 text-xs">{`${
65
+ item?.label || item?.name
66
+ } is required`}</p>
67
+ )}
68
+ </div>
69
+ );
70
+ };
71
+
72
+ export default CheckboxHandles;
@@ -0,0 +1,65 @@
1
+ import React from "react";
2
+ import { DatePicker } from "../datepicker";
3
+ import { FormItem } from "./types";
4
+
5
+ interface DatePickerHandlesProps {
6
+ item?: FormItem;
7
+ requirementError: string[];
8
+ setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
9
+ formRef?: React.RefObject<HTMLFormElement | null>;
10
+ }
11
+
12
+ export default function DatePickerHandles({
13
+ item,
14
+ requirementError,
15
+ setRequirementError,
16
+ formRef,
17
+ }: DatePickerHandlesProps) {
18
+ const handleSelectDate = (value: string[], key: string) => {
19
+ if (formRef && formRef.current) {
20
+ const hiddenInput = formRef.current.querySelector(
21
+ `input[name="${key}"]`
22
+ ) as HTMLInputElement;
23
+
24
+ if (hiddenInput) {
25
+ hiddenInput.value = Array.isArray(value)
26
+ ? JSON.stringify(value)
27
+ : value;
28
+ } else {
29
+ const input = document.createElement("input");
30
+ input.type = "hidden";
31
+ input.name = key;
32
+ input.value = Array.isArray(value) ? JSON.stringify(value) : value;
33
+ formRef.current.appendChild(input);
34
+ }
35
+ }
36
+ };
37
+
38
+ return (
39
+ <div className="w-full">
40
+ {item?.label && (
41
+ <label htmlFor={item.name} className="font-medium text-sm">
42
+ {item.label}
43
+ {item.required && <span className="text-red-500">*</span>}
44
+ </label>
45
+ )}
46
+ <DatePicker
47
+ name={item?.name}
48
+ onDateChange={(value: any) => {
49
+ if (item?.name) {
50
+ handleSelectDate(value, item.name);
51
+ setRequirementError?.((prevErrors) =>
52
+ prevErrors.filter((errorName) => errorName !== item.name)
53
+ );
54
+ }
55
+ }}
56
+ error={
57
+ item?.name && requirementError.includes(item.name)
58
+ ? `${item.name} is required`
59
+ : undefined
60
+ }
61
+ selectedDateValue={item?.value ? new Date(item.value) : undefined}
62
+ />
63
+ </div>
64
+ );
65
+ }
@@ -8,8 +8,10 @@
8
8
  import React, { useRef, useState } from "react";
9
9
  import InputHandles from "./InputHandles";
10
10
  import SelectHandles from "./SelectHandles";
11
- import { FormItem, FormRendererProps } from "./types";
11
+ import { FormElement, FormItem, FormRendererProps } from "./types";
12
12
  import MultiHandles from "./MultiHandles";
13
+ import DatePickerHandles from "./DatePickerHandles";
14
+ import CheckboxHandles from "./CheckBoxHandles";
13
15
 
14
16
  const FormRenderer = ({
15
17
  onSubmit,
@@ -18,7 +20,7 @@ const FormRenderer = ({
18
20
  formParentClass = "w-96",
19
21
  dependencyConfig,
20
22
  }: FormRendererProps) => {
21
- const formRef = useRef<HTMLFormElement>(null);
23
+ const formRef = useRef<FormElement>(null);
22
24
  const [requirementError, setRequirementError] = useState<string[]>([]);
23
25
 
24
26
  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
@@ -84,6 +86,28 @@ const FormRenderer = ({
84
86
  />
85
87
  );
86
88
 
89
+ case "datepicker":
90
+ return (
91
+ <DatePickerHandles
92
+ key={index}
93
+ item={item}
94
+ requirementError={requirementError}
95
+ setRequirementError={setRequirementError}
96
+ formRef={formRef}
97
+ />
98
+ );
99
+
100
+ case "checkbox":
101
+ return (
102
+ <CheckboxHandles
103
+ key={index}
104
+ item={item}
105
+ requirementError={requirementError}
106
+ setRequirementError={setRequirementError}
107
+ formRef={formRef}
108
+ />
109
+ );
110
+
87
111
  case "button":
88
112
  return (
89
113
  <div key={index} className="w-full mt-1">
@@ -1,3 +1,7 @@
1
+ export interface FormElement extends HTMLFormElement {
2
+ [key: string]: any;
3
+ }
4
+
1
5
  export type FormItem = {
2
6
  id?: string;
3
7
  name?: string;
@@ -48,3 +52,10 @@ export type Option = {
48
52
  value: string;
49
53
  label: string;
50
54
  };
55
+
56
+ export type CheckboxHandlesProps = {
57
+ item?: FormItem;
58
+ requirementError: string[];
59
+ setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
60
+ formRef?: React.RefObject<HTMLFormElement | null>;
61
+ };
@@ -93,7 +93,7 @@ export const Input = ({
93
93
  />
94
94
  </button>
95
95
  )}
96
- <div className="text-xs text-red-500">{error && error}</div>
96
+ {error && <div className="text-xs text-red-500">{error}</div>}
97
97
  </div>
98
98
  ) : (
99
99
  <div className="otp-container">
@@ -62,7 +62,7 @@ const MaterialInput = ({
62
62
  ${
63
63
  error
64
64
  ? "border-red-500 focus:border-red-500"
65
- : "border-gray-300 focus:border-blue-500"
65
+ : "border-gray-300 focus:border-black"
66
66
  }
67
67
  ${disabled ? "text-gray-400 cursor-not-allowed" : "text-gray-900"}
68
68
  `}
@@ -91,7 +91,7 @@ const MaterialInput = ({
91
91
  ${
92
92
  error
93
93
  ? "border-red-500 focus:border-red-500"
94
- : "border-gray-300 focus:border-blue-500"
94
+ : "border-gray-300 focus:border-black"
95
95
  }
96
96
  ${disabled ? "text-gray-400 cursor-not-allowed" : "text-gray-900"}
97
97
  `}
@@ -109,7 +109,7 @@ const MaterialInput = ({
109
109
  error
110
110
  ? "text-red-500"
111
111
  : isFocused
112
- ? "text-blue-500"
112
+ ? "text-black"
113
113
  : "text-gray-500"
114
114
  }
115
115
  ${disabled ? "text-gray-400 cursor-not-allowed" : ""}
@@ -177,7 +177,7 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
177
177
  </button>
178
178
  </div>
179
179
  </div>
180
- <p className={primary["error-primary"]}>{error && error}</p>
180
+ {error && <p className={primary["error-primary"]}>{error}</p>}
181
181
 
182
182
  {/* Hidden input to integrate with the form */}
183
183
  <input
@@ -172,7 +172,7 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
172
172
  />
173
173
  </button>
174
174
  )}
175
- <p className={primary["error-primary"]}>{error && error}</p>
175
+ {error && <p className={primary["error-primary"]}>{error}</p>}
176
176
  </div>
177
177
 
178
178
  {/* Hidden input to integrate with the form */}