gbs-add-block 0.0.38 → 0.0.39

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.39)
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.39)
10
10
 
11
- - Updated Material Input Component
11
+ - Form Renderer Now Supports Date Picker and CheckBox
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.39",
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
+ <p className={primary["error-primary"]}>{error && 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
+ };