@rovula/ui 0.0.64 → 0.0.66
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/dist/cjs/bundle.css +12 -0
- package/dist/cjs/bundle.js +1 -1
- package/dist/cjs/bundle.js.map +1 -1
- package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +2 -0
- package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
- package/dist/cjs/types/components/Search/Search.stories.d.ts +1 -0
- package/dist/cjs/types/components/Toast/Toast.stories.d.ts +1 -1
- package/dist/components/Dropdown/Dropdown.js +48 -22
- package/dist/esm/bundle.css +12 -0
- package/dist/esm/bundle.js +1 -1
- package/dist/esm/bundle.js.map +1 -1
- package/dist/esm/types/components/Dropdown/Dropdown.d.ts +2 -0
- package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
- package/dist/esm/types/components/Search/Search.stories.d.ts +1 -0
- package/dist/esm/types/components/Toast/Toast.stories.d.ts +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/src/theme/global.css +16 -0
- package/package.json +1 -1
- package/src/components/Dropdown/Dropdown.tsx +62 -25
|
@@ -24,6 +24,7 @@ export type DropdownProps = {
|
|
|
24
24
|
disabled?: boolean;
|
|
25
25
|
error?: boolean;
|
|
26
26
|
required?: boolean;
|
|
27
|
+
modal?: boolean;
|
|
27
28
|
className?: string;
|
|
28
29
|
optionContainerClassName?: string;
|
|
29
30
|
optionItemClassName?: string;
|
|
@@ -53,6 +54,7 @@ declare const Dropdown: React.ForwardRefExoticComponent<{
|
|
|
53
54
|
disabled?: boolean | undefined;
|
|
54
55
|
error?: boolean | undefined;
|
|
55
56
|
required?: boolean | undefined;
|
|
57
|
+
modal?: boolean | undefined;
|
|
56
58
|
className?: string | undefined;
|
|
57
59
|
optionContainerClassName?: string | undefined;
|
|
58
60
|
optionItemClassName?: string | undefined;
|
|
@@ -15,6 +15,7 @@ declare const meta: {
|
|
|
15
15
|
disabled?: boolean | undefined;
|
|
16
16
|
error?: boolean | undefined;
|
|
17
17
|
required?: boolean | undefined;
|
|
18
|
+
modal?: boolean | undefined;
|
|
18
19
|
className?: string | undefined;
|
|
19
20
|
optionContainerClassName?: string | undefined;
|
|
20
21
|
optionItemClassName?: string | undefined;
|
|
@@ -48,6 +49,7 @@ declare const meta: {
|
|
|
48
49
|
disabled?: boolean | undefined;
|
|
49
50
|
error?: boolean | undefined;
|
|
50
51
|
required?: boolean | undefined;
|
|
52
|
+
modal?: boolean | undefined;
|
|
51
53
|
className?: string | undefined;
|
|
52
54
|
optionContainerClassName?: string | undefined;
|
|
53
55
|
optionItemClassName?: string | undefined;
|
|
@@ -317,6 +317,7 @@ declare const meta: {
|
|
|
317
317
|
renderEndIcon?: (() => React.ReactNode) | undefined;
|
|
318
318
|
onClickStartIcon?: (() => void) | undefined;
|
|
319
319
|
options: Options[];
|
|
320
|
+
modal?: boolean | undefined;
|
|
320
321
|
onChangeText?: React.ChangeEventHandler<HTMLInputElement> | undefined;
|
|
321
322
|
renderOptions?: ((value: {
|
|
322
323
|
optionsFiltered: Options[];
|
|
@@ -15,7 +15,7 @@ declare const meta: {
|
|
|
15
15
|
id?: string | undefined;
|
|
16
16
|
lang?: string | undefined;
|
|
17
17
|
style?: React.CSSProperties | undefined;
|
|
18
|
-
type?: "
|
|
18
|
+
type?: "background" | "foreground" | undefined;
|
|
19
19
|
role?: React.AriaRole | undefined;
|
|
20
20
|
tabIndex?: number | undefined;
|
|
21
21
|
"aria-activedescendant"?: string | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -165,6 +165,7 @@ type DropdownProps = {
|
|
|
165
165
|
disabled?: boolean;
|
|
166
166
|
error?: boolean;
|
|
167
167
|
required?: boolean;
|
|
168
|
+
modal?: boolean;
|
|
168
169
|
className?: string;
|
|
169
170
|
optionContainerClassName?: string;
|
|
170
171
|
optionItemClassName?: string;
|
|
@@ -194,6 +195,7 @@ declare const Dropdown: React__default.ForwardRefExoticComponent<{
|
|
|
194
195
|
disabled?: boolean | undefined;
|
|
195
196
|
error?: boolean | undefined;
|
|
196
197
|
required?: boolean | undefined;
|
|
198
|
+
modal?: boolean | undefined;
|
|
197
199
|
className?: string | undefined;
|
|
198
200
|
optionContainerClassName?: string | undefined;
|
|
199
201
|
optionItemClassName?: string | undefined;
|
|
@@ -2260,6 +2260,10 @@ input[type=number] {
|
|
|
2260
2260
|
bottom: 40px;
|
|
2261
2261
|
}
|
|
2262
2262
|
|
|
2263
|
+
.bottom-full {
|
|
2264
|
+
bottom: 100%;
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2263
2267
|
.left-0 {
|
|
2264
2268
|
left: 0px;
|
|
2265
2269
|
}
|
|
@@ -2312,6 +2316,10 @@ input[type=number] {
|
|
|
2312
2316
|
top: 50%;
|
|
2313
2317
|
}
|
|
2314
2318
|
|
|
2319
|
+
.top-full {
|
|
2320
|
+
top: 100%;
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2315
2323
|
.z-0 {
|
|
2316
2324
|
z-index: 0;
|
|
2317
2325
|
}
|
|
@@ -2328,6 +2336,10 @@ input[type=number] {
|
|
|
2328
2336
|
z-index: 100;
|
|
2329
2337
|
}
|
|
2330
2338
|
|
|
2339
|
+
.z-\[9999\] {
|
|
2340
|
+
z-index: 9999;
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2331
2343
|
.col-span-3 {
|
|
2332
2344
|
grid-column: span 3 / span 3;
|
|
2333
2345
|
}
|
|
@@ -2364,6 +2376,10 @@ input[type=number] {
|
|
|
2364
2376
|
margin-top: -30px;
|
|
2365
2377
|
}
|
|
2366
2378
|
|
|
2379
|
+
.mb-1 {
|
|
2380
|
+
margin-bottom: 0.25rem;
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2367
2383
|
.ml-2 {
|
|
2368
2384
|
margin-left: 0.5rem;
|
|
2369
2385
|
}
|
package/package.json
CHANGED
|
@@ -47,6 +47,7 @@ export type DropdownProps = {
|
|
|
47
47
|
disabled?: boolean;
|
|
48
48
|
error?: boolean;
|
|
49
49
|
required?: boolean;
|
|
50
|
+
modal?: boolean;
|
|
50
51
|
className?: string;
|
|
51
52
|
optionContainerClassName?: string;
|
|
52
53
|
optionItemClassName?: string;
|
|
@@ -81,6 +82,7 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
81
82
|
error = false,
|
|
82
83
|
filterMode = false,
|
|
83
84
|
required = true,
|
|
85
|
+
modal = false,
|
|
84
86
|
onChangeText,
|
|
85
87
|
onSelect,
|
|
86
88
|
renderOptions: customRenderOptions,
|
|
@@ -102,7 +104,9 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
102
104
|
|
|
103
105
|
const dropdownRef = useRef<HTMLUListElement>(null);
|
|
104
106
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
105
|
-
const [dropdownStyles, setDropdownStyles] = useState({});
|
|
107
|
+
const [dropdownStyles, setDropdownStyles] = useState<CSSProperties>({});
|
|
108
|
+
const [isAbove, setIsAbove] = useState(false);
|
|
109
|
+
const [isInsideDialog, setIsInsideDialog] = useState(false);
|
|
106
110
|
|
|
107
111
|
useImperativeHandle(ref, () => inputRef?.current as HTMLInputElement);
|
|
108
112
|
|
|
@@ -111,6 +115,19 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
111
115
|
setTextValue(value?.label ?? "");
|
|
112
116
|
}, [value]);
|
|
113
117
|
|
|
118
|
+
/** ✅ Auto-detect if inside a Dialog */
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
let node: HTMLElement | null = inputRef.current;
|
|
121
|
+
while (node) {
|
|
122
|
+
if (node.getAttribute("role") === "dialog") {
|
|
123
|
+
setIsInsideDialog(true);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
node = node.parentElement;
|
|
127
|
+
}
|
|
128
|
+
setIsInsideDialog(false);
|
|
129
|
+
}, []);
|
|
130
|
+
|
|
114
131
|
const handleOnChangeText = useCallback(
|
|
115
132
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
116
133
|
onChangeText?.(event);
|
|
@@ -128,6 +145,7 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
128
145
|
setSelectedOption(option);
|
|
129
146
|
setTextValue(option.label);
|
|
130
147
|
onSelect?.(option);
|
|
148
|
+
setIsFocused(false);
|
|
131
149
|
},
|
|
132
150
|
[onSelect]
|
|
133
151
|
);
|
|
@@ -140,40 +158,49 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
140
158
|
);
|
|
141
159
|
}, [options, filterMode, textValue]);
|
|
142
160
|
|
|
161
|
+
const usePortal = isInsideDialog ? false : modal;
|
|
162
|
+
|
|
143
163
|
const updateDropdownPosition = useCallback(() => {
|
|
144
|
-
if (inputRef.current) {
|
|
164
|
+
if (inputRef.current && dropdownRef.current) {
|
|
145
165
|
const rect = inputRef.current.getBoundingClientRect();
|
|
146
|
-
const dropdownHeight = dropdownRef.current
|
|
166
|
+
const dropdownHeight = dropdownRef.current.offsetHeight;
|
|
147
167
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
148
168
|
const spaceAbove = rect.top;
|
|
149
169
|
|
|
150
|
-
const
|
|
151
|
-
spaceBelow
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
170
|
+
const shouldOpenAbove =
|
|
171
|
+
spaceBelow < dropdownHeight && spaceAbove > spaceBelow;
|
|
172
|
+
setIsAbove(shouldOpenAbove);
|
|
173
|
+
|
|
174
|
+
if (usePortal) {
|
|
175
|
+
setDropdownStyles({
|
|
176
|
+
position: "absolute",
|
|
177
|
+
top: shouldOpenAbove
|
|
178
|
+
? `${rect.top - dropdownHeight}px`
|
|
179
|
+
: `${rect.bottom}px`,
|
|
180
|
+
left: `${rect.left}px`,
|
|
181
|
+
width: `${rect.width}px`,
|
|
182
|
+
zIndex: 9999,
|
|
183
|
+
});
|
|
184
|
+
} else {
|
|
185
|
+
setDropdownStyles({
|
|
186
|
+
position: "absolute",
|
|
187
|
+
top: shouldOpenAbove ? `-${dropdownHeight}px` : "100%",
|
|
188
|
+
left: "0",
|
|
189
|
+
width: "100%",
|
|
190
|
+
zIndex: 9999,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
164
193
|
}
|
|
165
|
-
}, []);
|
|
194
|
+
}, [modal, isInsideDialog, usePortal]);
|
|
166
195
|
|
|
167
196
|
useEffect(() => {
|
|
168
197
|
if (isFocused) {
|
|
169
198
|
updateDropdownPosition();
|
|
170
199
|
window.addEventListener("resize", updateDropdownPosition);
|
|
171
|
-
window.addEventListener("scroll", updateDropdownPosition, true);
|
|
172
200
|
}
|
|
173
201
|
|
|
174
202
|
return () => {
|
|
175
203
|
window.removeEventListener("resize", updateDropdownPosition);
|
|
176
|
-
window.removeEventListener("scroll", updateDropdownPosition, true);
|
|
177
204
|
};
|
|
178
205
|
}, [isFocused, updateDropdownPosition]);
|
|
179
206
|
|
|
@@ -191,7 +218,8 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
191
218
|
return (
|
|
192
219
|
<ul
|
|
193
220
|
className={cn(
|
|
194
|
-
"absolute mt-1 w-full bg-base-popup border border-base-popup text-base-popup-foreground rounded-md shadow-md z-
|
|
221
|
+
"absolute mt-1 w-full bg-base-popup border border-base-popup text-base-popup-foreground rounded-md shadow-md z-[9999] max-h-60 overflow-y-auto",
|
|
222
|
+
!usePortal && (isAbove ? "bottom-full mb-1" : "top-full mt-1"),
|
|
195
223
|
optionContainerClassName
|
|
196
224
|
)}
|
|
197
225
|
style={dropdownStyles}
|
|
@@ -220,7 +248,9 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
220
248
|
return (
|
|
221
249
|
<li
|
|
222
250
|
key={option.value}
|
|
223
|
-
onMouseDown={() =>
|
|
251
|
+
onMouseDown={() => {
|
|
252
|
+
handleOptionClick(option);
|
|
253
|
+
}}
|
|
224
254
|
className={cn(
|
|
225
255
|
`px-4 py-2 hover:bg-primary-hover-bg cursor-pointer`,
|
|
226
256
|
optionItemClassName,
|
|
@@ -289,7 +319,7 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
289
319
|
|
|
290
320
|
const handleOnBlur = useCallback(
|
|
291
321
|
(e: React.FocusEvent<HTMLInputElement, Element>) => {
|
|
292
|
-
setIsFocused(false);
|
|
322
|
+
setTimeout(() => setIsFocused(false), 200);
|
|
293
323
|
clearMismatchValue(e);
|
|
294
324
|
props?.onBlur?.(e);
|
|
295
325
|
},
|
|
@@ -305,7 +335,7 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
305
335
|
);
|
|
306
336
|
|
|
307
337
|
return (
|
|
308
|
-
<div className={`relative
|
|
338
|
+
<div className={`relative ${fullwidth ? "w-full" : ""}`}>
|
|
309
339
|
<TextInput
|
|
310
340
|
hasClearIcon={false}
|
|
311
341
|
endIcon={
|
|
@@ -339,7 +369,14 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
339
369
|
onBlur={handleOnBlur}
|
|
340
370
|
onKeyDown={handleOnKeyDown}
|
|
341
371
|
/>
|
|
342
|
-
{isFocused &&
|
|
372
|
+
{isFocused &&
|
|
373
|
+
(usePortal ? (
|
|
374
|
+
<Portal.Root container={document.body}>
|
|
375
|
+
{renderOptions()}
|
|
376
|
+
</Portal.Root>
|
|
377
|
+
) : (
|
|
378
|
+
renderOptions()
|
|
379
|
+
))}
|
|
343
380
|
</div>
|
|
344
381
|
);
|
|
345
382
|
}
|