gbs-add-block 1.2.5 → 1.2.7

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 (39) hide show
  1. package/README.md +3 -4
  2. package/index.cjs +1 -0
  3. package/package.json +36 -30
  4. package/source/components/button/index.tsx +41 -24
  5. package/source/components/card/index.tsx +30 -9
  6. package/source/components/checkbox/index.tsx +24 -15
  7. package/source/components/datagrid/context/GridContext.tsx +38 -17
  8. package/source/components/datagrid/createTemplate.tsx +2 -2
  9. package/source/components/datagrid/hooks/usePagination.ts +0 -8
  10. package/source/components/datagrid/hooks/useSearch.ts +3 -3
  11. package/source/components/datagrid/index.tsx +2 -1
  12. package/source/components/datagrid/layout/GridBody.tsx +7 -4
  13. package/source/components/datagrid/layout/GridPagination.tsx +3 -2
  14. package/source/components/datagrid/layout/GridToolbar.tsx +2 -1
  15. package/source/components/datagrid/type.ts +18 -2
  16. package/source/components/datepicker/index.tsx +7 -6
  17. package/source/components/input/index.tsx +141 -143
  18. package/source/components/modal/index.tsx +151 -139
  19. package/source/components/modal/types.ts +18 -16
  20. package/source/components/multiselect/PortalDropDown.tsx +1 -0
  21. package/source/components/multiselect/index.tsx +5 -4
  22. package/source/components/navbar/AuthSection.tsx +5 -5
  23. package/source/components/navbar/NavItemComponent.tsx +2 -2
  24. package/source/components/select/PortalDropDown.tsx +20 -4
  25. package/source/components/select/index.tsx +6 -4
  26. package/source/components/select/types.ts +2 -0
  27. package/source/components/spinner/index.tsx +2 -2
  28. package/source/components/tabs/index.tsx +70 -51
  29. package/source/components/tabs/types.ts +15 -14
  30. package/source/components/textarea/index.tsx +122 -149
  31. package/source/components/toast/ToastComponent.tsx +26 -25
  32. package/source/components/toast/ToastIcon.tsx +8 -8
  33. package/source/components/uploader/ProgressAnimation.tsx +2 -2
  34. package/source/components/uploader/UploadedFilePreview.tsx +1 -1
  35. package/source/components/uploader/index.tsx +1 -1
  36. package/source/components/uploader/uploaderIcon.tsx +1 -1
  37. package/source/globalStyle.ts +74 -35
  38. package/source/icon/iconPaths.ts +6 -0
  39. package/source/theme.ts +40 -0
