gbs-add-block 0.0.1

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.
Files changed (37) hide show
  1. package/index.js +108 -0
  2. package/package.json +28 -0
  3. package/source/components/button/index.tsx +25 -0
  4. package/source/components/checkbox/index.tsx +9 -0
  5. package/source/components/darkmode/index.tsx +48 -0
  6. package/source/components/datepicker/DatePickerHelper.ts +42 -0
  7. package/source/components/datepicker/index.tsx +258 -0
  8. package/source/components/datepicker/types.ts +9 -0
  9. package/source/components/dialog/index.tsx +44 -0
  10. package/source/components/dialog/types.ts +12 -0
  11. package/source/components/grid/FilterPopup.tsx +85 -0
  12. package/source/components/grid/Grid.tsx +615 -0
  13. package/source/components/grid/GridHelperFunctions.ts +218 -0
  14. package/source/components/grid/index.tsx +26 -0
  15. package/source/components/grid/type.ts +28 -0
  16. package/source/components/input/index.tsx +105 -0
  17. package/source/components/input/types.ts +9 -0
  18. package/source/components/modal/index.tsx +58 -0
  19. package/source/components/modal/types.ts +11 -0
  20. package/source/components/multiselect/index.tsx +213 -0
  21. package/source/components/multiselect/types.ts +14 -0
  22. package/source/components/select/index.tsx +220 -0
  23. package/source/components/select/types.ts +13 -0
  24. package/source/components/spinner/Stroke.tsx +89 -0
  25. package/source/components/spinner/index.tsx +54 -0
  26. package/source/components/spinner/types.ts +9 -0
  27. package/source/components/toast/Toast.tsx +61 -0
  28. package/source/components/toast/index.tsx +36 -0
  29. package/source/components/toast/toastAtom.ts +23 -0
  30. package/source/components/toast/types.ts +24 -0
  31. package/source/components/toast/useToast.ts +24 -0
  32. package/source/fallback/index.tsx +11 -0
  33. package/source/icon/Icon.tsx +55 -0
  34. package/source/icon/iconPaths.ts +92 -0
  35. package/source/index.ts +15 -0
  36. package/source/types.ts +108 -0
  37. package/source/utils.ts +10 -0
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Copyright (c) Grampro Business Services and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import React, {
9
+ forwardRef,
10
+ memo,
11
+ useCallback,
12
+ useEffect,
13
+ useImperativeHandle,
14
+ useMemo,
15
+ useRef,
16
+ useState,
17
+ } from "react";
18
+ import Icon from "../icon/Icon";
19
+ import { check, search, upDown, x } from "../icon/iconPaths";
20
+ import { getSourceData } from "../utils";
21
+ import type { SelectProps } from "./types";
22
+
23
+ interface ItemsProps {
24
+ value: string;
25
+ label: string;
26
+ }
27
+
28
+ const Select = forwardRef<any, SelectProps>((props, ref) => {
29
+ const {
30
+ placeholder = "Select an Item...",
31
+ items,
32
+ lazy = false,
33
+ showSearch = true,
34
+ onSelect,
35
+ selectedItem: initialSelectedItem,
36
+ } = props;
37
+
38
+ const [showPopover, setShowPopover] = useState(false);
39
+ const [workingDataSource, setWorkingDataSource] = useState<ItemsProps[]>([]);
40
+ const [searchTerm, setSearchTerm] = useState("");
41
+ const [selectedItem, setSelectedItem] = useState<string | undefined>(
42
+ initialSelectedItem
43
+ );
44
+ const inputRef = useRef<HTMLInputElement>(null);
45
+ const selectRef = useRef<HTMLDivElement>(null);
46
+
47
+ // This will close the popup if clicked outside of the component
48
+ const handleClickOutside = useCallback((event: MouseEvent) => {
49
+ if (
50
+ selectRef.current &&
51
+ !selectRef.current.contains(event.target as Node)
52
+ ) {
53
+ setShowPopover(false);
54
+ }
55
+ }, []);
56
+
57
+ // This will close the popup if clicked outside of the component
58
+ useEffect(() => {
59
+ document.addEventListener("mousedown", handleClickOutside);
60
+ return () => {
61
+ document.removeEventListener("mousedown", handleClickOutside);
62
+ };
63
+ }, [handleClickOutside]);
64
+
65
+ // Focus to input feild when popup opens
66
+ useEffect(() => {
67
+ if (showPopover) {
68
+ inputRef.current?.focus();
69
+ }
70
+ }, [showPopover]);
71
+
72
+ // Handles if items is an API URL
73
+ const getSelectItems = useCallback(async (itemsApi: string) => {
74
+ const itemsData = await getSourceData(itemsApi);
75
+ setWorkingDataSource(itemsData.sourcedata);
76
+ }, []);
77
+
78
+ useEffect(() => {
79
+ if (Array.isArray(items)) {
80
+ setWorkingDataSource(items);
81
+ } else if (typeof items === "string") {
82
+ getSelectItems(items);
83
+ }
84
+ }, [items, getSelectItems]);
85
+
86
+ useEffect(() => {
87
+ setSelectedItem(initialSelectedItem);
88
+ }, [initialSelectedItem]);
89
+
90
+ //Sets if a default value is passed
91
+ const selectedDisplay = useMemo(() => {
92
+ if (selectedItem && workingDataSource.length > 0) {
93
+ const selected = workingDataSource.find(
94
+ (item) => item.value === selectedItem
95
+ );
96
+ return selected ? selected.label : "";
97
+ }
98
+ return "";
99
+ }, [selectedItem, workingDataSource]);
100
+
101
+ // Toggles Popover
102
+ const togglePopover = useCallback((e: React.MouseEvent) => {
103
+ e.stopPropagation();
104
+ setShowPopover((prev) => !prev);
105
+ }, []);
106
+
107
+ // Handles Selection of items
108
+ const handleSelect = useCallback(
109
+ (value: string) => {
110
+ setSelectedItem(value);
111
+ setShowPopover(false);
112
+ setSearchTerm("");
113
+ if (onSelect) onSelect(value);
114
+ },
115
+ [onSelect]
116
+ );
117
+
118
+ // Clears Selection
119
+ const clearSelected = useCallback(() => {
120
+ setSelectedItem(undefined);
121
+ if (onSelect) onSelect("");
122
+ setShowPopover(false);
123
+ setSearchTerm("");
124
+ }, [onSelect]);
125
+
126
+ // Filtering logic
127
+ const filteredItems = useMemo(() => {
128
+ if (!searchTerm || lazy) return workingDataSource;
129
+ return workingDataSource.filter((item) =>
130
+ Object.values(item).some((val) =>
131
+ val.toString().toLowerCase().includes(searchTerm.toLowerCase())
132
+ )
133
+ );
134
+ }, [workingDataSource, searchTerm, lazy]);
135
+
136
+ // Making Select Functions Accessible in Paren
137
+ useImperativeHandle(ref, () => ({
138
+ workingDataSource,
139
+ items,
140
+ clearSelected,
141
+ togglePopover,
142
+ getSelectItems,
143
+ selectedDisplay,
144
+ selected: selectedItem,
145
+ }));
146
+
147
+ return (
148
+ <div className="relative" ref={selectRef}>
149
+ <div className="w-[200px] relative">
150
+ <button
151
+ className="flex items-center border px-4 py-2 w-[200px] justify-between rounded-lg font-medium text-sm dark:bg-black dark:border-white dark:text-white"
152
+ onClick={togglePopover}
153
+ >
154
+ {selectedDisplay || placeholder}
155
+ <Icon
156
+ elements={upDown}
157
+ svgClass="h-4 w-4 stroke-gray-500 fill-none dark:stroke-white"
158
+ />
159
+ </button>
160
+ {selectedDisplay && (
161
+ <button
162
+ className="absolute right-8 top-0 h-full flex items-center px-2 z-20"
163
+ onClick={clearSelected}
164
+ >
165
+ <Icon
166
+ elements={x}
167
+ svgClass="h-4 w-4 stroke-gray-500 fill-none dark:stroke-white"
168
+ />
169
+ </button>
170
+ )}
171
+ </div>
172
+
173
+ {showPopover && (
174
+ <div className="w-[200px] absolute overflow-y-auto border px-2 rounded-lg mt-[1px] scrollbar bg-white z-50 scrollbar h-auto dark:bg-black dark:text-white">
175
+ {showSearch && (
176
+ <div className="flex p-2 gap-1 items-center sticky top-0 bg-white border-b dark:bg-black">
177
+ <Icon
178
+ elements={search}
179
+ svgClass="stroke-gray-500 fill-none dark:stroke-white"
180
+ />
181
+ <input
182
+ autoComplete="off"
183
+ type="text"
184
+ name="search"
185
+ id="search"
186
+ placeholder="Search a value"
187
+ className="w-full outline-none dark:bg-black"
188
+ ref={inputRef}
189
+ value={searchTerm}
190
+ onChange={(e) => setSearchTerm(e.target.value)}
191
+ />
192
+ </div>
193
+ )}
194
+ {filteredItems.length > 0 ? (
195
+ filteredItems.map(({ value, label }) => (
196
+ <button
197
+ key={value}
198
+ className="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"
199
+ onClick={() => handleSelect(value)}
200
+ >
201
+ <Icon
202
+ elements={check}
203
+ svgClass={`h-4 w-4 fill-none ${
204
+ selectedItem === value ? "stroke-gray-500" : ""
205
+ }`}
206
+ />
207
+ {label}
208
+ </button>
209
+ ))
210
+ ) : (
211
+ <div className="text-sm text-center">No Data Found</div>
212
+ )}
213
+ </div>
214
+ )}
215
+ </div>
216
+ );
217
+ });
218
+
219
+ const MemoizedSelect = memo(Select);
220
+ export { MemoizedSelect as Select };
@@ -0,0 +1,13 @@
1
+ interface ItemsProps {
2
+ value: string;
3
+ label: string;
4
+ }
5
+
6
+ export type SelectProps = {
7
+ placeholder?: string;
8
+ items?: ItemsProps[] | string;
9
+ lazy?: boolean;
10
+ showSearch?: boolean;
11
+ onSelect?: any;
12
+ selectedItem?: string;
13
+ };
@@ -0,0 +1,89 @@
1
+ import React from "react";
2
+
3
+ interface StrokeProps {
4
+ size?: string;
5
+ strokeColor?: string;
6
+ }
7
+
8
+ export default function Stroke(props: StrokeProps) {
9
+ const { size, strokeColor } = props;
10
+ return (
11
+ <svg
12
+ className={`${size} animate-spin ${strokeColor}`}
13
+ viewBox="0 0 256 256"
14
+ >
15
+ <line
16
+ x1="128"
17
+ y1="32"
18
+ x2="128"
19
+ y2="64"
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ strokeWidth="24"
23
+ ></line>
24
+ <line
25
+ x1="195.9"
26
+ y1="60.1"
27
+ x2="173.3"
28
+ y2="82.7"
29
+ strokeLinecap="round"
30
+ strokeLinejoin="round"
31
+ strokeWidth="24"
32
+ ></line>
33
+ <line
34
+ x1="224"
35
+ y1="128"
36
+ x2="192"
37
+ y2="128"
38
+ strokeLinecap="round"
39
+ strokeLinejoin="round"
40
+ strokeWidth="24"
41
+ ></line>
42
+ <line
43
+ x1="195.9"
44
+ y1="195.9"
45
+ x2="173.3"
46
+ y2="173.3"
47
+ strokeLinecap="round"
48
+ strokeLinejoin="round"
49
+ strokeWidth="24"
50
+ ></line>
51
+ <line
52
+ x1="128"
53
+ y1="224"
54
+ x2="128"
55
+ y2="192"
56
+ strokeLinecap="round"
57
+ strokeLinejoin="round"
58
+ strokeWidth="24"
59
+ ></line>
60
+ <line
61
+ x1="60.1"
62
+ y1="195.9"
63
+ x2="82.7"
64
+ y2="173.3"
65
+ strokeLinecap="round"
66
+ strokeLinejoin="round"
67
+ strokeWidth="24"
68
+ ></line>
69
+ <line
70
+ x1="32"
71
+ y1="128"
72
+ x2="64"
73
+ y2="128"
74
+ strokeLinecap="round"
75
+ strokeLinejoin="round"
76
+ strokeWidth="24"
77
+ ></line>
78
+ <line
79
+ x1="60.1"
80
+ y1="60.1"
81
+ x2="82.7"
82
+ y2="82.7"
83
+ strokeLinecap="round"
84
+ strokeLinejoin="round"
85
+ strokeWidth="24"
86
+ ></line>
87
+ </svg>
88
+ );
89
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Copyright (c) Grampro Business Services and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import React from "react";
9
+ import Stroke from "./Stroke";
10
+ import type { SpinnerProps } from "./types";
11
+
12
+ export const Spinner: React.FC<SpinnerProps> = ({
13
+ size = "h-10 w-10",
14
+ color = "border-blue-600",
15
+ fullCircleColor = "border-t-red-400",
16
+ dotColor = "bg-blue-500",
17
+ duration = "duration-500",
18
+ type = "circle",
19
+ strokeColor = "stroke-blue-500",
20
+ }) => {
21
+ if (type === "circle") {
22
+ return (
23
+ <div
24
+ className={`loader ease-linear rounded-full border-4 border-t-4 ${color} ${size} ${duration} border-t-transparent animate-spin`}
25
+ ></div>
26
+ );
27
+ } else if (type === "circle-bg") {
28
+ return (
29
+ <div
30
+ className={`${size} animate-spin rounded-full border-4 ${fullCircleColor}`}
31
+ ></div>
32
+ );
33
+ } else if (type === "dot") {
34
+ return (
35
+ <div className="flex space-x-2 dark:invert">
36
+ <div
37
+ className={`${size} ${dotColor} rounded-full animate-bounce`}
38
+ style={{ animationDelay: "-0.3s" }}
39
+ ></div>
40
+ <div
41
+ className={`${size} ${dotColor} rounded-full animate-bounce`}
42
+ style={{ animationDelay: "-0.15s" }}
43
+ ></div>
44
+ <div
45
+ className={`${size} ${dotColor} rounded-full animate-bounce`}
46
+ ></div>
47
+ </div>
48
+ );
49
+ } else if (type === "stroke") {
50
+ return <Stroke size={size} strokeColor={strokeColor} />;
51
+ } else {
52
+ return null;
53
+ }
54
+ };
@@ -0,0 +1,9 @@
1
+ export type SpinnerProps = {
2
+ size?: string;
3
+ color?: string;
4
+ fullCircleColor?: string;
5
+ dotColor?: string;
6
+ duration?: string;
7
+ type?: "circle" | "circle-bg" | "dot" | "stroke";
8
+ strokeColor?: string;
9
+ };
@@ -0,0 +1,61 @@
1
+ import React from "react";
2
+ import { Toast as ToastType } from "./types";
3
+ import { useToastStore } from "./toastAtom";
4
+ import Icon from "../icon/Icon";
5
+ import { circleCheck, info, x, xCircle } from "../icon/iconPaths";
6
+
7
+ interface ToastProps {
8
+ toast: ToastType;
9
+ }
10
+
11
+ const Toast: React.FC<ToastProps> = ({ toast }) => {
12
+ const { dismissToast } = useToastStore();
13
+ const icons = {
14
+ success: (
15
+ <Icon
16
+ elements={circleCheck}
17
+ svgClass="h-4 w-4 stroke-green-500 fill-none dark:stroke-white"
18
+ />
19
+ ),
20
+ error: (
21
+ <Icon
22
+ elements={xCircle}
23
+ svgClass="h-4 w-4 stroke-red-500 fill-none dark:stroke-white"
24
+ />
25
+ ),
26
+ info: (
27
+ <Icon
28
+ elements={info}
29
+ svgClass="h-4 w-4 stroke-blue-500 fill-none dark:stroke-white"
30
+ />
31
+ ),
32
+ };
33
+
34
+ return (
35
+ <div
36
+ className={`bg-white px-2 py-3 rounded-lg text-sm flex justify-between w-80 m-2 border shadow-lg ${
37
+ toast.type === "success"
38
+ ? "text-green-500"
39
+ : toast.type === "error"
40
+ ? "text-red-500"
41
+ : "text-blue-500"
42
+ }`}
43
+ role="alert"
44
+ >
45
+ <div className="flex gap-2 items-center">
46
+ {icons[toast.type]}
47
+ <div>{toast.message}</div>
48
+ </div>
49
+ {toast.dismissible && (
50
+ <button onClick={() => dismissToast(toast.id)}>
51
+ <Icon
52
+ elements={x}
53
+ svgClass="h-4 w-4 stroke-gray-500 fill-none dark:stroke-white"
54
+ />
55
+ </button>
56
+ )}
57
+ </div>
58
+ );
59
+ };
60
+
61
+ export default Toast;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Copyright (c) Grampro Business Services and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import React from "react";
9
+ import { useToastStore } from "./toastAtom";
10
+ import Toast from "./Toast";
11
+ import { ToastPosition } from "./types";
12
+
13
+ interface ToastsProps {
14
+ position?: ToastPosition;
15
+ }
16
+
17
+ const positionClasses: Record<ToastPosition, string> = {
18
+ "top-left": "top-0 left-0",
19
+ "top-right": "top-0 right-0",
20
+ "bottom-left": "bottom-0 left-0",
21
+ "bottom-right": "bottom-0 right-0",
22
+ };
23
+
24
+ export const Toasts: React.FC<ToastsProps> = ({ position = "top-right" }) => {
25
+ const { toasts } = useToastStore();
26
+
27
+ return (
28
+ <section
29
+ className={`fixed flex flex-col z-50 p-4 ${positionClasses[position]}`}
30
+ >
31
+ {toasts.map((toast) => (
32
+ <Toast key={toast.id} toast={toast} />
33
+ ))}
34
+ </section>
35
+ );
36
+ };
@@ -0,0 +1,23 @@
1
+ import { atom, useAtom } from "jotai";
2
+ import { Toast } from "./types";
3
+
4
+ export const toastsAtom = atom<Toast[]>([]);
5
+
6
+ export const useToastStore = () => {
7
+ const [toasts, setToasts] = useAtom(toastsAtom);
8
+
9
+ const addToast = (toast: Omit<Toast, "id">) => {
10
+ const id = Math.floor(Math.random() * 10000);
11
+ const newToast = { ...toast, id };
12
+ setToasts((prev) => [newToast, ...prev]);
13
+ if (toast.timeout) {
14
+ setTimeout(() => dismissToast(id), toast.timeout);
15
+ }
16
+ };
17
+
18
+ const dismissToast = (id: number) => {
19
+ setToasts((prev) => prev.filter((t) => t.id !== id));
20
+ };
21
+
22
+ return { toasts, addToast, dismissToast };
23
+ };
@@ -0,0 +1,24 @@
1
+ import { ReactNode } from "react";
2
+
3
+ export type ToastType = "success" | "error" | "info";
4
+
5
+ export type Toast = {
6
+ id: number;
7
+ type: ToastType;
8
+ message: string | ReactNode;
9
+ dismissible: boolean;
10
+ timeout: number;
11
+ };
12
+
13
+ export type ToastOptions = {
14
+ message: string | ReactNode;
15
+ type?: ToastType;
16
+ dismissible?: boolean;
17
+ timeout?: number;
18
+ };
19
+
20
+ export type ToastPosition =
21
+ | "top-left"
22
+ | "top-right"
23
+ | "bottom-left"
24
+ | "bottom-right";
@@ -0,0 +1,24 @@
1
+ import { ToastOptions } from "./types";
2
+ import { useToastStore } from "./toastAtom";
3
+
4
+ const useToast = () => {
5
+ const { addToast, dismissToast } = useToastStore();
6
+
7
+ return {
8
+ addToast: ({
9
+ message,
10
+ type = "info",
11
+ dismissible = false,
12
+ timeout = 3000,
13
+ }: ToastOptions) =>
14
+ addToast({
15
+ message,
16
+ type,
17
+ dismissible,
18
+ timeout,
19
+ }),
20
+ dismissToast,
21
+ };
22
+ };
23
+
24
+ export default useToast;
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+
3
+ export default function Fallback(props: any) {
4
+ const { component } = props;
5
+ return (
6
+ <div>
7
+ <p>oops {component} failed!!</p>
8
+ <button>Raise an Issue</button>
9
+ </div>
10
+ );
11
+ }
@@ -0,0 +1,55 @@
1
+ import React from "react";
2
+
3
+ type SvgElement = {
4
+ type: "path" | "circle";
5
+ key: string;
6
+ d?: string;
7
+ cx?: number;
8
+ cy?: number;
9
+ r?: number;
10
+ };
11
+
12
+ type IconProps = {
13
+ svgClass?: string;
14
+ elements?: SvgElement[];
15
+ dimensions?: any;
16
+ };
17
+
18
+ export default function Icon({
19
+ svgClass = "stroke-none fill-none",
20
+ elements = [
21
+ { type: "path", d: "m11 17-5-5 5-5", key: "13zhaf" },
22
+ { type: "path", d: "m18 17-5-5 5-5", key: "h8a8et" },
23
+ { type: "circle", cx: 12, cy: 12, r: 5, key: "circle1" },
24
+ ],
25
+ dimensions = { width: "24", height: "24" },
26
+ }: IconProps) {
27
+ return (
28
+ <svg
29
+ xmlns="http://www.w3.org/2000/svg"
30
+ width={dimensions.width}
31
+ height={dimensions.height}
32
+ viewBox="0 0 24 24"
33
+ strokeWidth="2"
34
+ strokeLinecap="round"
35
+ strokeLinejoin="round"
36
+ className={svgClass}
37
+ >
38
+ {elements.map((element) => {
39
+ if (element.type === "path") {
40
+ return <path d={element.d} key={element.key} />;
41
+ } else if (element.type === "circle") {
42
+ return (
43
+ <circle
44
+ cx={element.cx}
45
+ cy={element.cy}
46
+ r={element.r}
47
+ key={element.key}
48
+ />
49
+ );
50
+ }
51
+ return null;
52
+ })}
53
+ </svg>
54
+ );
55
+ }