@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.
@@ -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 @@ type TabsProps = {
30
30
  leftAction?: React.ReactNode;
31
31
  rightAction?: React.ReactNode;
32
32
  disabled?: boolean;
33
+ keepMounted?: boolean;
33
34
  onAddTab?: () => void;
34
35
  onTabChange?: (tabIndex: number) => void;
35
36
  };
@@ -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)[];
@@ -10,7 +10,8 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { Fragment, forwardRef, useCallback, useEffect, useMemo, useRef, useState, } from "react";
13
+ import { Fragment, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from "react";
14
+ import * as Portal from "@radix-ui/react-portal";
14
15
  import TextInput from "../TextInput/TextInput";
15
16
  import { customInputVariant, dropdownIconVariant, iconWrapperVariant, } from "./Dropdown.styles";
16
17
  import { ChevronDownIcon } from "@heroicons/react/16/solid";
@@ -22,6 +23,10 @@ const Dropdown = forwardRef((_a, ref) => {
22
23
  const [selectedOption, setSelectedOption] = useState(null);
23
24
  const [textValue, setTextValue] = useState("");
24
25
  const keyCode = useRef("");
26
+ const dropdownRef = useRef(null);
27
+ const inputRef = useRef(null);
28
+ const [dropdownStyles, setDropdownStyles] = useState({});
29
+ useImperativeHandle(ref, () => inputRef === null || inputRef === void 0 ? void 0 : inputRef.current);
25
30
  useEffect(() => {
26
31
  var _a;
27
32
  setSelectedOption(value);
@@ -46,15 +51,49 @@ const Dropdown = forwardRef((_a, ref) => {
46
51
  ((_a = option.label) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(textValue === null || textValue === void 0 ? void 0 : textValue.toLowerCase()));
47
52
  });
48
53
  }, [options, filterMode, textValue]);
54
+ const updateDropdownPosition = useCallback(() => {
55
+ var _a;
56
+ if (inputRef.current) {
57
+ const rect = inputRef.current.getBoundingClientRect();
58
+ const dropdownHeight = ((_a = dropdownRef.current) === null || _a === void 0 ? void 0 : _a.offsetHeight) || 0;
59
+ const spaceBelow = window.innerHeight - rect.bottom;
60
+ const spaceAbove = rect.top;
61
+ const position = spaceBelow >= dropdownHeight || spaceBelow > spaceAbove
62
+ ? {
63
+ top: `${rect.bottom + window.scrollY}px`,
64
+ left: `${rect.left + window.scrollX}px`,
65
+ width: `${rect.width}px`,
66
+ }
67
+ : {
68
+ top: `${rect.top - dropdownHeight + window.scrollY}px`,
69
+ left: `${rect.left + window.scrollX}px`,
70
+ width: `${rect.width}px`,
71
+ };
72
+ setDropdownStyles(position);
73
+ }
74
+ }, []);
75
+ useEffect(() => {
76
+ if (isFocused) {
77
+ updateDropdownPosition();
78
+ window.addEventListener("resize", updateDropdownPosition);
79
+ window.addEventListener("scroll", updateDropdownPosition, true);
80
+ }
81
+ return () => {
82
+ window.removeEventListener("resize", updateDropdownPosition);
83
+ window.removeEventListener("scroll", updateDropdownPosition, true);
84
+ };
85
+ }, [isFocused, updateDropdownPosition]);
49
86
  const renderOptions = () => {
50
87
  if (customRenderOptions) {
51
88
  return customRenderOptions({
52
89
  optionsFiltered,
53
90
  selectedOption,
54
91
  onClick: handleOptionClick,
92
+ style: dropdownStyles,
93
+ dropdownRef,
55
94
  });
56
95
  }
57
- return (_jsxs("ul", { className: cn("absolute mt-1 w-full bg-base-popup border border-base-popup text-base-popup-foreground rounded-md shadow-md z-10 max-h-60 overflow-y-auto", optionContainerClassName), children: [optionsFiltered.map((option) => {
96
+ return (_jsxs("ul", { className: cn("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", optionContainerClassName), style: dropdownStyles, ref: dropdownRef, children: [optionsFiltered.map((option) => {
58
97
  if (option.renderLabel) {
59
98
  return (_jsx(Fragment, { children: option.renderLabel({
60
99
  value: option.value,
@@ -106,6 +145,6 @@ const Dropdown = forwardRef((_a, ref) => {
106
145
  keyCode.current = e.code;
107
146
  (_a = props === null || props === void 0 ? void 0 : props.onKeyDown) === null || _a === void 0 ? void 0 : _a.call(props, e);
108
147
  }, [props === null || props === void 0 ? void 0 : props.onKeyDown]);
109
- return (_jsxs("div", { className: `relative ${fullwidth ? "w-full" : ""}`, children: [_jsx(TextInput, Object.assign({ hasClearIcon: false, endIcon: _jsx("div", { className: iconWrapperVariant({ size }), children: _jsx(ChevronDownIcon, { className: dropdownIconVariant({ size, isFocus: isFocused }) }) }) }, props, { ref: ref, readOnly: !filterMode, value: textValue, onChange: handleOnChangeText, label: label, placeholder: " ", type: "text", autoComplete: "off", rounded: rounded, variant: variant, helperText: helperText, errorMessage: errorMessage, fullwidth: fullwidth, error: error, required: required, id: _id, disabled: disabled, size: size, className: customInputVariant({ size }), onFocus: handleOnFocus, onBlur: handleOnBlur, onKeyDown: handleOnKeyDown })), isFocused && renderOptions()] }));
148
+ return (_jsxs("div", { className: `relative ${fullwidth ? "w-full" : ""}`, children: [_jsx(TextInput, Object.assign({ hasClearIcon: false, endIcon: _jsx("div", { className: iconWrapperVariant({ size }), children: _jsx(ChevronDownIcon, { className: dropdownIconVariant({ size, isFocus: isFocused }) }) }) }, props, { ref: inputRef, readOnly: !filterMode, value: textValue, onChange: handleOnChangeText, label: label, placeholder: " ", type: "text", autoComplete: "off", rounded: rounded, variant: variant, helperText: helperText, errorMessage: errorMessage, fullwidth: fullwidth, error: error, required: required, id: _id, disabled: disabled, size: size, className: customInputVariant({ size }), onFocus: handleOnFocus, onBlur: handleOnBlur, onKeyDown: handleOnKeyDown })), isFocused && _jsx(Portal.Root, { children: renderOptions() })] }));
110
149
  });
111
150
  export default Dropdown;
@@ -14,7 +14,7 @@ import { forwardRef } from "react";
14
14
  import { cn } from "@/utils/cn";
15
15
  import { inputVariants } from "./Input.styles";
16
16
  const Input = forwardRef((_a, ref) => {
17
- var { className, type = "text", size = "md", variant = "outline", fullwidth = false, disabled = false, error = false, required = false, hiddenPlaceholder = false } = _a, props = __rest(_a, ["className", "type", "size", "variant", "fullwidth", "disabled", "error", "required", "hiddenPlaceholder"]);
17
+ var { className, type = "text", size = "md", variant = "outline", rounded = "normal", fullwidth = false, disabled = false, error = false, required = false, hiddenPlaceholder = false } = _a, props = __rest(_a, ["className", "type", "size", "variant", "rounded", "fullwidth", "disabled", "error", "required", "hiddenPlaceholder"]);
18
18
  return (_jsx("input", Object.assign({ type: type, className: cn(inputVariants({
19
19
  size,
20
20
  variant,
@@ -22,6 +22,7 @@ const Input = forwardRef((_a, ref) => {
22
22
  error,
23
23
  hiddenPlaceholder,
24
24
  disabled,
25
+ rounded,
25
26
  }), className), ref: ref, disabled: disabled }, props)));
26
27
  });
27
28
  Input.displayName = "Input";
@@ -5,8 +5,7 @@ import "./Tabs.css";
5
5
  import ActionButton from "../ActionButton/ActionButton";
6
6
  import Icon from "../Icon/Icon";
7
7
  import { Loading } from "@/index";
8
- const Tabs = ({ tabs = [], value, initialTab = 0, tabBarSize = 38, enableBorderLine = true, enableAddTabButton = false, keepIconSpace = true, disabled = false, tabMode = "start", className, tabBarClassName, tabBarContainerClassName, tabBarWrapperClassName, tabButtonClassName, tabButtonActiveClassName, tabContentClassName, addTabButtonWrapperClassName, borderSliderClassName, leftAction, rightAction, onAddTab, onTabChange, }) => {
9
- var _a;
8
+ const Tabs = ({ tabs = [], value, initialTab = 0, tabBarSize = 38, enableBorderLine = true, enableAddTabButton = false, keepIconSpace = true, disabled = false, keepMounted = false, tabMode = "start", className, tabBarClassName, tabBarContainerClassName, tabBarWrapperClassName, tabButtonClassName, tabButtonActiveClassName, tabContentClassName, addTabButtonWrapperClassName, borderSliderClassName, leftAction, rightAction, onAddTab, onTabChange, }) => {
10
9
  const [activeTab, setActiveTab] = useState(initialTab);
11
10
  const [sliderStyle, setSliderStyle] = useState({
12
11
  width: "0px",
@@ -19,47 +18,46 @@ const Tabs = ({ tabs = [], value, initialTab = 0, tabBarSize = 38, enableBorderL
19
18
  setActiveTab(value);
20
19
  }
21
20
  }, [value]);
22
- // const updateSliderStyle = () => {
23
- // const activeTabElement = tabRefs.current[activeTab];
24
- // if (activeTabElement) {
25
- // setSliderStyle({
26
- // width: `${activeTabElement.offsetWidth}px`,
27
- // transform: `translateX(${activeTabElement.offsetLeft}px)`,
28
- // });
29
- // }
30
- // };
31
- // useEffect(() => {
32
- // let timer: NodeJS.Timeout;
33
- // if (isInitialMount.current) {
34
- // isInitialMount.current = false;
35
- // // Set initial position without animation
36
- // const activeTabElement = tabRefs.current[activeTab];
37
- // if (activeTabElement) {
38
- // setSliderStyle({
39
- // width: "0px",
40
- // transform: `translateX(${
41
- // activeTabElement.offsetLeft + activeTabElement.offsetWidth / 2
42
- // }px)`,
43
- // });
44
- // // Trigger reflow
45
- // timer = setTimeout(() => {
46
- // updateSliderStyle();
47
- // }, 50);
48
- // }
49
- // } else {
50
- // updateSliderStyle();
51
- // }
52
- // const handleResize = () => {
53
- // updateSliderStyle();
54
- // };
55
- // window.addEventListener("resize", handleResize);
56
- // return () => {
57
- // window.removeEventListener("resize", handleResize);
58
- // if (timer) {
59
- // clearTimeout(timer);
60
- // }
61
- // };
62
- // }, [activeTab, tabs, tabMode, keepIconSpace]);
21
+ const updateSliderStyle = () => {
22
+ const activeTabElement = tabRefs.current[activeTab];
23
+ if (activeTabElement) {
24
+ setSliderStyle({
25
+ width: `${activeTabElement.offsetWidth}px`,
26
+ transform: `translateX(${activeTabElement.offsetLeft}px)`,
27
+ });
28
+ }
29
+ };
30
+ useEffect(() => {
31
+ let timeout;
32
+ if (isInitialMount.current) {
33
+ isInitialMount.current = false;
34
+ // Set initial position without animation
35
+ const activeTabElement = tabRefs.current[activeTab];
36
+ if (activeTabElement) {
37
+ setSliderStyle({
38
+ width: "0px",
39
+ transform: `translateX(${activeTabElement.offsetLeft + activeTabElement.offsetWidth / 2}px)`,
40
+ });
41
+ // Trigger reflow
42
+ timeout = setTimeout(() => {
43
+ updateSliderStyle();
44
+ }, 50);
45
+ }
46
+ }
47
+ else {
48
+ updateSliderStyle();
49
+ }
50
+ const handleResize = () => {
51
+ updateSliderStyle();
52
+ };
53
+ window.addEventListener("resize", handleResize);
54
+ return () => {
55
+ window.removeEventListener("resize", handleResize);
56
+ if (timeout) {
57
+ clearTimeout(timeout);
58
+ }
59
+ };
60
+ }, [activeTab, tabs, tabMode, keepIconSpace]);
63
61
  return (_jsxs("div", { className: cn("w-full", className), children: [_jsxs("div", { className: cn(" relative flex flex-row w-full", {
64
62
  [`border-b-[1px] border-base-stroke`]: enableBorderLine,
65
63
  "border-state-disable-outline": disabled,
@@ -84,6 +82,12 @@ const Tabs = ({ tabs = [], value, initialTab = 0, tabBarSize = 38, enableBorderL
84
82
  onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange(index);
85
83
  }, children: [(keepIconSpace || tab.startTabContent) && (_jsx("div", { className: "h-full w-3 flex items-center justify-center", children: tab.isLoading ? _jsx(Loading, {}) : tab.startTabContent })), tab.label, (keepIconSpace || tab.endTabContent) && (_jsx("div", { className: "h-full w-3 flex items-center justify-center", children: tab.endTabContent }))] }, index))), _jsx("div", { className: cn(`absolute left-0 bottom-0 h-[2px] rounded-full bg-foreground transition-all duration-300 ease-in-out`, {
86
84
  "bg-state-disable-solid": disabled,
87
- }, borderSliderClassName), style: sliderStyle })] }) }), enableAddTabButton && (_jsx("div", { className: cn("sticky right-0 flex content-center items-center mx-4", addTabButtonWrapperClassName), children: _jsx(ActionButton, { variant: "outline", size: "sm", onClick: () => onAddTab === null || onAddTab === void 0 ? void 0 : onAddTab(), disabled: disabled, children: _jsx(Icon, { name: "plus" }) }) })), rightAction] }), _jsx("div", { className: cn("mt-4 text-foreground", tabContentClassName), role: "tabpanel", id: `tab-content-${activeTab}`, "aria-labelledby": `tab-${activeTab}`, children: (_a = tabs[activeTab]) === null || _a === void 0 ? void 0 : _a.content })] }));
85
+ }, borderSliderClassName), style: sliderStyle })] }) }), enableAddTabButton && (_jsx("div", { className: cn("sticky right-0 flex content-center items-center mx-4", addTabButtonWrapperClassName), children: _jsx(ActionButton, { variant: "outline", size: "sm", onClick: () => onAddTab === null || onAddTab === void 0 ? void 0 : onAddTab(), disabled: disabled, children: _jsx(Icon, { name: "plus" }) }) })), rightAction] }), _jsx("div", { className: cn("mt-4 text-foreground", tabContentClassName), role: "tabpanel", id: `tab-content-${activeTab}`, "aria-labelledby": `tab-${activeTab}`, children: tabs.map((tab, idx) => {
86
+ var _a;
87
+ if (!keepMounted && activeTab !== idx) {
88
+ return null;
89
+ }
90
+ return (_jsx("div", { className: `transition ${activeTab === idx ? "block" : "hidden"}`, children: tab.content }, (_a = tab.id) !== null && _a !== void 0 ? _a : idx));
91
+ }) })] }));
88
92
  };
89
93
  export default Tabs;