@@ -1,144 +1,142 @@
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, { useState, useRef } from "react";
9
- import { twMerge } from "tailwind-merge";
10
- import Icon from "../icon/Icon";
11
- import { eye, search } from "../icon/iconPaths";
12
- import type { InputProps } from "./types";
13
- import { iconClass, inputStyles, primary } from "../globalStyle";
14
-
15
- export const Input = ({
16
- OTPField = false,
17
- OTPValue = "",
18
- OTPLength = 4,
19
- OTPClass = inputStyles.otp,
20
- onOTPValueChange,
21
- error = undefined,
22
- ...props
23
- }: InputProps) => {
24
- const [otpValues, setOtpValues] = useState(new Array(OTPLength).fill(""));
25
- const [showPassword, setShowPassword] = useState(false);
26
- const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
27
-
28
- const updateOtpValue = (
29
- index: number,
30
- e: React.ChangeEvent<HTMLInputElement>
31
- ) => {
32
- e.preventDefault();
33
- const input = e.target;
34
- if (input) {
35
- const newOtpValues = [...otpValues];
36
- newOtpValues[index] = input.value;
37
- setOtpValues(newOtpValues);
38
- if (index < OTPLength - 1 && input.value) {
39
- inputRefs.current[index + 1]?.focus();
40
- }
41
- OTPValue = newOtpValues.join("");
42
- if (onOTPValueChange) onOTPValueChange(OTPValue);
43
- }
44
- };
45
-
46
- const handleKeydown = (
47
- index: number,
48
- e: React.KeyboardEvent<HTMLInputElement>
49
- ) => {
50
- if (e.key === "Backspace" && otpValues[index] === "" && index > 0) {
51
- inputRefs.current[index - 1]?.focus();
52
- } else if (e.key === "ArrowLeft" && index > 0) {
53
- e.preventDefault();
54
- inputRefs.current[index - 1]?.focus();
55
- setTimeout(() => {
56
- if (inputRefs.current[index - 1]) {
57
- const inputEl = inputRefs.current[index - 1];
58
- if (inputEl) {
59
- const length = inputEl.value.length;
60
- inputEl.setSelectionRange(length, length);
61
- }
62
- }
63
- }, 0);
64
- } else if (e.key === "ArrowRight" && index < OTPLength - 1) {
65
- e.preventDefault();
66
- inputRefs.current[index + 1]?.focus();
67
- setTimeout(() => {
68
- if (inputRefs.current[index + 1]) {
69
- const inputEl = inputRefs.current[index + 1];
70
- if (inputEl) {
71
- const length = inputEl.value.length;
72
- inputEl.setSelectionRange(length, length);
73
- }
74
- }
75
- }, 0);
76
- }
77
- };
78
-
79
- const toggleTypeForPassword = () => {
80
- setShowPassword(!showPassword);
81
- };
82
-
83
- return (
84
- <div>
85
- {!OTPField ? (
86
- <div className="text-input-container w-full relative flex flex-col">
87
- <div className="relative">
88
- <input
89
- className={twMerge(
90
- inputStyles.default,
91
- error ? inputStyles.error : "",
92
- "w-full text-black pr-10"
93
- )}
94
- {...props}
95
- type={
96
- props.type === "password" && showPassword ? "text" : props.type
97
- }
98
- />
99
- {props.type === "search" && (
100
- <div className="absolute inset-y-0 right-2 flex items-center">
101
- <Icon elements={search} svgClass={iconClass["grey-common"]} />
102
- </div>
103
- )}
104
- {props.type === "password" && (
105
- <button
106
- type="button"
107
- className="absolute inset-y-0 right-2 flex items-center"
108
- onClick={toggleTypeForPassword}
109
- >
110
- <Icon
111
- elements={eye}
112
- svgClass={
113
- error ? iconClass["error-icon"] : iconClass["grey-common"]
114
- }
115
- />
116
- </button>
117
- )}
118
- </div>
119
- {error && <div className={primary["error-primary"]}>{error}</div>}
120
- </div>
121
- ) : (
122
- <div className={inputStyles.otpContainer}>
123
- {Array(OTPLength)
124
- .fill("")
125
- .map((_, index) => (
126
- <input
127
- key={index}
128
- type="text"
129
- maxLength={1}
130
- pattern="[0-9]*"
131
- value={otpValues[index]}
132
- onChange={(e) => updateOtpValue(index, e)}
133
- onKeyDown={(e) => handleKeydown(index, e)}
134
- ref={(ref) => {
135
- inputRefs.current[index] = ref;
136
- }}
137
- className={OTPClass}
138
- />
139
- ))}
140
- </div>
141
- )}
142
- </div>
143
- );
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, { useState, useRef } from "react";
9
+ import { twMerge } from "tailwind-merge";
10
+ import Icon from "../icon/Icon";
11
+ import { eye, search } from "../icon/iconPaths";
12
+ import type { InputProps } from "./types";
13
+ import { iconClass, inputStyles, primary } from "../globalStyle";
14
+
15
+ export const Input = ({
16
+ OTPField = false,
17
+ OTPValue = "",
18
+ OTPLength = 4,
19
+ OTPClass = inputStyles.otp,
20
+ onOTPValueChange,
21
+ error = undefined,
22
+ className,
23
+ ...props
24
+ }: InputProps) => {
25
+ const [otpValues, setOtpValues] = useState(new Array(OTPLength).fill(""));
26
+ const [showPassword, setShowPassword] = useState(false);
27
+ const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
28
+
29
+ const updateOtpValue = (
30
+ index: number,
31
+ e: React.ChangeEvent<HTMLInputElement>
32
+ ) => {
33
+ e.preventDefault();
34
+ const input = e.target;
35
+ if (input) {
36
+ const newOtpValues = [...otpValues];
37
+ newOtpValues[index] = input.value;
38
+ setOtpValues(newOtpValues);
39
+ if (index < OTPLength - 1 && input.value) {
40
+ inputRefs.current[index + 1]?.focus();
41
+ }
42
+ OTPValue = newOtpValues.join("");
43
+ if (onOTPValueChange) onOTPValueChange(OTPValue);
44
+ }
45
+ };
46
+
47
+ const handleKeydown = (
48
+ index: number,
49
+ e: React.KeyboardEvent<HTMLInputElement>
50
+ ) => {
51
+ if (e.key === "Backspace" && otpValues[index] === "" && index > 0) {
52
+ inputRefs.current[index - 1]?.focus();
53
+ } else if (e.key === "ArrowLeft" && index > 0) {
54
+ e.preventDefault();
55
+ inputRefs.current[index - 1]?.focus();
56
+ setTimeout(() => {
57
+ const inputEl = inputRefs.current[index - 1];
58
+ if (inputEl) {
59
+ const length = inputEl.value.length;
60
+ inputEl.setSelectionRange(length, length);
61
+ }
62
+ }, 0);
63
+ } else if (e.key === "ArrowRight" && index < OTPLength - 1) {
64
+ e.preventDefault();
65
+ inputRefs.current[index + 1]?.focus();
66
+ setTimeout(() => {
67
+ const inputEl = inputRefs.current[index + 1];
68
+ if (inputEl) {
69
+ const length = inputEl.value.length;
70
+ inputEl.setSelectionRange(length, length);
71
+ }
72
+ }, 0);
73
+ }
74
+ };
75
+
76
+ const toggleTypeForPassword = () => {
77
+ setShowPassword(!showPassword);
78
+ };
79
+
80
+ return (
81
+ <div>
82
+ {!OTPField ? (
83
+ <div className="text-input-container relative flex w-full flex-col">
84
+ <div className="relative">
85
+ <input
86
+ className={twMerge(
87
+ inputStyles.default,
88
+ error ? inputStyles.error : "",
89
+ "w-full pr-10",
90
+ className
91
+ )}
92
+ {...props}
93
+ type={
94
+ props.type === "password" && showPassword ? "text" : props.type
95
+ }
96
+ />
97
+ {props.type === "search" && (
98
+ <div className="absolute inset-y-0 right-2 flex items-center">
99
+ <Icon elements={search} svgClass={iconClass["grey-common"]} />
100
+ </div>
101
+ )}
102
+ {props.type === "password" && (
103
+ <button
104
+ type="button"
105
+ className="absolute inset-y-0 right-2 flex items-center rounded-md p-1"
106
+ onClick={toggleTypeForPassword}
107
+ >
108
+ <Icon
109
+ elements={eye}
110
+ svgClass={
111
+ error ? iconClass["error-icon"] : iconClass["grey-common"]
112
+ }
113
+ />
114
+ </button>
115
+ )}
116
+ </div>
117
+ {error && <div className={primary["error-primary"]}>{error}</div>}
118
+ </div>
119
+ ) : (
120
+ <div className={inputStyles.otpContainer}>
121
+ {Array(OTPLength)
122
+ .fill("")
123
+ .map((_, index) => (
124
+ <input
125
+ key={index}
126
+ type="text"
127
+ maxLength={1}
128
+ pattern="[0-9]*"
129
+ value={otpValues[index]}
130
+ onChange={(e) => updateOtpValue(index, e)}
131
+ onKeyDown={(e) => handleKeydown(index, e)}
132
+ ref={(ref) => {
133
+ inputRefs.current[index] = ref;
134
+ }}
135
+ className={OTPClass}
136
+ />
137
+ ))}
138
+ </div>
139
+ )}
140
+ </div>
141
+ );
144
142
  };
@@ -1,140 +1,152 @@
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, { useEffect, useCallback, memo, useRef } from "react";
9
- import { twMerge } from "tailwind-merge";
10
- import Icon from "../icon/Icon";
11
- import { x } from "../icon/iconPaths";
12
- import type { ModalProps } from "./types";
13
-
14
- const defaultClasses = {
15
- modal:
16
- "fixed z-50 overflow-y-auto inset-0 flex items-center justify-center bg-[#2b2426bd]",
17
- modalContent: "bg-white m-10 md:w-[80vh] rounded-xl relative",
18
- modalTitle:
19
- "p-4 text-lg leading-6 font-medium text-gray-900 flex justify-between items-center",
20
- closeButton:
21
- "absolute top-2 right-2 p-2 hover:bg-gray-100 rounded-full transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-gray-300",
22
- closeIcon: "h-5 w-5 stroke-gray-500 fill-none",
23
- };
24
-
25
- export const Modal = memo(
26
- ({
27
- showModal = false,
28
- setShowModal,
29
- modalTitle = "Modal Title",
30
- modalClass = defaultClasses.modal,
31
- modalContentClass = defaultClasses.modalContent,
32
- classModalContent = "",
33
- modalTitleClass = defaultClasses.modalTitle,
34
- classModalTitle = "",
35
- children,
36
- showCloseButton = false,
37
- dismissible = false,
38
- titleId = "modal-title",
39
- closeButtonContent,
40
- animationDuration = 200,
41
- showTitle = true,
42
- }: ModalProps) => {
43
- const modalRef = useRef<HTMLDivElement>(null);
44
- const previousActiveElement = useRef<HTMLElement | null>(null);
45
-
46
- // Handle ESC key press to close modal
47
- const handleEscKey = useCallback(
48
- (event: KeyboardEvent) => {
49
- if (event.key === "Escape" && showModal && dismissible) {
50
- setShowModal(false);
51
- }
52
- },
53
- [showModal, dismissible, setShowModal]
54
- );
55
-
56
- // Handle click outside modal to close
57
- const handleOutsideClick = useCallback(
58
- (e: React.MouseEvent<HTMLDivElement>) => {
59
- if (e.target === e.currentTarget && dismissible) {
60
- setShowModal(false);
61
- }
62
- },
63
- [dismissible, setShowModal]
64
- );
65
-
66
- // Handle close button click
67
- const handleClose = useCallback(() => {
68
- setShowModal(false);
69
- }, [setShowModal]);
70
-
71
- // Manage focus trap and keyboard events
72
- useEffect(() => {
73
- if (showModal) {
74
- previousActiveElement.current = document.activeElement as HTMLElement;
75
- document.addEventListener("keydown", handleEscKey);
76
- document.body.style.overflow = "hidden";
77
- modalRef.current?.focus();
78
- } else {
79
- document.removeEventListener("keydown", handleEscKey);
80
- document.body.style.overflow = "";
81
- previousActiveElement.current?.focus();
82
- }
83
-
84
- return () => {
85
- document.removeEventListener("keydown", handleEscKey);
86
- document.body.style.overflow = "";
87
- };
88
- }, [showModal, handleEscKey]);
89
-
90
- // Animation styles
91
- const modalStyles: React.CSSProperties = {
92
- transition: `opacity ${animationDuration}ms ease-in-out`,
93
- opacity: showModal ? 1 : 0,
94
- visibility: showModal ? "visible" : "hidden",
95
- };
96
-
97
- if (!showModal) {
98
- return null;
99
- }
100
-
101
- return (
102
- <div
103
- className={modalClass}
104
- aria-labelledby={titleId}
105
- role="dialog"
106
- aria-modal="true"
107
- onClick={handleOutsideClick}
108
- style={modalStyles}
109
- >
110
- <div
111
- ref={modalRef}
112
- className={twMerge(modalContentClass, classModalContent)}
113
- onClick={(e: React.MouseEvent) => e.stopPropagation()}
114
- role="document"
115
- tabIndex={-1}
116
- >
117
- <div className={twMerge(modalTitleClass, classModalTitle)}>
118
- {showTitle && <h2 id={titleId}>{modalTitle}</h2>}
119
- {showCloseButton && (
120
- <button
121
- onClick={handleClose}
122
- className={defaultClasses.closeButton}
123
- aria-label="Close modal"
124
- type="button"
125
- >
126
- {closeButtonContent || (
127
- <Icon elements={x} svgClass={defaultClasses.closeIcon} />
128
- )}
129
- </button>
130
- )}
131
- </div>
132
- {showTitle && <hr className="border-gray-200" />}
133
- <div className="p-4">{children}</div>
134
- </div>
135
- </div>
136
- );
137
- }
138
- );
139
-
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, { useEffect, useCallback, memo, useRef } from "react";
9
+ import { twMerge } from "tailwind-merge";
10
+ import Icon from "../icon/Icon";
11
+ import { x } from "../icon/iconPaths";
12
+ import type { ModalProps } from "./types";
13
+
14
+ const defaultClasses = {
15
+ modal:
16
+ "fixed inset-0 z-50 flex items-center justify-center overflow-y-auto bg-slate-950/55 p-4 backdrop-blur-sm",
17
+ modalContent:
18
+ "relative max-h-[90vh] overflow-hidden rounded-lg border border-slate-200 bg-white shadow-xl shadow-slate-950/20 dark:border-slate-700 dark:bg-slate-950",
19
+ modalTitle:
20
+ "flex items-center justify-between gap-4 px-5 py-4 text-base font-semibold text-slate-950 dark:text-slate-50",
21
+ closeButton:
22
+ "absolute right-3 top-3 rounded-md p-2 text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500 dark:hover:bg-slate-800 dark:hover:text-white",
23
+ closeIcon: "h-5 w-5 stroke-current fill-none",
24
+ };
25
+
26
+ const sizeClasses = {
27
+ sm: "w-full max-w-sm",
28
+ md: "w-full max-w-lg",
29
+ lg: "w-full max-w-2xl",
30
+ xl: "w-full max-w-4xl",
31
+ };
32
+
33
+ export const Modal = memo(
34
+ ({
35
+ showModal = false,
36
+ setShowModal,
37
+ modalTitle = "Modal Title",
38
+ modalClass = defaultClasses.modal,
39
+ modalContentClass = defaultClasses.modalContent,
40
+ classModalContent = "",
41
+ modalTitleClass = defaultClasses.modalTitle,
42
+ classModalTitle = "",
43
+ className = "",
44
+ size = "md",
45
+ children,
46
+ showCloseButton = false,
47
+ dismissible = false,
48
+ titleId = "modal-title",
49
+ closeButtonContent,
50
+ animationDuration = 200,
51
+ showTitle = true,
52
+ }: ModalProps) => {
53
+ const modalRef = useRef<HTMLDivElement>(null);
54
+ const previousActiveElement = useRef<HTMLElement | null>(null);
55
+
56
+ const handleEscKey = useCallback(
57
+ (event: KeyboardEvent) => {
58
+ if (event.key === "Escape" && showModal && dismissible) {
59
+ setShowModal(false);
60
+ }
61
+ },
62
+ [showModal, dismissible, setShowModal]
63
+ );
64
+
65
+ const handleOutsideClick = useCallback(
66
+ (e: React.MouseEvent<HTMLDivElement>) => {
67
+ if (e.target === e.currentTarget && dismissible) {
68
+ setShowModal(false);
69
+ }
70
+ },
71
+ [dismissible, setShowModal]
72
+ );
73
+
74
+ const handleClose = useCallback(() => {
75
+ setShowModal(false);
76
+ }, [setShowModal]);
77
+
78
+ useEffect(() => {
79
+ if (showModal) {
80
+ previousActiveElement.current = document.activeElement as HTMLElement;
81
+ document.addEventListener("keydown", handleEscKey);
82
+ document.body.style.overflow = "hidden";
83
+ modalRef.current?.focus();
84
+ } else {
85
+ document.removeEventListener("keydown", handleEscKey);
86
+ document.body.style.overflow = "";
87
+ previousActiveElement.current?.focus();
88
+ }
89
+
90
+ return () => {
91
+ document.removeEventListener("keydown", handleEscKey);
92
+ document.body.style.overflow = "";
93
+ };
94
+ }, [showModal, handleEscKey]);
95
+
96
+ const modalStyles: React.CSSProperties = {
97
+ transition: `opacity ${animationDuration}ms ease-in-out`,
98
+ opacity: showModal ? 1 : 0,
99
+ visibility: showModal ? "visible" : "hidden",
100
+ };
101
+
102
+ if (!showModal) {
103
+ return null;
104
+ }
105
+
106
+ return (
107
+ <div
108
+ className={modalClass}
109
+ aria-labelledby={titleId}
110
+ role="dialog"
111
+ aria-modal="true"
112
+ onClick={handleOutsideClick}
113
+ style={modalStyles}
114
+ >
115
+ <div
116
+ ref={modalRef}
117
+ className={twMerge(
118
+ modalContentClass,
119
+ sizeClasses[size],
120
+ className,
121
+ classModalContent
122
+ )}
123
+ onClick={(e: React.MouseEvent) => e.stopPropagation()}
124
+ role="document"
125
+ tabIndex={-1}
126
+ >
127
+ <div className={twMerge(modalTitleClass, classModalTitle)}>
128
+ {showTitle && <h2 id={titleId}>{modalTitle}</h2>}
129
+ {showCloseButton && (
130
+ <button
131
+ onClick={handleClose}
132
+ className={defaultClasses.closeButton}
133
+ aria-label="Close modal"
134
+ type="button"
135
+ >
136
+ {closeButtonContent || (
137
+ <Icon elements={x} svgClass={defaultClasses.closeIcon} />
138
+ )}
139
+ </button>
140
+ )}
141
+ </div>
142
+ {showTitle && <hr className="border-slate-200 dark:border-slate-700" />}
143
+ <div className="max-h-[calc(90vh-73px)] overflow-y-auto p-5 text-sm text-slate-700 dark:text-slate-200">
144
+ {children}
145
+ </div>
146
+ </div>
147
+ </div>
148
+ );
149
+ }
150
+ );
151
+
140
152
  Modal.displayName = "Modal";
@@ -1,17 +1,19 @@
1
- export type ModalProps = {
2
- showModal?: boolean;
3
- setShowModal: (show: boolean) => void;
4
- modalTitle?: string;
5
- modalClass?: string;
6
- modalContentClass?: string;
7
- classModalContent?: string;
8
- modalTitleClass?: string;
9
- classModalTitle?: string;
10
- children: React.ReactNode;
11
- showCloseButton?: boolean;
12
- dismissible?: boolean;
13
- titleId?: string;
14
- closeButtonContent?: React.ReactNode;
15
- animationDuration?: number;
16
- showTitle?: boolean;
1
+ export type ModalProps = {
2
+ showModal?: boolean;
3
+ setShowModal: (show: boolean) => void;
4
+ modalTitle?: string;
5
+ modalClass?: string;
6
+ modalContentClass?: string;
7
+ classModalContent?: string;
8
+ modalTitleClass?: string;
9
+ classModalTitle?: string;
10
+ className?: string;
11
+ size?: "sm" | "md" | "lg" | "xl";
12
+ children: React.ReactNode;
13
+ showCloseButton?: boolean;
14
+ dismissible?: boolean;
15
+ titleId?: string;
16
+ closeButtonContent?: React.ReactNode;
17
+ animationDuration?: number;
18
+ showTitle?: boolean;
17
19
  };
@@ -41,3 +41,4 @@ export const PortalDropdown = ({ targetRef, children, isVisible }: any) => {
41
41
  document.body
42
42
  );
43
43
  };
44
+