gbs-add-block 0.0.30 → 0.0.31
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 +3 -1
- package/index.js +1 -0
- package/package.json +1 -1
- package/source/components/formrenderer/index.tsx +9 -4
- package/source/components/formrenderer/types.ts +2 -0
- package/source/components/materialInput/index.tsx +92 -0
- package/source/components/materialInput/types.ts +11 -0
- package/source/components/modal/index.tsx +50 -6
- package/source/components/modal/types.ts +3 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GBS Building Blocks 2.0 (v0.0.
|
|
1
|
+
# GBS Building Blocks 2.0 (v0.0.31)
|
|
2
2
|
|
|
3
3
|
Latest and upgraded version of GBS building blocks with headless UI and removed dependencies.
|
|
4
4
|
|
|
@@ -9,6 +9,8 @@ For detailed documentation on usage and props, Please visit: [Building Block Doc
|
|
|
9
9
|
## What's New 🎉 (Ver 0.0.23)
|
|
10
10
|
|
|
11
11
|
- Updated Grid
|
|
12
|
+
- Updated Modal
|
|
13
|
+
- New Components (Material Input (Material Design Inspired Input), Form Renderer(Dynamic Form Rendering))
|
|
12
14
|
|
|
13
15
|
## Authors
|
|
14
16
|
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -10,7 +10,12 @@ import InputHandles from "./InputHandles";
|
|
|
10
10
|
import SelectHandles from "./SelectHandles";
|
|
11
11
|
import { FormItem, FormRendererProps } from "./types";
|
|
12
12
|
|
|
13
|
-
const FormRenderer = ({
|
|
13
|
+
const FormRenderer = ({
|
|
14
|
+
onSubmit,
|
|
15
|
+
sourceData,
|
|
16
|
+
formFormationClass = "grid grid-cols-1 text-left gap-4",
|
|
17
|
+
formParentClass = "w-96",
|
|
18
|
+
}: FormRendererProps) => {
|
|
14
19
|
const formRef = useRef<HTMLFormElement>(null);
|
|
15
20
|
const [requirementError, setRequirementError] = useState<string[]>([]);
|
|
16
21
|
|
|
@@ -40,9 +45,9 @@ const FormRenderer = ({ onSubmit, sourceData }: FormRendererProps) => {
|
|
|
40
45
|
};
|
|
41
46
|
|
|
42
47
|
return (
|
|
43
|
-
<form ref={formRef} onSubmit={handleSubmit} className=
|
|
48
|
+
<form ref={formRef} onSubmit={handleSubmit} className={formParentClass}>
|
|
44
49
|
{sourceData && sourceData?.length > 0 ? (
|
|
45
|
-
<div className=
|
|
50
|
+
<div className={formFormationClass}>
|
|
46
51
|
{sourceData.map((item: FormItem, index: number) => {
|
|
47
52
|
switch (item.component) {
|
|
48
53
|
case "input":
|
|
@@ -68,7 +73,7 @@ const FormRenderer = ({ onSubmit, sourceData }: FormRendererProps) => {
|
|
|
68
73
|
|
|
69
74
|
case "button":
|
|
70
75
|
return (
|
|
71
|
-
<div key={index} className="w-full mt-
|
|
76
|
+
<div key={index} className="w-full mt-1">
|
|
72
77
|
<button
|
|
73
78
|
type={item.button_type}
|
|
74
79
|
className="w-full bg-black text-white py-2 rounded-xl hover:bg-gray-800 dark:hover:bg-gray-300 dark:bg-white dark:text-black"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React, { useState, useRef } from "react";
|
|
2
|
+
import { MaterialInputProps } from "./types";
|
|
3
|
+
|
|
4
|
+
const MaterialInput = ({
|
|
5
|
+
label,
|
|
6
|
+
type = "text",
|
|
7
|
+
value,
|
|
8
|
+
onChange,
|
|
9
|
+
error,
|
|
10
|
+
helperText,
|
|
11
|
+
required = false,
|
|
12
|
+
disabled = false,
|
|
13
|
+
className = "",
|
|
14
|
+
}: MaterialInputProps) => {
|
|
15
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
16
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
17
|
+
|
|
18
|
+
const handleFocus = () => setIsFocused(true);
|
|
19
|
+
const handleBlur = () => {
|
|
20
|
+
if (!value) {
|
|
21
|
+
setIsFocused(false);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const isFloating = isFocused || value;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className={`relative w-full ${className}`}>
|
|
29
|
+
<div className="relative">
|
|
30
|
+
<input
|
|
31
|
+
ref={inputRef}
|
|
32
|
+
type={type}
|
|
33
|
+
value={value}
|
|
34
|
+
onChange={(e) => onChange(e.target.value)}
|
|
35
|
+
onFocus={handleFocus}
|
|
36
|
+
onBlur={handleBlur}
|
|
37
|
+
disabled={disabled}
|
|
38
|
+
required={required}
|
|
39
|
+
className={`
|
|
40
|
+
peer w-full px-3 py-3
|
|
41
|
+
bg-transparent
|
|
42
|
+
border rounded-md
|
|
43
|
+
outline-none
|
|
44
|
+
transition-all duration-200 ease-in-out
|
|
45
|
+
placeholder-transparent
|
|
46
|
+
${
|
|
47
|
+
error
|
|
48
|
+
? "border-red-500 focus:border-red-500"
|
|
49
|
+
: "border-gray-300 focus:border-blue-500"
|
|
50
|
+
}
|
|
51
|
+
${disabled ? "text-gray-400 cursor-not-allowed" : "text-gray-900"}
|
|
52
|
+
`}
|
|
53
|
+
/>
|
|
54
|
+
<label
|
|
55
|
+
onClick={() => inputRef.current?.focus()}
|
|
56
|
+
className={`
|
|
57
|
+
absolute left-3
|
|
58
|
+
pointer-events-none
|
|
59
|
+
transition-all duration-200 ease-in-out
|
|
60
|
+
cursor-text
|
|
61
|
+
${isFloating ? "-top-2 text-xs bg-white px-1" : "top-3 text-base"}
|
|
62
|
+
${
|
|
63
|
+
error
|
|
64
|
+
? "text-red-500"
|
|
65
|
+
: isFocused
|
|
66
|
+
? "text-blue-500"
|
|
67
|
+
: "text-gray-500"
|
|
68
|
+
}
|
|
69
|
+
${disabled ? "text-gray-400 cursor-not-allowed" : ""}
|
|
70
|
+
`}
|
|
71
|
+
>
|
|
72
|
+
{label}
|
|
73
|
+
{required && " *"}
|
|
74
|
+
</label>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
{/* Helper text or error message */}
|
|
78
|
+
{(helperText || error) && (
|
|
79
|
+
<div
|
|
80
|
+
className={`
|
|
81
|
+
mt-1 text-xs
|
|
82
|
+
${error ? "text-red-500" : "text-gray-500"}
|
|
83
|
+
`}
|
|
84
|
+
>
|
|
85
|
+
{error || helperText}
|
|
86
|
+
</div>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default MaterialInput;
|
|
@@ -5,20 +5,49 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React from "react";
|
|
8
|
+
import React, { useEffect } from "react";
|
|
9
9
|
import { twMerge } from "tailwind-merge";
|
|
10
10
|
import type { ModalProps } from "./types";
|
|
11
|
+
import Icon from "../icon/Icon";
|
|
12
|
+
import { x } from "../icon/iconPaths";
|
|
11
13
|
|
|
12
14
|
export const Modal = ({
|
|
13
15
|
showModal = false,
|
|
14
16
|
modalTitle = "Modal Title",
|
|
15
17
|
modalClass = "fixed z-10 overflow-y-auto inset-0 flex items-center justify-center bg-gray-500 bg-opacity-75 transition-opacity",
|
|
16
|
-
modalContentClass = "bg-white m-10 md:w-[80vh] rounded-xl",
|
|
18
|
+
modalContentClass = "bg-white m-10 md:w-[80vh] rounded-xl relative",
|
|
17
19
|
classModalContent = "",
|
|
18
|
-
modalTitleClass = "p-4 text-lg leading-6 font-medium text-gray-900 flex justify-between",
|
|
20
|
+
modalTitleClass = "p-4 text-lg leading-6 font-medium text-gray-900 flex justify-between items-center",
|
|
19
21
|
classModalTitle = "",
|
|
20
22
|
children,
|
|
23
|
+
showCloseButton = false,
|
|
24
|
+
onClose,
|
|
25
|
+
dissmissible = false,
|
|
21
26
|
}: ModalProps) => {
|
|
27
|
+
// Handle ESC key press to close modal
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const handleEscKey = (event: KeyboardEvent) => {
|
|
30
|
+
if (event.key === "Escape" && showModal && onClose) {
|
|
31
|
+
onClose();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
if (showModal) {
|
|
36
|
+
document.addEventListener("keydown", handleEscKey);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return () => {
|
|
40
|
+
document.removeEventListener("keydown", handleEscKey);
|
|
41
|
+
};
|
|
42
|
+
}, [showModal, onClose]);
|
|
43
|
+
|
|
44
|
+
// Handle click outside modal to close
|
|
45
|
+
const handleOutsideClick = (e: React.MouseEvent) => {
|
|
46
|
+
if (onClose && e.target === e.currentTarget && dissmissible) {
|
|
47
|
+
onClose();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
22
51
|
return (
|
|
23
52
|
<>
|
|
24
53
|
{showModal && (
|
|
@@ -27,18 +56,33 @@ export const Modal = ({
|
|
|
27
56
|
aria-labelledby="modal-title"
|
|
28
57
|
role="dialog"
|
|
29
58
|
aria-modal="true"
|
|
59
|
+
onClick={handleOutsideClick}
|
|
30
60
|
>
|
|
31
61
|
<div
|
|
32
62
|
className={twMerge(modalContentClass, classModalContent)}
|
|
33
|
-
onClick={(e:
|
|
63
|
+
onClick={(e: React.MouseEvent) => {
|
|
34
64
|
e.stopPropagation();
|
|
35
65
|
}}
|
|
36
66
|
role="dialog"
|
|
37
67
|
>
|
|
38
68
|
<div className={twMerge(modalTitleClass, classModalTitle)}>
|
|
39
|
-
{modalTitle}
|
|
69
|
+
<span>{modalTitle}</span>
|
|
70
|
+
{showCloseButton && onClose && (
|
|
71
|
+
<button
|
|
72
|
+
onClick={onClose}
|
|
73
|
+
className="p-2 hover:bg-gray-100 rounded-full transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-gray-300"
|
|
74
|
+
aria-label="Close modal"
|
|
75
|
+
>
|
|
76
|
+
<Icon
|
|
77
|
+
elements={x}
|
|
78
|
+
svgClass={
|
|
79
|
+
"h-5 w-5 stroke-gray-500 fill-none dark:stroke-white"
|
|
80
|
+
}
|
|
81
|
+
/>
|
|
82
|
+
</button>
|
|
83
|
+
)}
|
|
40
84
|
</div>
|
|
41
|
-
<hr />
|
|
85
|
+
<hr className="border-gray-200" />
|
|
42
86
|
<div className="p-4">{children}</div>
|
|
43
87
|
</div>
|
|
44
88
|
</div>
|