@rovula/ui 0.0.62 → 0.0.64
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.js +3 -3
- package/dist/cjs/bundle.js.map +1 -1
- package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +5 -1
- package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +4 -0
- package/dist/cjs/types/components/Search/Search.stories.d.ts +2 -0
- package/dist/cjs/types/components/Tabs/Tabs.d.ts +1 -0
- package/dist/cjs/types/components/Tabs/Tabs.stories.d.ts +2 -0
- package/dist/components/Dropdown/Dropdown.js +42 -3
- package/dist/components/Input/Input.js +2 -1
- package/dist/components/Tabs/Tabs.js +48 -44
- package/dist/esm/bundle.js +3 -3
- package/dist/esm/bundle.js.map +1 -1
- package/dist/esm/types/components/Dropdown/Dropdown.d.ts +5 -1
- package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +4 -0
- package/dist/esm/types/components/Search/Search.stories.d.ts +2 -0
- package/dist/esm/types/components/Tabs/Tabs.d.ts +1 -0
- package/dist/esm/types/components/Tabs/Tabs.stories.d.ts +2 -0
- package/dist/index.d.ts +6 -1
- package/package.json +1 -1
- package/src/components/Dropdown/Dropdown.tsx +55 -4
- package/src/components/Input/Input.tsx +2 -0
- package/src/components/Tabs/Tabs.tsx +57 -42
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { ReactNode } from "react";
|
|
1
|
+
import React, { CSSProperties, ReactNode } from "react";
|
|
2
2
|
import { InputProps } from "../TextInput/TextInput";
|
|
3
3
|
type RenderLabelCallbackArg = {
|
|
4
4
|
value: string;
|
|
@@ -36,6 +36,8 @@ export type DropdownProps = {
|
|
|
36
36
|
optionsFiltered: Options[];
|
|
37
37
|
selectedOption: Options | null | undefined;
|
|
38
38
|
onClick: (option: Options) => void;
|
|
39
|
+
style?: CSSProperties;
|
|
40
|
+
dropdownRef?: React.RefObject<HTMLUListElement>;
|
|
39
41
|
}) => ReactNode;
|
|
40
42
|
} & Omit<InputProps, "value" | "onSelect">;
|
|
41
43
|
declare const Dropdown: React.ForwardRefExoticComponent<{
|
|
@@ -63,6 +65,8 @@ declare const Dropdown: React.ForwardRefExoticComponent<{
|
|
|
63
65
|
optionsFiltered: Options[];
|
|
64
66
|
selectedOption: Options | null | undefined;
|
|
65
67
|
onClick: (option: Options) => void;
|
|
68
|
+
style?: CSSProperties;
|
|
69
|
+
dropdownRef?: React.RefObject<HTMLUListElement>;
|
|
66
70
|
}) => ReactNode) | undefined;
|
|
67
71
|
} & Omit<InputProps, "onSelect" | "value"> & React.RefAttributes<HTMLInputElement>>;
|
|
68
72
|
export default Dropdown;
|
|
@@ -27,6 +27,8 @@ declare const meta: {
|
|
|
27
27
|
optionsFiltered: Options[];
|
|
28
28
|
selectedOption: Options | null | undefined;
|
|
29
29
|
onClick: (option: Options) => void;
|
|
30
|
+
style?: React.CSSProperties | undefined;
|
|
31
|
+
dropdownRef?: React.RefObject<HTMLUListElement> | undefined;
|
|
30
32
|
}) => React.ReactNode) | undefined;
|
|
31
33
|
} & Omit<import("../..").InputProps, "onSelect" | "value"> & React.RefAttributes<HTMLInputElement>>;
|
|
32
34
|
tags: string[];
|
|
@@ -58,6 +60,8 @@ declare const meta: {
|
|
|
58
60
|
optionsFiltered: Options[];
|
|
59
61
|
selectedOption: Options | null | undefined;
|
|
60
62
|
onClick: (option: Options) => void;
|
|
63
|
+
style?: React.CSSProperties | undefined;
|
|
64
|
+
dropdownRef?: React.RefObject<HTMLUListElement> | undefined;
|
|
61
65
|
}) => React.ReactNode) | undefined;
|
|
62
66
|
suppressHydrationWarning?: boolean | undefined;
|
|
63
67
|
color?: string | undefined;
|
|
@@ -322,6 +322,8 @@ declare const meta: {
|
|
|
322
322
|
optionsFiltered: Options[];
|
|
323
323
|
selectedOption: Options | null | undefined;
|
|
324
324
|
onClick: (option: Options) => void;
|
|
325
|
+
style?: React.CSSProperties | undefined;
|
|
326
|
+
dropdownRef?: React.RefObject<HTMLUListElement> | undefined;
|
|
325
327
|
}) => React.ReactNode) | undefined;
|
|
326
328
|
optionContainerClassName?: string | undefined;
|
|
327
329
|
optionItemClassName?: string | undefined;
|
|
@@ -30,6 +30,7 @@ declare const meta: {
|
|
|
30
30
|
leftAction?: React.ReactNode;
|
|
31
31
|
rightAction?: React.ReactNode;
|
|
32
32
|
disabled?: boolean | undefined;
|
|
33
|
+
keepMounted?: boolean | undefined;
|
|
33
34
|
onAddTab?: (() => void) | undefined;
|
|
34
35
|
onTabChange?: ((tabIndex: number) => void) | undefined;
|
|
35
36
|
}>;
|
|
@@ -66,6 +67,7 @@ declare const meta: {
|
|
|
66
67
|
leftAction?: React.ReactNode;
|
|
67
68
|
rightAction?: React.ReactNode;
|
|
68
69
|
disabled?: boolean | undefined;
|
|
70
|
+
keepMounted?: boolean | undefined;
|
|
69
71
|
onAddTab?: (() => void) | undefined;
|
|
70
72
|
onTabChange?: ((tabIndex: number) => void) | undefined;
|
|
71
73
|
}>) => import("react/jsx-runtime").JSX.Element)[];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import React__default, { ReactElement, ReactNode, FC, ComponentPropsWithoutRef
|
|
2
|
+
import React__default, { ReactElement, ReactNode, CSSProperties, FC, ComponentPropsWithoutRef } from 'react';
|
|
3
3
|
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
|
4
4
|
import * as class_variance_authority_dist_types from 'class-variance-authority/dist/types';
|
|
5
5
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
@@ -135,6 +135,7 @@ type TabsProps = {
|
|
|
135
135
|
leftAction?: React__default.ReactNode;
|
|
136
136
|
rightAction?: React__default.ReactNode;
|
|
137
137
|
disabled?: boolean;
|
|
138
|
+
keepMounted?: boolean;
|
|
138
139
|
onAddTab?: () => void;
|
|
139
140
|
onTabChange?: (tabIndex: number) => void;
|
|
140
141
|
};
|
|
@@ -176,6 +177,8 @@ type DropdownProps = {
|
|
|
176
177
|
optionsFiltered: Options$1[];
|
|
177
178
|
selectedOption: Options$1 | null | undefined;
|
|
178
179
|
onClick: (option: Options$1) => void;
|
|
180
|
+
style?: CSSProperties;
|
|
181
|
+
dropdownRef?: React__default.RefObject<HTMLUListElement>;
|
|
179
182
|
}) => ReactNode;
|
|
180
183
|
} & Omit<InputProps, "value" | "onSelect">;
|
|
181
184
|
declare const Dropdown: React__default.ForwardRefExoticComponent<{
|
|
@@ -203,6 +206,8 @@ declare const Dropdown: React__default.ForwardRefExoticComponent<{
|
|
|
203
206
|
optionsFiltered: Options$1[];
|
|
204
207
|
selectedOption: Options$1 | null | undefined;
|
|
205
208
|
onClick: (option: Options$1) => void;
|
|
209
|
+
style?: CSSProperties;
|
|
210
|
+
dropdownRef?: React__default.RefObject<HTMLUListElement>;
|
|
206
211
|
}) => ReactNode) | undefined;
|
|
207
212
|
} & Omit<InputProps, "onSelect" | "value"> & React__default.RefAttributes<HTMLInputElement>>;
|
|
208
213
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import React, {
|
|
2
|
+
CSSProperties,
|
|
2
3
|
Fragment,
|
|
3
4
|
ReactNode,
|
|
4
5
|
forwardRef,
|
|
5
6
|
useCallback,
|
|
6
7
|
useEffect,
|
|
8
|
+
useImperativeHandle,
|
|
7
9
|
useMemo,
|
|
8
10
|
useRef,
|
|
9
11
|
useState,
|
|
10
12
|
} from "react";
|
|
11
|
-
|
|
13
|
+
import * as Portal from "@radix-ui/react-portal";
|
|
12
14
|
import TextInput, { InputProps } from "../TextInput/TextInput";
|
|
13
15
|
import {
|
|
14
16
|
customInputVariant,
|
|
@@ -57,6 +59,8 @@ export type DropdownProps = {
|
|
|
57
59
|
optionsFiltered: Options[];
|
|
58
60
|
selectedOption: Options | null | undefined;
|
|
59
61
|
onClick: (option: Options) => void;
|
|
62
|
+
style?: CSSProperties;
|
|
63
|
+
dropdownRef?: React.RefObject<HTMLUListElement>;
|
|
60
64
|
}) => ReactNode;
|
|
61
65
|
} & Omit<InputProps, "value" | "onSelect">;
|
|
62
66
|
|
|
@@ -96,6 +100,12 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
96
100
|
const [textValue, setTextValue] = useState("");
|
|
97
101
|
const keyCode = useRef("");
|
|
98
102
|
|
|
103
|
+
const dropdownRef = useRef<HTMLUListElement>(null);
|
|
104
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
105
|
+
const [dropdownStyles, setDropdownStyles] = useState({});
|
|
106
|
+
|
|
107
|
+
useImperativeHandle(ref, () => inputRef?.current as HTMLInputElement);
|
|
108
|
+
|
|
99
109
|
useEffect(() => {
|
|
100
110
|
setSelectedOption(value);
|
|
101
111
|
setTextValue(value?.label ?? "");
|
|
@@ -130,21 +140,62 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
130
140
|
);
|
|
131
141
|
}, [options, filterMode, textValue]);
|
|
132
142
|
|
|
143
|
+
const updateDropdownPosition = useCallback(() => {
|
|
144
|
+
if (inputRef.current) {
|
|
145
|
+
const rect = inputRef.current.getBoundingClientRect();
|
|
146
|
+
const dropdownHeight = dropdownRef.current?.offsetHeight || 0;
|
|
147
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
148
|
+
const spaceAbove = rect.top;
|
|
149
|
+
|
|
150
|
+
const position =
|
|
151
|
+
spaceBelow >= dropdownHeight || spaceBelow > spaceAbove
|
|
152
|
+
? {
|
|
153
|
+
top: `${rect.bottom + window.scrollY}px`,
|
|
154
|
+
left: `${rect.left + window.scrollX}px`,
|
|
155
|
+
width: `${rect.width}px`,
|
|
156
|
+
}
|
|
157
|
+
: {
|
|
158
|
+
top: `${rect.top - dropdownHeight + window.scrollY}px`,
|
|
159
|
+
left: `${rect.left + window.scrollX}px`,
|
|
160
|
+
width: `${rect.width}px`,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
setDropdownStyles(position);
|
|
164
|
+
}
|
|
165
|
+
}, []);
|
|
166
|
+
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (isFocused) {
|
|
169
|
+
updateDropdownPosition();
|
|
170
|
+
window.addEventListener("resize", updateDropdownPosition);
|
|
171
|
+
window.addEventListener("scroll", updateDropdownPosition, true);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return () => {
|
|
175
|
+
window.removeEventListener("resize", updateDropdownPosition);
|
|
176
|
+
window.removeEventListener("scroll", updateDropdownPosition, true);
|
|
177
|
+
};
|
|
178
|
+
}, [isFocused, updateDropdownPosition]);
|
|
179
|
+
|
|
133
180
|
const renderOptions = () => {
|
|
134
181
|
if (customRenderOptions) {
|
|
135
182
|
return customRenderOptions({
|
|
136
183
|
optionsFiltered,
|
|
137
184
|
selectedOption,
|
|
138
185
|
onClick: handleOptionClick,
|
|
186
|
+
style: dropdownStyles,
|
|
187
|
+
dropdownRef,
|
|
139
188
|
});
|
|
140
189
|
}
|
|
141
190
|
|
|
142
191
|
return (
|
|
143
192
|
<ul
|
|
144
193
|
className={cn(
|
|
145
|
-
"absolute mt-1 w-full bg-base-popup border border-base-popup text-base-popup-foreground rounded-md shadow-md z-
|
|
194
|
+
"absolute mt-1 w-full bg-base-popup border border-base-popup text-base-popup-foreground rounded-md shadow-md z-50 max-h-60 overflow-y-auto",
|
|
146
195
|
optionContainerClassName
|
|
147
196
|
)}
|
|
197
|
+
style={dropdownStyles}
|
|
198
|
+
ref={dropdownRef}
|
|
148
199
|
>
|
|
149
200
|
{optionsFiltered.map((option) => {
|
|
150
201
|
if (option.renderLabel) {
|
|
@@ -265,7 +316,7 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
265
316
|
</div>
|
|
266
317
|
}
|
|
267
318
|
{...props}
|
|
268
|
-
ref={
|
|
319
|
+
ref={inputRef}
|
|
269
320
|
readOnly={!filterMode}
|
|
270
321
|
value={textValue}
|
|
271
322
|
onChange={handleOnChangeText}
|
|
@@ -288,7 +339,7 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
288
339
|
onBlur={handleOnBlur}
|
|
289
340
|
onKeyDown={handleOnKeyDown}
|
|
290
341
|
/>
|
|
291
|
-
{isFocused && renderOptions()}
|
|
342
|
+
{isFocused && <Portal.Root>{renderOptions()}</Portal.Root>}
|
|
292
343
|
</div>
|
|
293
344
|
);
|
|
294
345
|
}
|
|
@@ -21,6 +21,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
21
21
|
type = "text",
|
|
22
22
|
size = "md",
|
|
23
23
|
variant = "outline",
|
|
24
|
+
rounded = "normal",
|
|
24
25
|
fullwidth = false,
|
|
25
26
|
disabled = false,
|
|
26
27
|
error = false,
|
|
@@ -41,6 +42,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
41
42
|
error,
|
|
42
43
|
hiddenPlaceholder,
|
|
43
44
|
disabled,
|
|
45
|
+
rounded,
|
|
44
46
|
}),
|
|
45
47
|
className
|
|
46
48
|
)}
|
|
@@ -36,6 +36,7 @@ type TabsProps = {
|
|
|
36
36
|
leftAction?: React.ReactNode;
|
|
37
37
|
rightAction?: React.ReactNode;
|
|
38
38
|
disabled?: boolean;
|
|
39
|
+
keepMounted?: boolean;
|
|
39
40
|
onAddTab?: () => void;
|
|
40
41
|
onTabChange?: (tabIndex: number) => void;
|
|
41
42
|
};
|
|
@@ -49,6 +50,7 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
49
50
|
enableAddTabButton = false,
|
|
50
51
|
keepIconSpace = true,
|
|
51
52
|
disabled = false,
|
|
53
|
+
keepMounted = false,
|
|
52
54
|
tabMode = "start",
|
|
53
55
|
className,
|
|
54
56
|
tabBarClassName,
|
|
@@ -78,53 +80,54 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
78
80
|
}
|
|
79
81
|
}, [value]);
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
83
|
+
const updateSliderStyle = () => {
|
|
84
|
+
const activeTabElement = tabRefs.current[activeTab];
|
|
85
|
+
if (activeTabElement) {
|
|
86
|
+
setSliderStyle({
|
|
87
|
+
width: `${activeTabElement.offsetWidth}px`,
|
|
88
|
+
transform: `translateX(${activeTabElement.offsetLeft}px)`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
let timeout: NodeJS.Timeout;
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
if (isInitialMount.current) {
|
|
97
|
+
isInitialMount.current = false;
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
// Set initial position without animation
|
|
100
|
+
const activeTabElement = tabRefs.current[activeTab];
|
|
101
|
+
if (activeTabElement) {
|
|
102
|
+
setSliderStyle({
|
|
103
|
+
width: "0px",
|
|
104
|
+
transform: `translateX(${
|
|
105
|
+
activeTabElement.offsetLeft + activeTabElement.offsetWidth / 2
|
|
106
|
+
}px)`,
|
|
107
|
+
});
|
|
96
108
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// });
|
|
109
|
+
// Trigger reflow
|
|
110
|
+
timeout = setTimeout(() => {
|
|
111
|
+
updateSliderStyle();
|
|
112
|
+
}, 50);
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
updateSliderStyle();
|
|
116
|
+
}
|
|
106
117
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// }, 50);
|
|
111
|
-
// }
|
|
112
|
-
// } else {
|
|
113
|
-
// updateSliderStyle();
|
|
114
|
-
// }
|
|
118
|
+
const handleResize = () => {
|
|
119
|
+
updateSliderStyle();
|
|
120
|
+
};
|
|
115
121
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
window.addEventListener("resize", handleResize);
|
|
123
|
+
return () => {
|
|
124
|
+
window.removeEventListener("resize", handleResize);
|
|
119
125
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// }
|
|
126
|
-
// };
|
|
127
|
-
// }, [activeTab, tabs, tabMode, keepIconSpace]);
|
|
126
|
+
if (timeout) {
|
|
127
|
+
clearTimeout(timeout);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}, [activeTab, tabs, tabMode, keepIconSpace]);
|
|
128
131
|
|
|
129
132
|
return (
|
|
130
133
|
<div className={cn("w-full", className)}>
|
|
@@ -240,7 +243,19 @@ const Tabs: React.FC<TabsProps> = ({
|
|
|
240
243
|
id={`tab-content-${activeTab}`}
|
|
241
244
|
aria-labelledby={`tab-${activeTab}`}
|
|
242
245
|
>
|
|
243
|
-
{tabs
|
|
246
|
+
{tabs.map((tab, idx) => {
|
|
247
|
+
if (!keepMounted && activeTab !== idx) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
return (
|
|
251
|
+
<div
|
|
252
|
+
key={tab.id ?? idx}
|
|
253
|
+
className={`transition ${activeTab === idx ? "block" : "hidden"}`}
|
|
254
|
+
>
|
|
255
|
+
{tab.content}
|
|
256
|
+
</div>
|
|
257
|
+
);
|
|
258
|
+
})}
|
|
244
259
|
</div>
|
|
245
260
|
</div>
|
|
246
261
|
);
|