gbs-add-block 1.0.6 → 1.0.8
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 -2
- package/package.json +1 -1
- package/source/components/select/index.tsx +100 -47
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GBS Building Blocks 2.0 (v1.0.
|
|
1
|
+
# GBS Building Blocks 2.0 (v1.0.8)
|
|
2
2
|
|
|
3
3
|
Latest and upgraded version of GBS building blocks with headless UI and removed dependencies.
|
|
4
4
|
|
|
@@ -6,9 +6,10 @@ 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 1.0.
|
|
9
|
+
## What's New 🎉 (Ver 1.0.8)
|
|
10
10
|
|
|
11
11
|
- Uploader Enhancement
|
|
12
|
+
- Select Bug Fix
|
|
12
13
|
|
|
13
14
|
## Authors
|
|
14
15
|
|
package/package.json
CHANGED
|
@@ -12,7 +12,9 @@ import React, {
|
|
|
12
12
|
useEffect,
|
|
13
13
|
useImperativeHandle,
|
|
14
14
|
useRef,
|
|
15
|
+
useState,
|
|
15
16
|
} from "react";
|
|
17
|
+
import { createPortal } from "react-dom";
|
|
16
18
|
import Icon from "../icon/Icon";
|
|
17
19
|
import { check, search, upDown, x } from "../icon/iconPaths";
|
|
18
20
|
import type { ItemsProps, SelectHandle, SelectProps } from "./types";
|
|
@@ -26,6 +28,46 @@ import {
|
|
|
26
28
|
useKeyboardNavigation,
|
|
27
29
|
} from "@grampro/headless-helpers";
|
|
28
30
|
|
|
31
|
+
// Portal Dropdown Component
|
|
32
|
+
const PortalDropdown = ({ targetRef, children, isVisible }: any) => {
|
|
33
|
+
const [position, setPosition] = useState({ top: 0, left: 0, width: 0 });
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (isVisible && targetRef.current) {
|
|
37
|
+
const rect = targetRef.current.getBoundingClientRect();
|
|
38
|
+
const scrollTop =
|
|
39
|
+
window.pageYOffset || document.documentElement.scrollTop;
|
|
40
|
+
const scrollLeft =
|
|
41
|
+
window.pageXOffset || document.documentElement.scrollLeft;
|
|
42
|
+
|
|
43
|
+
setPosition({
|
|
44
|
+
top: rect.bottom + scrollTop + 4,
|
|
45
|
+
left: rect.left + scrollLeft,
|
|
46
|
+
width: rect.width, // Match the width of the select button
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}, [isVisible, targetRef]);
|
|
50
|
+
|
|
51
|
+
if (!isVisible) return null;
|
|
52
|
+
|
|
53
|
+
return createPortal(
|
|
54
|
+
<div
|
|
55
|
+
style={{
|
|
56
|
+
position: "absolute",
|
|
57
|
+
top: position.top,
|
|
58
|
+
left: position.left,
|
|
59
|
+
width: position.width,
|
|
60
|
+
zIndex: 1000,
|
|
61
|
+
}}
|
|
62
|
+
className={popUp["pop-up-style"]}
|
|
63
|
+
data-select-portal="true"
|
|
64
|
+
>
|
|
65
|
+
{children}
|
|
66
|
+
</div>,
|
|
67
|
+
document.body
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
29
71
|
const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
30
72
|
const {
|
|
31
73
|
id = "",
|
|
@@ -67,6 +109,7 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
|
67
109
|
|
|
68
110
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
69
111
|
const selectRef = useRef<HTMLDivElement>(null);
|
|
112
|
+
const buttonRef = useRef<HTMLButtonElement>(null); // Reference for portal positioning
|
|
70
113
|
|
|
71
114
|
useClickOutside(selectRef as React.RefObject<HTMLElement>, () =>
|
|
72
115
|
setShowPopover(false)
|
|
@@ -120,6 +163,55 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
|
120
163
|
selected: selectedItem,
|
|
121
164
|
}));
|
|
122
165
|
|
|
166
|
+
// Portal dropdown content
|
|
167
|
+
const dropdownContent = (
|
|
168
|
+
<>
|
|
169
|
+
{showSearch && (
|
|
170
|
+
<div className={selectStyle["input-parent"]}>
|
|
171
|
+
<Icon elements={search} svgClass={iconClass["grey-common"]} />
|
|
172
|
+
<input
|
|
173
|
+
autoComplete="off"
|
|
174
|
+
type="text"
|
|
175
|
+
name="search"
|
|
176
|
+
id="search"
|
|
177
|
+
placeholder="Search a value"
|
|
178
|
+
className="w-full outline-none dark:bg-transparent"
|
|
179
|
+
ref={inputRef}
|
|
180
|
+
value={searchTerm}
|
|
181
|
+
onChange={(e) => {
|
|
182
|
+
setSearchTerm(e.target.value);
|
|
183
|
+
if (onFiltering) onFiltering(e.target.value);
|
|
184
|
+
setFocusedIndex(-1);
|
|
185
|
+
}}
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
)}
|
|
189
|
+
{filteredItems.length > 0 ? (
|
|
190
|
+
filteredItems.map(({ value, label }, index: number) => (
|
|
191
|
+
<button
|
|
192
|
+
key={value}
|
|
193
|
+
ref={(el: any) => (itemRefs.current[index] = el)}
|
|
194
|
+
className={`${selectStyle["filter-button"]} ${
|
|
195
|
+
focusedIndex === index ? "bg-gray-100 dark:bg-gray-700" : ""
|
|
196
|
+
}`}
|
|
197
|
+
onClick={() => handleSelect({ value, label })}
|
|
198
|
+
onMouseEnter={() => setFocusedIndex(index)}
|
|
199
|
+
>
|
|
200
|
+
<Icon
|
|
201
|
+
elements={check}
|
|
202
|
+
svgClass={`h-4 w-4 fill-none ${
|
|
203
|
+
selectedItem === value ? "stroke-gray-500" : ""
|
|
204
|
+
}`}
|
|
205
|
+
/>
|
|
206
|
+
{label}
|
|
207
|
+
</button>
|
|
208
|
+
))
|
|
209
|
+
) : (
|
|
210
|
+
<div className="text-sm text-center p-2">No Data Found</div>
|
|
211
|
+
)}
|
|
212
|
+
</>
|
|
213
|
+
);
|
|
214
|
+
|
|
123
215
|
return (
|
|
124
216
|
<div
|
|
125
217
|
className="relative w-full mt-2"
|
|
@@ -129,6 +221,7 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
|
129
221
|
>
|
|
130
222
|
<div className="w-full relative">
|
|
131
223
|
<button
|
|
224
|
+
ref={buttonRef} // Add ref for portal positioning
|
|
132
225
|
className={`${error ? primary["error-border"] : "border"} ${
|
|
133
226
|
selectStyle["select-button"]
|
|
134
227
|
} ${disabled ? "opacity-50 cursor-not-allowed" : ""}`}
|
|
@@ -163,53 +256,13 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
|
163
256
|
disabled={disabled}
|
|
164
257
|
/>
|
|
165
258
|
|
|
166
|
-
{
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
type="text"
|
|
174
|
-
name="search"
|
|
175
|
-
id="search"
|
|
176
|
-
placeholder="Search a value"
|
|
177
|
-
className="w-full outline-none"
|
|
178
|
-
ref={inputRef}
|
|
179
|
-
value={searchTerm}
|
|
180
|
-
onChange={(e) => {
|
|
181
|
-
setSearchTerm(e.target.value);
|
|
182
|
-
if (onFiltering) onFiltering(e.target.value);
|
|
183
|
-
setFocusedIndex(-1);
|
|
184
|
-
}}
|
|
185
|
-
/>
|
|
186
|
-
</div>
|
|
187
|
-
)}
|
|
188
|
-
{filteredItems.length > 0 ? (
|
|
189
|
-
filteredItems.map(({ value, label }, index: number) => (
|
|
190
|
-
<button
|
|
191
|
-
key={value}
|
|
192
|
-
ref={(el: any) => (itemRefs.current[index] = el)}
|
|
193
|
-
className={`${selectStyle["filter-button"]} ${
|
|
194
|
-
focusedIndex === index ? "bg-gray-100" : ""
|
|
195
|
-
}`}
|
|
196
|
-
onClick={() => handleSelect({ value, label })}
|
|
197
|
-
onMouseEnter={() => setFocusedIndex(index)}
|
|
198
|
-
>
|
|
199
|
-
<Icon
|
|
200
|
-
elements={check}
|
|
201
|
-
svgClass={`h-4 w-4 fill-none ${
|
|
202
|
-
selectedItem === value ? "stroke-gray-500" : ""
|
|
203
|
-
}`}
|
|
204
|
-
/>
|
|
205
|
-
{label}
|
|
206
|
-
</button>
|
|
207
|
-
))
|
|
208
|
-
) : (
|
|
209
|
-
<div className="text-sm text-center">No Data Found</div>
|
|
210
|
-
)}
|
|
211
|
-
</div>
|
|
212
|
-
)}
|
|
259
|
+
{/* Portal Dropdown - renders to document.body */}
|
|
260
|
+
<PortalDropdown
|
|
261
|
+
targetRef={buttonRef}
|
|
262
|
+
isVisible={showPopover && !disabled}
|
|
263
|
+
>
|
|
264
|
+
{dropdownContent}
|
|
265
|
+
</PortalDropdown>
|
|
213
266
|
</div>
|
|
214
267
|
);
|
|
215
268
|
});
|