@rovula/ui 0.0.64 → 0.0.65
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 -20
- 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 +61 -22
|
@@ -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 = true,
|
|
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
|
);
|
|
@@ -141,28 +159,39 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
141
159
|
}, [options, filterMode, textValue]);
|
|
142
160
|
|
|
143
161
|
const updateDropdownPosition = useCallback(() => {
|
|
144
|
-
if (inputRef.current) {
|
|
162
|
+
if (inputRef.current && dropdownRef.current) {
|
|
145
163
|
const rect = inputRef.current.getBoundingClientRect();
|
|
146
|
-
const dropdownHeight = dropdownRef.current
|
|
164
|
+
const dropdownHeight = dropdownRef.current.offsetHeight;
|
|
147
165
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
148
166
|
const spaceAbove = rect.top;
|
|
149
167
|
|
|
150
|
-
const
|
|
151
|
-
spaceBelow
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
168
|
+
const shouldOpenAbove =
|
|
169
|
+
spaceBelow < dropdownHeight && spaceAbove > spaceBelow;
|
|
170
|
+
setIsAbove(shouldOpenAbove);
|
|
171
|
+
|
|
172
|
+
const usePortal = isInsideDialog ? false : modal;
|
|
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, // Ensure it's above everything
|
|
183
|
+
});
|
|
184
|
+
} else {
|
|
185
|
+
setDropdownStyles({
|
|
186
|
+
position: "absolute",
|
|
187
|
+
top: shouldOpenAbove ? `-${dropdownHeight}px` : "100%",
|
|
188
|
+
left: "0",
|
|
189
|
+
width: "100%",
|
|
190
|
+
zIndex: 10,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
164
193
|
}
|
|
165
|
-
}, []);
|
|
194
|
+
}, [modal, isInsideDialog]);
|
|
166
195
|
|
|
167
196
|
useEffect(() => {
|
|
168
197
|
if (isFocused) {
|
|
@@ -191,7 +220,8 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
191
220
|
return (
|
|
192
221
|
<ul
|
|
193
222
|
className={cn(
|
|
194
|
-
"absolute mt-1 w-full bg-base-popup border border-base-popup text-base-popup-foreground rounded-md shadow-md z-
|
|
223
|
+
"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",
|
|
224
|
+
isAbove ? "bottom-full mb-1" : "top-full mt-1",
|
|
195
225
|
optionContainerClassName
|
|
196
226
|
)}
|
|
197
227
|
style={dropdownStyles}
|
|
@@ -220,7 +250,9 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
220
250
|
return (
|
|
221
251
|
<li
|
|
222
252
|
key={option.value}
|
|
223
|
-
onMouseDown={() =>
|
|
253
|
+
onMouseDown={() => {
|
|
254
|
+
handleOptionClick(option);
|
|
255
|
+
}}
|
|
224
256
|
className={cn(
|
|
225
257
|
`px-4 py-2 hover:bg-primary-hover-bg cursor-pointer`,
|
|
226
258
|
optionItemClassName,
|
|
@@ -289,7 +321,7 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
289
321
|
|
|
290
322
|
const handleOnBlur = useCallback(
|
|
291
323
|
(e: React.FocusEvent<HTMLInputElement, Element>) => {
|
|
292
|
-
setIsFocused(false);
|
|
324
|
+
setTimeout(() => setIsFocused(false), 200);
|
|
293
325
|
clearMismatchValue(e);
|
|
294
326
|
props?.onBlur?.(e);
|
|
295
327
|
},
|
|
@@ -339,7 +371,14 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
339
371
|
onBlur={handleOnBlur}
|
|
340
372
|
onKeyDown={handleOnKeyDown}
|
|
341
373
|
/>
|
|
342
|
-
{isFocused &&
|
|
374
|
+
{isFocused &&
|
|
375
|
+
((isInsideDialog ? false : modal) ? (
|
|
376
|
+
<Portal.Root container={document.body}>
|
|
377
|
+
{renderOptions()}
|
|
378
|
+
</Portal.Root>
|
|
379
|
+
) : (
|
|
380
|
+
renderOptions()
|
|
381
|
+
))}
|
|
343
382
|
</div>
|
|
344
383
|
);
|
|
345
384
|
}
|