@@ -11,7 +14,3 @@ export const SidebarFooter = ({ children, ...rest }) => {
};
SidebarFooter.displayName = "Sidebar.Footer";
-
-SidebarFooter.propTypes = {
- children: PropTypes.node.isRequired,
-};
diff --git a/src/components/Sidebar/Sidebar.Heading.jsx b/src/components/Sidebar/Sidebar.Heading.tsx
similarity index 51%
rename from src/components/Sidebar/Sidebar.Heading.jsx
rename to src/components/Sidebar/Sidebar.Heading.tsx
index f914bdc63..94e1566c5 100644
--- a/src/components/Sidebar/Sidebar.Heading.jsx
+++ b/src/components/Sidebar/Sidebar.Heading.tsx
@@ -1,8 +1,13 @@
import clsx from "clsx";
-import PropTypes from "prop-types";
-import React from "react";
+import React, { type ComponentType } from "react";
-export const SidebarHeading = ({ icon: Icon, label, className }) => {
+export interface SidebarHeadingProps {
+ icon: ComponentType
>;
+ label: string;
+ className?: string;
+}
+
+export const SidebarHeading = ({ icon: Icon, label, className }: SidebarHeadingProps) => {
return (
@@ -12,9 +17,3 @@ export const SidebarHeading = ({ icon: Icon, label, className }) => {
};
SidebarHeading.displayName = "Sidebar.Heading";
-
-SidebarHeading.propTypes = {
- icon: PropTypes.func.isRequired,
- label: PropTypes.string.isRequired,
- className: PropTypes.string,
-};
diff --git a/src/components/Sidebar/Sidebar.Link.jsx b/src/components/Sidebar/Sidebar.Link.tsx
similarity index 68%
rename from src/components/Sidebar/Sidebar.Link.jsx
rename to src/components/Sidebar/Sidebar.Link.tsx
index da346b989..9f5cee4f2 100644
--- a/src/components/Sidebar/Sidebar.Link.jsx
+++ b/src/components/Sidebar/Sidebar.Link.tsx
@@ -1,12 +1,31 @@
-/* eslint-disable no-undef */
import clsx from "clsx";
-import PropTypes from "prop-types";
-import React, { useEffect, useRef, useState } from "react";
+import React, { type ComponentType, useEffect, useRef, useState } from "react";
import { ChevronRightIcon } from "../../icons";
import { Dot } from "../Dot/Dot";
-export const SidebarLink = ({ isActive = false, icon: Icon, children, isSubMenuItem, align, classNames, ...rest }) => {
- const containerRef = useRef(null);
+type SidebarLinkAlign = "center" | "left" | "right";
+
+export interface SidebarLinkProps extends React.ButtonHTMLAttributes {
+ isActive?: boolean;
+ icon?: ComponentType>;
+ isSubMenuItem?: boolean;
+ align?: SidebarLinkAlign;
+ children: React.ReactNode;
+ classNames?: {
+ text?: string;
+ };
+}
+
+export const SidebarLink = ({
+ isActive = false,
+ icon: Icon,
+ isSubMenuItem,
+ align,
+ children,
+ classNames,
+ ...rest
+}: SidebarLinkProps) => {
+ const containerRef = useRef(null);
const [showText, setShowText] = useState(true);
const [showIcon, setShowIcon] = useState(true);
@@ -44,7 +63,7 @@ export const SidebarLink = ({ isActive = false, icon: Icon, children, isSubMenuI
"bg-primary text-white hover:bg-primary-dark": isActive,
"text-gray hover:bg-gray-darker": !isActive,
"justify-start px-6 py-2": isSubMenuItem,
- "justify-center py-3 px-6": !isSubMenuItem,
+ "justify-center px-6 py-3": !isSubMenuItem,
},
)}
{...rest}
@@ -52,15 +71,16 @@ export const SidebarLink = ({ isActive = false, icon: Icon, children, isSubMenuI
{isSubMenuItem ? (
) : (
- showIcon && (
+ showIcon &&
+ Icon && (
)
)}
- {(showText || isSubMenuItem) && (
-
+ {(showText ?? isSubMenuItem) && (
+
{children}
)}
@@ -72,20 +92,12 @@ export const SidebarLink = ({ isActive = false, icon: Icon, children, isSubMenuI
SidebarLink.displayName = "Sidebar.Link";
-SidebarLink.propTypes = {
- align: PropTypes.oneOf(["center", "left", "right"]),
- isActive: PropTypes.bool,
- icon: PropTypes.func,
- children: PropTypes.node.isRequired,
- isSubMenuItem: PropTypes.bool,
-};
+export interface SidebarSeparatorProps {
+ className?: string;
+}
-export const SidebarSeparator = ({ className }) => {
+export const SidebarSeparator = ({ className }: SidebarSeparatorProps) => {
return
;
};
SidebarSeparator.displayName = "Sidebar.Separator";
-
-SidebarSeparator.propTypes = {
- className: PropTypes.string,
-};
diff --git a/src/components/Sidebar/Sidebar.Menu.module.css.d.ts b/src/components/Sidebar/Sidebar.Menu.module.css.d.ts
new file mode 100644
index 000000000..d35f6ec8e
--- /dev/null
+++ b/src/components/Sidebar/Sidebar.Menu.module.css.d.ts
@@ -0,0 +1,5 @@
+declare const styles: {
+ [key: string]: string;
+ main: string;
+};
+export default styles;
diff --git a/src/components/Sidebar/Sidebar.Menu.jsx b/src/components/Sidebar/Sidebar.Menu.tsx
similarity index 78%
rename from src/components/Sidebar/Sidebar.Menu.jsx
rename to src/components/Sidebar/Sidebar.Menu.tsx
index 8c93dc536..cbe7ffc4d 100644
--- a/src/components/Sidebar/Sidebar.Menu.jsx
+++ b/src/components/Sidebar/Sidebar.Menu.tsx
@@ -1,13 +1,17 @@
import Tippy from "@tippyjs/react";
import clsx from "clsx";
-import PropTypes from "prop-types";
import React from "react";
import styles from "./Sidebar.Menu.module.css";
-// eslint-disable-next-line no-undef
const appendTo = typeof window === "undefined" ? undefined : window.document.body;
-export const SidebarMenu = ({ children, content, ...rest }) => {
+export interface SidebarMenuProps {
+ [key: string]: any;
+ content: React.ReactNode;
+ children: React.ReactNode;
+}
+
+export const SidebarMenu = ({ content, children, ...rest }: SidebarMenuProps) => {
return (
{
};
SidebarMenu.displayName = "Sidebar.Menu";
-
-SidebarMenu.propTypes = {
- children: PropTypes.node.isRequired,
- content: PropTypes.node.isRequired,
-};
diff --git a/src/components/Sidebar/Sidebar.jsx b/src/components/Sidebar/Sidebar.tsx
similarity index 84%
rename from src/components/Sidebar/Sidebar.jsx
rename to src/components/Sidebar/Sidebar.tsx
index 7020802b3..7ad514746 100644
--- a/src/components/Sidebar/Sidebar.jsx
+++ b/src/components/Sidebar/Sidebar.tsx
@@ -1,6 +1,4 @@
-/* eslint-disable no-undef */
import clsx from "clsx";
-import PropTypes from "prop-types";
import React, { useEffect, useRef, useState } from "react";
import { AnnounceIcon, BellIcon, XolaLogoSimple } from "../../icons";
import { Counter } from "../Counter";
@@ -34,29 +32,55 @@ const BREAKPOINTS = {
};
// Get max width based on window size
-
-const getMaxWidth = (currentWindowWidth = typeof window === "undefined" ? 1280 : window.innerWidth) => {
+const getMaxWidth = (currentWindowWidth = typeof window === "undefined" ? 1280 : window.innerWidth): number => {
if (currentWindowWidth >= BREAKPOINTS.XL) return SIDEBAR_WIDTHS.XL;
if (currentWindowWidth >= BREAKPOINTS.LG) return SIDEBAR_WIDTHS.LG;
if (currentWindowWidth >= BREAKPOINTS.MD) return SIDEBAR_WIDTHS.MD;
return SIDEBAR_WIDTHS.SM;
};
+interface NotificationDrawer {
+ count: number;
+ content: React.ReactNode;
+ title: string;
+ hide?: boolean;
+ onClose?: () => void;
+}
+
+export interface SidebarProps {
+ logo?: React.ReactElement;
+ footer: React.ReactElement;
+ notifications?: {
+ announcements?: NotificationDrawer;
+ notices?: NotificationDrawer;
+ };
+ isFixed?: boolean;
+ isStickyHeader?: boolean;
+ isStickyFooter?: boolean;
+ isLeftDrawerOpen?: boolean;
+ isRightDrawerOpen?: boolean;
+ children: React.ReactNode;
+ className?: string;
+ onLogoClick: () => void;
+ handleDrawerStateChange?: (drawer: "left" | "right") => void;
+ onSidebarResize?: (width: number) => void;
+}
+
export const Sidebar = ({
logo,
- children,
- className,
footer,
notifications,
isFixed = true,
- onLogoClick,
isStickyHeader = true,
isStickyFooter = true,
isLeftDrawerOpen,
isRightDrawerOpen,
+ children,
+ className,
+ onLogoClick,
handleDrawerStateChange,
onSidebarResize,
-}) => {
+}: SidebarProps) => {
// Initialize width from localStorage or use default responsive values
const [width, setWidth] = useState(() => {
if (typeof window !== "undefined") {
@@ -69,10 +93,10 @@ export const Sidebar = ({
const [isHovered, setIsHovered] = useState(false);
const [isResizing, setIsResizing] = useState(false);
- const sidebarRef = useRef(null);
+ const sidebarRef = useRef(null);
const { announcements: leftDrawer, notices: rightDrawer } = notifications ?? {};
- const hideRightDrawer = rightDrawer?.count <= 0 || !rightDrawer;
+ const hideRightDrawer = (rightDrawer?.count ?? 0) <= 0 || !rightDrawer;
const isStickyHeaderFooter = isStickyHeader && isStickyFooter;
// Handle window resize
@@ -88,7 +112,7 @@ export const Sidebar = ({
// Handle resizing
useEffect(() => {
- const handleMouseMove = (e) => {
+ const handleMouseMove = (e: MouseEvent) => {
if (!isResizing || !sidebarRef.current) return;
const newWidth = Math.min(Math.max(e.clientX, 64), 200); // Constrain between 64px and 200px
@@ -124,7 +148,7 @@ export const Sidebar = ({
}
}, [width, onSidebarResize]);
- const handleResizeStart = (e) => {
+ const handleResizeStart = (e: React.MouseEvent) => {
e.preventDefault();
setIsResizing(true);
};
@@ -144,12 +168,12 @@ export const Sidebar = ({
>
{/* Resize handle */}
setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
/>
- {leftDrawer || rightDrawer ? (
+ {leftDrawer ?? rightDrawer ? (
handleDrawerStateChange("left")}
+ onClick={() => handleDrawerStateChange?.("left")}
>
{leftDrawer.count}
@@ -192,7 +216,7 @@ export const Sidebar = ({
justifyContent: "center",
alignItems: "center",
}}
- onClick={() => handleDrawerStateChange("right")}
+ onClick={() => handleDrawerStateChange?.("right")}
>
{rightDrawer.count}
@@ -210,8 +234,8 @@ export const Sidebar = ({
size="xl"
title={leftDrawer.title}
content={leftDrawer.content}
- isOpen={isLeftDrawerOpen}
- onClose={(e) => !!e && handleDrawerStateChange("left")}
+ isOpen={isLeftDrawerOpen ?? false}
+ onClose={(e: any) => !!e && handleDrawerStateChange?.("left")}
/>
)}
@@ -223,8 +247,8 @@ export const Sidebar = ({
size="xl"
title={rightDrawer.title}
content={rightDrawer.content}
- isOpen={isRightDrawerOpen}
- onClose={(e) => !!e && handleDrawerStateChange("right")}
+ isOpen={isRightDrawerOpen ?? false}
+ onClose={(e: any) => !!e && handleDrawerStateChange?.("right")}
/>
)}
@@ -244,7 +268,8 @@ export const Sidebar = ({
className={clsx(
"inline-block h-12 w-12 ",
width > 160 && "h-30 w-30",
- onLogoClick && "cursor-pointer transition-opacity hover:opacity-80",
+ onLogoClick !== undefined &&
+ "cursor-pointer transition-opacity hover:opacity-80",
)}
onClick={onLogoClick}
/>
@@ -260,35 +285,6 @@ export const Sidebar = ({
);
};
-Sidebar.propTypes = {
- logo: PropTypes.node,
- children: PropTypes.node.isRequired,
- className: PropTypes.string,
- footer: PropTypes.element.isRequired,
- isFixed: PropTypes.bool,
- isStickyHeader: PropTypes.bool,
- isStickyFooter: PropTypes.bool,
- onLogoClick: PropTypes.func.isRequired,
- isLeftDrawerOpen: PropTypes.bool,
- isRightDrawerOpen: PropTypes.bool,
- handleDrawerStateChange: PropTypes.func,
- notifications: PropTypes.shape({
- announcements: PropTypes.shape({
- count: PropTypes.number,
- content: PropTypes.node,
- title: PropTypes.string,
- hide: PropTypes.bool,
- onClose: PropTypes.func,
- }),
- notices: PropTypes.shape({
- count: PropTypes.number,
- content: PropTypes.node,
- title: PropTypes.string,
- onClose: PropTypes.func,
- }),
- }),
-};
-
Sidebar.Account = SidebarAccount;
Sidebar.Button = SidebarButton;
Sidebar.Footer = SidebarFooter;
diff --git a/src/components/Sidebar/SidebarScroll.module.css.d.ts b/src/components/Sidebar/SidebarScroll.module.css.d.ts
new file mode 100644
index 000000000..9c7afd5b1
--- /dev/null
+++ b/src/components/Sidebar/SidebarScroll.module.css.d.ts
@@ -0,0 +1,4 @@
+declare const styles: {
+ [key: string]: string;
+};
+export default styles;
diff --git a/src/components/Sidebar/index.js b/src/components/Sidebar/index.js
deleted file mode 100644
index 7ad39439c..000000000
--- a/src/components/Sidebar/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { Sidebar } from "./Sidebar";
diff --git a/src/components/Sidebar/index.ts b/src/components/Sidebar/index.ts
new file mode 100644
index 000000000..7929b8ce7
--- /dev/null
+++ b/src/components/Sidebar/index.ts
@@ -0,0 +1,8 @@
+export { Sidebar } from "./Sidebar";
+export type { SidebarProps } from "./Sidebar";
+export type { SidebarAccountProps } from "./Sidebar.Account";
+export type { SidebarButtonProps } from "./Sidebar.Button";
+export type { SidebarFooterProps } from "./Sidebar.Footer";
+export type { SidebarHeadingProps } from "./Sidebar.Heading";
+export type { SidebarLinkProps, SidebarSeparatorProps } from "./Sidebar.Link";
+export type { SidebarMenuProps } from "./Sidebar.Menu";
diff --git a/src/components/Skeleton.module.css.d.ts b/src/components/Skeleton.module.css.d.ts
new file mode 100644
index 000000000..90696529b
--- /dev/null
+++ b/src/components/Skeleton.module.css.d.ts
@@ -0,0 +1,5 @@
+declare const styles: {
+ shimmer: string;
+};
+
+export default styles;
diff --git a/src/components/Skeleton.jsx b/src/components/Skeleton.tsx
similarity index 60%
rename from src/components/Skeleton.jsx
rename to src/components/Skeleton.tsx
index 1059921a4..2487f9ee8 100644
--- a/src/components/Skeleton.jsx
+++ b/src/components/Skeleton.tsx
@@ -1,9 +1,27 @@
import clsx from "clsx";
-import PropTypes from "prop-types";
import React from "react";
import styles from "./Skeleton.module.css";
-export const Skeleton = ({ style, height = 300, shouldAnimate = true, children, classNames = {}, ...rest }) => {
+export interface SkeletonProps extends React.HTMLAttributes
{
+ style?: React.CSSProperties;
+ height?: number | string;
+ shouldAnimate?: boolean;
+ children?: React.ReactNode;
+ classNames?: {
+ container?: string;
+ shimmer?: string;
+ text?: string;
+ };
+}
+
+export const Skeleton = ({
+ style,
+ height = 300,
+ shouldAnimate = true,
+ children,
+ classNames = {},
+ ...rest
+}: SkeletonProps) => {
return (
);
};
-
-Skeleton.propTypes = {
- style: PropTypes.object,
- height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
- shouldAnimate: PropTypes.bool,
- classNames: PropTypes.object,
- children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
-};
diff --git a/src/components/Spinner.jsx b/src/components/Spinner.tsx
similarity index 59%
rename from src/components/Spinner.jsx
rename to src/components/Spinner.tsx
index 3e72c7c9a..135d021d7 100644
--- a/src/components/Spinner.jsx
+++ b/src/components/Spinner.tsx
@@ -1,5 +1,4 @@
import clsx from "clsx";
-import PropTypes from "prop-types";
import React from "react";
import { CircleNotch } from "../icons";
@@ -10,18 +9,27 @@ const colors = {
success: "text-success",
danger: "text-danger",
caution: "text-caution",
- current: null, // TODO: Consider setting this as the default value.
-};
+ current: null,
+} as const;
const sizes = {
tiny: "w-4 h-4",
small: "w-7 h-7",
medium: "w-10 h-10",
large: "w-14 h-14",
- current: "w-[1em] h-[1em]", // TODO: Consider setting this as the default value.
-};
+ current: "w-[1em] h-[1em]",
+} as const;
+
+type SpinnerColor = keyof typeof colors;
+type SpinnerSize = keyof typeof sizes;
-export const Spinner = ({ className, size = "small", color = "secondary", ...rest }) => {
+export interface SpinnerProps extends React.SVGProps
{
+ size?: SpinnerSize;
+ color?: SpinnerColor;
+ className?: string;
+}
+
+export const Spinner = ({ size = "small", color = "secondary", className, ...rest }: SpinnerProps) => {
return (
);
};
-
-Spinner.propTypes = {
- className: PropTypes.string,
- size: PropTypes.string,
- color: PropTypes.string,
-};
diff --git a/src/components/Table.jsx b/src/components/Table.tsx
similarity index 56%
rename from src/components/Table.jsx
rename to src/components/Table.tsx
index 07ef3a071..276a67fbf 100644
--- a/src/components/Table.jsx
+++ b/src/components/Table.tsx
@@ -1,8 +1,12 @@
import clsx from "clsx";
-import PropTypes from "prop-types";
-import React, { Children, cloneElement, useEffect, useRef, useState } from "react";
+import React, { Children, cloneElement, useEffect, useRef, useState, type ReactElement } from "react";
-export const Table = ({ className, ...rest }) => (
+export interface TableProps extends React.TableHTMLAttributes {
+ children?: React.ReactNode;
+ className?: string;
+}
+
+export const Table = ({ className, ...rest }: TableProps) => (
@@ -14,54 +18,65 @@ export const Table = ({ className, ...rest }) => (
);
-Table.propTypes = {
- className: PropTypes.string,
-};
+export interface TableHeadProps extends React.HTMLAttributes
{
+ children?: React.ReactNode;
+ className?: string;
+}
-Table.Head = ({ className, ...rest }) => {
+const Head = ({ className, ...rest }: TableHeadProps) => {
return ;
};
-Table.Head.displayName = "Table.Head";
-Table.Head.propTypes = {
- className: PropTypes.string,
-};
+Head.displayName = "Table.Head";
+
+export interface TableHeaderProps extends React.ThHTMLAttributes {
+ children?: React.ReactNode;
+ className?: string;
+}
-Table.Header = ({ className, ...rest }) => {
+const Header = ({ className, ...rest }: TableHeaderProps) => {
return | ;
};
-Table.Header.displayName = "Table.Header";
-Table.Header.propTypes = {
- className: PropTypes.string,
-};
+Header.displayName = "Table.Header";
+
+export interface TableBodyProps extends React.HTMLAttributes {
+ isStriped?: boolean;
+ children: React.ReactNode;
+ className?: string;
+}
-Table.Body = ({ className, isStriped = false, children, ...rest }) => {
+const Body = ({ isStriped = false, children, className, ...rest }: TableBodyProps) => {
return (
- {Children.map(children, (child) => child && cloneElement(child, { isStriped }))}
+ {Children.map(children, (child) => {
+ if (!child || !React.isValidElement(child)) return child;
+ return cloneElement(child as ReactElement, { isStriped });
+ })}
);
};
-Table.Body.displayName = "Table.Body";
-Table.Body.propTypes = {
- className: PropTypes.string,
- children: PropTypes.node.isRequired,
- isStriped: PropTypes.bool,
-};
+Body.displayName = "Table.Body";
-Table.Row = ({ isStriped = false, className, ...rest }) => {
+export interface TableRowProps extends React.HTMLAttributes {
+ isStriped?: boolean;
+ children?: React.ReactNode;
+ className?: string;
+}
+
+const Row = ({ isStriped = false, className, ...rest }: TableRowProps) => {
return
;
};
-Table.Row.displayName = "Table.Row";
-Table.Row.propTypes = {
- isStriped: PropTypes.bool,
- className: PropTypes.string,
-};
+Row.displayName = "Table.Row";
-Table.Cell = ({ className, ...rest }) => {
+export interface TableCellProps extends React.TdHTMLAttributes {
+ children?: React.ReactNode;
+ className?: string;
+}
+
+const Cell = ({ className, ...rest }: TableCellProps) => {
return (
{
);
};
-Table.Cell.displayName = "Table.Cell";
-Table.Cell.propTypes = {
- className: PropTypes.string,
-};
+Cell.displayName = "Table.Cell";
-const EditableCell = ({ className, value, onSave, ...rest }) => {
+export interface EditableCellProps extends React.TdHTMLAttributes {
+ value: string;
+ className?: string;
+ onSave: (value: string) => void;
+}
+
+const EditableCell = ({ value, className, onSave, ...rest }: EditableCellProps) => {
const [isEditing, setIsEditing] = useState(false);
const [localValue, setLocalValue] = useState(value);
- const textRef = useRef(null);
- const textAreaRef = useRef(null);
+ const textRef = useRef(null);
+ const textAreaRef = useRef(null);
const [height, setHeight] = useState("auto");
const handleBlur = async () => {
@@ -97,7 +115,7 @@ const EditableCell = ({ className, value, onSave, ...rest }) => {
}
};
- const handleChange = (e) => {
+ const handleChange = (e: React.ChangeEvent) => {
setLocalValue(e.target.value);
e.target.style.height = "auto";
e.target.style.height = `${e.target.scrollHeight}px`;
@@ -105,7 +123,6 @@ const EditableCell = ({ className, value, onSave, ...rest }) => {
useEffect(() => {
if (isEditing) {
- // eslint-disable-next-line no-undef
requestAnimationFrame(() => {
textAreaRef.current?.focus();
});
@@ -147,10 +164,11 @@ const EditableCell = ({ className, value, onSave, ...rest }) => {
);
};
+EditableCell.displayName = "Table.EditableCell";
+
+Table.Head = Head;
+Table.Header = Header;
+Table.Body = Body;
+Table.Row = Row;
+Table.Cell = Cell;
Table.EditableCell = EditableCell;
-Table.EditableCell.displayName = "Table.EditableCell";
-Table.EditableCell.propTypes = {
- className: PropTypes.string,
- value: PropTypes.string.isRequired,
- onSave: PropTypes.func.isRequired,
-};
diff --git a/src/components/Tabs/Tabs.Panel.jsx b/src/components/Tabs/Tabs.Panel.jsx
deleted file mode 100644
index 7c1a1c73e..000000000
--- a/src/components/Tabs/Tabs.Panel.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import clsx from "clsx";
-import PropTypes from "prop-types";
-import React from "react";
-
-export const Panel = ({ className, ...rest }) => {
- return ;
-};
-
-Panel.propTypes = {
- className: PropTypes.string,
-};
-
-Panel.displayName = "Tabs.Panel";
diff --git a/src/components/Tabs/Tabs.Panel.tsx b/src/components/Tabs/Tabs.Panel.tsx
new file mode 100644
index 000000000..5bb3c6ffb
--- /dev/null
+++ b/src/components/Tabs/Tabs.Panel.tsx
@@ -0,0 +1,13 @@
+import clsx from "clsx";
+import React from "react";
+
+export interface PanelProps extends React.HTMLAttributes {
+ children?: React.ReactNode;
+ className?: string;
+}
+
+export const Panel = ({ className, ...rest }: PanelProps) => {
+ return ;
+};
+
+Panel.displayName = "Tabs.Panel";
diff --git a/src/components/Tabs/Tabs.Tab.jsx b/src/components/Tabs/Tabs.Tab.tsx
similarity index 57%
rename from src/components/Tabs/Tabs.Tab.jsx
rename to src/components/Tabs/Tabs.Tab.tsx
index 08b3fa0b9..f8461196f 100644
--- a/src/components/Tabs/Tabs.Tab.jsx
+++ b/src/components/Tabs/Tabs.Tab.tsx
@@ -1,29 +1,41 @@
import clsx from "clsx";
-import PropTypes from "prop-types";
-import React from "react";
+import React, { type ElementType } from "react";
const variants = {
- default: (isActive) =>
+ default: (isActive: boolean) =>
clsx(
"border-b border-gray-light",
isActive ? "text-primary border-l border-r border-b-transparent" : "hover:text-gray-darker",
),
- simple: (isActive) =>
+ simple: (isActive: boolean) =>
clsx(
"flex-1 text-lg transition-colors",
isActive ? "bg-white text-black" : "text-gray-dark hover:text-black hover:bg-gray-light",
),
-};
+} as const;
+
+type TabVariant = keyof typeof variants;
-export const Tab = ({
+export interface TabProps {
+ as?: T;
+ variant?: TabVariant;
+ isActive?: boolean;
+ isHidden?: boolean;
+ children?: React.ReactNode;
+ className?: string;
+}
+
+export const Tab = ({
variant = "default",
- as: Tag = "button",
- className,
+ as,
isActive = false,
isHidden = false,
+ className,
...rest
-}) => {
+}: TabProps & Omit, keyof TabProps>) => {
+ const Tag = as ?? "button";
+
if (isHidden) {
return null;
}
@@ -33,7 +45,7 @@ export const Tab = ({
className={clsx(
"ui-tabs-tab",
className,
- "cursor-pointer whitespace-nowrap py-4 px-8 text-center font-semibold focus-visible:ring",
+ "cursor-pointer whitespace-nowrap px-8 py-4 text-center font-semibold focus-visible:ring",
variants[variant](isActive),
)}
{...rest}
@@ -41,12 +53,4 @@ export const Tab = ({
);
};
-Tab.propTypes = {
- variant: PropTypes.oneOf(Object.keys(variants)),
- as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
- className: PropTypes.string,
- isActive: PropTypes.bool,
- isHidden: PropTypes.bool,
-};
-
Tab.displayName = "Tabs.Tab";
diff --git a/src/components/Tabs/Tabs.jsx b/src/components/Tabs/Tabs.jsx
deleted file mode 100644
index 590ab71d4..000000000
--- a/src/components/Tabs/Tabs.jsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import clsx from "clsx";
-import PropTypes from "prop-types";
-import React, { Children, cloneElement } from "react";
-import { Panel } from "./Tabs.Panel";
-import { Tab } from "./Tabs.Tab";
-
-const variants = {
- default: "border-t border-gray-light",
- simple: "bg-gray-lighter",
-};
-
-export const Tabs = ({ className, variant = "default", value, onChange, children, ...rest }) => {
- const childrenArray = Children.toArray(children);
- const tabs = childrenArray.filter((child) => child.type === Tabs.Tab);
- const panels = childrenArray.filter((child) => child.type === Tabs.Panel);
-
- return (
- <>
-
-
- {panels[value]}
- >
- );
-};
-
-Tabs.propTypes = {
- variant: PropTypes.oneOf(Object.keys(variants)),
- className: PropTypes.string,
- value: PropTypes.number.isRequired,
- onChange: PropTypes.func.isRequired,
- children: PropTypes.node.isRequired,
-};
-
-Tabs.Tab = Tab;
-Tabs.Panel = Panel;
diff --git a/src/components/Tabs/Tabs.tsx b/src/components/Tabs/Tabs.tsx
new file mode 100644
index 000000000..c748cf430
--- /dev/null
+++ b/src/components/Tabs/Tabs.tsx
@@ -0,0 +1,52 @@
+import clsx from "clsx";
+import React, { Children, cloneElement, type ReactElement } from "react";
+import { Panel, type PanelProps } from "./Tabs.Panel";
+import { Tab, type TabProps } from "./Tabs.Tab";
+
+const variants = {
+ default: "border-t border-gray-light",
+ simple: "bg-gray-lighter",
+} as const;
+
+type TabsVariant = keyof typeof variants;
+
+export interface TabsProps extends Omit, "onChange"> {
+ variant?: TabsVariant;
+ value: number;
+ children: React.ReactNode;
+ className?: string;
+ onChange: (index: number) => void;
+}
+
+export const Tabs = ({ variant = "default", value, children, className, onChange, ...rest }: TabsProps) => {
+ const childrenArray = Children.toArray(children);
+ const tabs = childrenArray.filter((child): child is ReactElement => {
+ return React.isValidElement(child) && child.type === Tab;
+ });
+ const panels = childrenArray.filter((child): child is ReactElement => {
+ return React.isValidElement(child) && child.type === Panel;
+ });
+
+ return (
+ <>
+
+
+ {panels[value]}
+ >
+ );
+};
+
+Tabs.Tab = Tab;
+Tabs.Panel = Panel;
diff --git a/src/components/Tabs/index.js b/src/components/Tabs/index.js
deleted file mode 100644
index 471a0b2d6..000000000
--- a/src/components/Tabs/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { Tabs } from "./Tabs";
diff --git a/src/components/Tabs/index.ts b/src/components/Tabs/index.ts
new file mode 100644
index 000000000..e51bb71db
--- /dev/null
+++ b/src/components/Tabs/index.ts
@@ -0,0 +1,4 @@
+export { Tabs } from "./Tabs";
+export type { TabsProps } from "./Tabs";
+export type { TabProps } from "./Tabs.Tab";
+export type { PanelProps } from "./Tabs.Panel";
diff --git a/src/components/Tag.jsx b/src/components/Tag.tsx
similarity index 69%
rename from src/components/Tag.jsx
rename to src/components/Tag.tsx
index 3be2f285f..f608b23ce 100644
--- a/src/components/Tag.jsx
+++ b/src/components/Tag.tsx
@@ -1,5 +1,4 @@
import clsx from "clsx";
-import PropTypes from "prop-types";
import React from "react";
import { CloseIcon } from "../icons";
@@ -10,19 +9,28 @@ const colors = {
warning: "bg-warning-lighter text-black border border-warning",
danger: "bg-danger-lighter text-black border border-danger",
caution: "bg-caution-lighter text-black border border-caution",
-};
+} as const;
const sizes = {
small: "px-1 py-0.75 text-sm leading-3.5",
medium: "px-2 py-1 text-base leading-3.5",
large: "px-2 py-1.5 text-base leading-4",
-};
+} as const;
+
+type TagColor = keyof typeof colors;
+type TagSize = keyof typeof sizes;
-// Dashboard - height 25 Padding 6, 8Purchases - 20 padding 3,4
+export interface TagProps extends React.HTMLAttributes {
+ color?: TagColor;
+ size?: TagSize;
+ children: React.ReactNode;
+ className?: string;
+ onClose?: () => void;
+}
-export const Tag = ({ color = "primary", size = "small", onClose, className, children, ...rest }) => {
- const handleClose = (e) => {
- e.stopPropagation(); // Stop from bubbling to the click handler for the tag itself
+export const Tag = ({ color = "primary", size = "small", children, className, onClose, ...rest }: TagProps) => {
+ const handleClose = (e: React.MouseEvent) => {
+ e.stopPropagation();
onClose?.();
};
@@ -43,11 +51,3 @@ export const Tag = ({ color = "primary", size = "small", onClose, className, chi
);
};
-
-Tag.propTypes = {
- color: PropTypes.oneOf(Object.keys(colors)),
- size: PropTypes.oneOf(Object.keys(sizes)),
- onClose: PropTypes.func,
- className: PropTypes.string,
- children: PropTypes.node.isRequired,
-};
diff --git a/src/components/Tooltip.jsx b/src/components/Tooltip.tsx
similarity index 55%
rename from src/components/Tooltip.jsx
rename to src/components/Tooltip.tsx
index 5ae1108a4..0a75a9712 100644
--- a/src/components/Tooltip.jsx
+++ b/src/components/Tooltip.tsx
@@ -1,20 +1,19 @@
-import Tippy from "@tippyjs/react";
-import PropTypes from "prop-types";
+import Tippy, { type TippyProps } from "@tippyjs/react";
import React from "react";
import { followCursor } from "tippy.js"; // Dont remove this even if unused. It is required for one prop
import "tippy.js/dist/tippy.css"; // If we customize the style, the change this and import our own style
+export interface TooltipProps extends Omit, "content" | "children" | "className"> {
+ content: React.ReactNode; // string, an element, or an array of elements
+ children: React.ReactNode;
+ className?: string;
+}
+
// TODO: Implement "as='div'"
-export const Tooltip = ({ children, className, content, ...rest }) => {
+export const Tooltip = ({ content, children, className, ...rest }: TooltipProps) => {
return (
{children}
);
};
-
-Tooltip.propTypes = {
- content: PropTypes.node.isRequired, // string, an element, or an array of elements
- children: PropTypes.node.isRequired,
- className: PropTypes.string,
-};
diff --git a/src/components/Utilities/Currency.jsx b/src/components/Utilities/Currency.tsx
similarity index 64%
rename from src/components/Utilities/Currency.jsx
rename to src/components/Utilities/Currency.tsx
index f8461512d..07913d91d 100644
--- a/src/components/Utilities/Currency.jsx
+++ b/src/components/Utilities/Currency.tsx
@@ -1,11 +1,20 @@
-import getUserLocale from "get-user-locale";
-import PropTypes from "prop-types";
+import { getUserLocale } from "get-user-locale";
import React from "react";
import { getSymbol, isZeroDecimal } from "../../helpers/currency";
import { almostZero, numberFormat, roundNumber } from "../../helpers/numbers";
const userLocale = getUserLocale();
+export interface CurrencyProps {
+ currency?: string;
+ locale?: string;
+ shouldRemoveTrailingZeroes?: boolean;
+ maximumFractionDigits?: number;
+ compact?: boolean;
+ isNarrowSymbolForm?: boolean;
+ children: number;
+}
+
export const Currency = ({
currency = "USD",
locale = userLocale,
@@ -14,14 +23,14 @@ export const Currency = ({
compact = false,
isNarrowSymbolForm,
children,
-}) => {
+}: CurrencyProps) => {
let amount = children;
if (almostZero(amount)) {
amount = 0;
}
const maxDigits = isZeroDecimal(currency) ? 0 : maximumFractionDigits;
- let formattedAmount = numberFormat(amount, currency, locale, maxDigits, compact);
+ let formattedAmount = numberFormat(amount, currency, locale, maxDigits, compact, isNarrowSymbolForm);
const isNegative = amount < 0;
if (isNegative) {
@@ -32,7 +41,7 @@ export const Currency = ({
return (
{isNegative && "-"}
- {getSymbol(currency, locale, isNarrowSymbolForm)}
+ {getSymbol(currency, locale, 0, isNarrowSymbolForm)}
{formattedAmount}
);
@@ -47,26 +56,27 @@ export const Currency = ({
);
};
-Currency.propTypes = {
- currency: PropTypes.string,
- locale: PropTypes.string,
- shouldRemoveTrailingZeroes: PropTypes.bool,
- maximumFractionDigits: PropTypes.number,
- children: PropTypes.node.isRequired,
-};
-
// TODO: See if this feature can be implemented as a prop on `Currency` component.
-Currency.Round = ({ currency, children }) => {
+interface CurrencyRoundProps {
+ currency?: string;
+ children: number;
+}
+
+const Round = ({ currency = "USD", children }: CurrencyRoundProps) => {
const number = roundNumber(currency, children);
return {number};
};
-Currency.Round.propTypes = {
- currency: PropTypes.string,
- children: PropTypes.node.isRequired,
-};
+Round.displayName = "Currency.Round";
-Currency.Split = ({ currency = "USD", locale = userLocale, isNarrowSymbolForm, children }) => {
+interface CurrencySplitProps {
+ currency?: string;
+ locale?: string;
+ isNarrowSymbolForm?: boolean;
+ children: number;
+}
+
+const Split = ({ currency = "USD", locale = userLocale, isNarrowSymbolForm, children }: CurrencySplitProps) => {
let amount = children;
if (almostZero(amount)) {
amount = 0;
@@ -81,10 +91,10 @@ Currency.Split = ({ currency = "USD", locale = userLocale, isNarrowSymbolForm, c
amountDecimal += "0";
}
- const formattedAmountInt = numberFormat(amountInt, currency, locale, 0, isNarrowSymbolForm);
+ const formattedAmountInt = numberFormat(Number(amountInt), currency, locale, 0, false, isNarrowSymbolForm);
return (
-
+
{formattedAmountInt}
{!isZeroDecimal(currency) && (
@@ -95,9 +105,9 @@ Currency.Split = ({ currency = "USD", locale = userLocale, isNarrowSymbolForm, c
);
};
-Currency.Split.propTypes = {
- currency: PropTypes.string,
- locale: PropTypes.string,
- children: PropTypes.node.isRequired,
- isNarrowSymbolForm: PropTypes.bool,
-};
+Split.displayName = "Currency.Split";
+
+Currency.Round = Round;
+Currency.Split = Split;
+
+export type { CurrencyRoundProps, CurrencySplitProps };
diff --git a/src/components/Utilities/Number.jsx b/src/components/Utilities/Number.jsx
deleted file mode 100644
index 7597be0c7..000000000
--- a/src/components/Utilities/Number.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import getUserLocale from "get-user-locale";
-import PropTypes from "prop-types";
-import React from "react";
-import { numberFormat } from "../../helpers/numbers";
-
-const userLocale = getUserLocale();
-
-export const Number = ({ locale = userLocale, maximumFractionDigits = 2, isCompact = false, children }) => {
- const formattedNumber = numberFormat(children, null, locale, maximumFractionDigits, isCompact);
- return {formattedNumber};
-};
-
-Number.propTypes = {
- locale: PropTypes.string,
- maximumFractionDigits: PropTypes.number,
- isCompact: PropTypes.bool,
- children: PropTypes.node.isRequired,
-};
diff --git a/src/components/Utilities/Number.tsx b/src/components/Utilities/Number.tsx
new file mode 100644
index 000000000..111c9cf6a
--- /dev/null
+++ b/src/components/Utilities/Number.tsx
@@ -0,0 +1,22 @@
+import { getUserLocale } from "get-user-locale";
+import React from "react";
+import { numberFormat } from "../../helpers/numbers";
+
+const userLocale = getUserLocale();
+
+export interface NumberProps {
+ locale?: string;
+ maximumFractionDigits?: number;
+ isCompact?: boolean;
+ children: number;
+}
+
+export const Number = ({
+ locale = userLocale,
+ maximumFractionDigits = 2,
+ isCompact = false,
+ children,
+}: NumberProps) => {
+ const formattedNumber = numberFormat(children, null, locale, maximumFractionDigits, isCompact);
+ return {formattedNumber};
+};
diff --git a/src/components/Utilities/Phone.jsx b/src/components/Utilities/Phone.tsx
similarity index 59%
rename from src/components/Utilities/Phone.jsx
rename to src/components/Utilities/Phone.tsx
index 755b3f916..da242a95c 100644
--- a/src/components/Utilities/Phone.jsx
+++ b/src/components/Utilities/Phone.tsx
@@ -1,17 +1,22 @@
import clsx from "clsx";
-import PropTypes from "prop-types";
import React from "react";
import { formatPhoneNumber, getRegionCode } from "../../helpers/phone";
-export const Phone = ({ countryCode = "US", className, children }) => {
- const number = children;
+export interface PhoneProps {
+ countryCode?: string;
+ children: string | number;
+ className?: string;
+}
+
+export const Phone = ({ countryCode = "US", children, className }: PhoneProps) => {
+ const number = children.toString();
const formattedNumber = formatPhoneNumber(number, countryCode);
const regionCode = getRegionCode(number, countryCode);
return (
@@ -19,9 +24,3 @@ export const Phone = ({ countryCode = "US", className, children }) => {
);
};
-
-Phone.propTypes = {
- countryCode: PropTypes.string,
- className: PropTypes.string,
- children: PropTypes.node.isRequired,
-};
diff --git a/src/helpers/avatar.test.js b/src/helpers/avatar.test.ts
similarity index 81%
rename from src/helpers/avatar.test.js
rename to src/helpers/avatar.test.ts
index f0d26c2d2..cec7cde6d 100644
--- a/src/helpers/avatar.test.js
+++ b/src/helpers/avatar.test.ts
@@ -1,7 +1,8 @@
+import { describe, it, expect } from "vitest";
import { getInitials } from "./avatar";
describe("getInitials", () => {
- test("should get initials from name", () => {
+ it("should get initials from name", () => {
expect(getInitials("Nemanja Krstić")).toBe("NK");
expect(getInitials("Cher")).toBe("C");
expect(getInitials("James Scott Zimmerman")).toBe("JZ");
diff --git a/src/helpers/avatar.js b/src/helpers/avatar.ts
similarity index 70%
rename from src/helpers/avatar.js
rename to src/helpers/avatar.ts
index 4a1b6d738..9d9136d15 100644
--- a/src/helpers/avatar.js
+++ b/src/helpers/avatar.ts
@@ -1,4 +1,4 @@
-export const getInitials = (name) => {
+export const getInitials = (name?: string): string => {
let initials = "N/A";
const pieces = typeof name === "string" && name.match(/\b\w/g);
@@ -6,7 +6,7 @@ export const getInitials = (name) => {
initials = pieces[0];
if (pieces.length > 1) {
- initials += pieces[pieces.length - 1];
+ initials += pieces.at(-1);
}
}
diff --git a/src/helpers/browser.js b/src/helpers/browser.js
deleted file mode 100644
index 9f7f3de94..000000000
--- a/src/helpers/browser.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// eslint-disable-next-line no-undef
-export const isOSX = typeof window === "undefined" ? false : window.navigator.userAgent.includes("Macintosh");
-
-export const isIosBrowser = () => {
- // eslint-disable-next-line no-undef
- if (typeof window === "undefined" || !window.navigator) return false;
-
- // eslint-disable-next-line no-undef
- const ua = window.navigator.userAgent;
- const indexOS = !!/ipad/i.test(ua) || !!/iphone/i.test(ua);
- const webkit = !!/webkit/i.test(ua);
-
- // Additional check for iPad with iOS 13+
- // eslint-disable-next-line no-undef
- const indexPad = !!/macintosh/i.test(ua) && navigator.maxTouchPoints > 1;
-
- return (indexOS && webkit) || indexPad;
-};
diff --git a/src/helpers/browser.ts b/src/helpers/browser.ts
new file mode 100644
index 000000000..adcf49b3b
--- /dev/null
+++ b/src/helpers/browser.ts
@@ -0,0 +1,15 @@
+export const isOSX: boolean = typeof window === "undefined" ? false : window.navigator.userAgent.includes("Macintosh");
+
+export const isIosBrowser = (): boolean => {
+ if (!window?.navigator) {
+ return false;
+ }
+
+ const ua = window.navigator.userAgent;
+ const indexOS = !!/ipad/i.test(ua) || !!/iphone/i.test(ua);
+ const webkit = !!/webkit/i.test(ua);
+
+ const indexPad = !!/macintosh/i.test(ua) && navigator.maxTouchPoints > 1;
+
+ return (indexOS && webkit) || indexPad;
+};
diff --git a/src/helpers/children.js b/src/helpers/children.js
deleted file mode 100644
index d1a69fc2b..000000000
--- a/src/helpers/children.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Children } from "react";
-
-export const getChildrenByType = (children, type) => {
- return Children.toArray(children).filter((child) => child.type === type);
-};
-
-export const getChildByType = (children, type) => {
- return Children.toArray(children).find((child) => child.type === type) ?? null;
-};
diff --git a/src/helpers/children.ts b/src/helpers/children.ts
new file mode 100644
index 000000000..a12232c20
--- /dev/null
+++ b/src/helpers/children.ts
@@ -0,0 +1,9 @@
+import { Children, type ReactNode, type ReactElement, type JSXElementConstructor } from "react";
+
+export const getChildrenByType = (children: ReactNode, type: JSXElementConstructor | string): ReactElement[] => {
+ return Children.toArray(children).filter((child): child is ReactElement => (child as ReactElement).type === type);
+};
+
+export const getChildByType = (children: ReactNode, type: JSXElementConstructor | string): ReactElement | null => {
+ return (Children.toArray(children).find((child) => (child as ReactElement).type === type) as ReactElement) ?? null;
+};
diff --git a/src/helpers/currency.js b/src/helpers/currency.ts
similarity index 51%
rename from src/helpers/currency.js
rename to src/helpers/currency.ts
index 3f01210cc..586625d71 100644
--- a/src/helpers/currency.js
+++ b/src/helpers/currency.ts
@@ -1,14 +1,20 @@
-import getUserLocale from "get-user-locale";
+import * as getUserLocaleModule from "get-user-locale";
+const getUserLocale = getUserLocaleModule.default || getUserLocaleModule;
const userLocale = getUserLocale();
const zeroDecimalCurrencies = new Set(["JPY", "CLP", "KRW", "LAK", "PYG", "VND", "VUV"]);
-export const isZeroDecimal = (currency) => {
+export const isZeroDecimal = (currency: string): boolean => {
return zeroDecimalCurrencies.has(currency);
};
-export const getSymbol = (currency, locale = userLocale, amount = 0, isNarrowSymbolForm = false) => {
+export const getSymbol = (
+ currency: string,
+ locale: string = userLocale,
+ amount = 0,
+ isNarrowSymbolForm = false,
+): string => {
const string = new Intl.NumberFormat(locale, {
style: "currency",
currency,
@@ -16,5 +22,5 @@ export const getSymbol = (currency, locale = userLocale, amount = 0, isNarrowSym
currencyDisplay: isNarrowSymbolForm ? "narrowSymbol" : "symbol",
}).format(amount);
- return string.replace(/\d/g, "").trim();
+ return string.replaceAll(/\d/g, "").trim();
};
diff --git a/src/helpers/date.js b/src/helpers/date.ts
similarity index 70%
rename from src/helpers/date.js
rename to src/helpers/date.ts
index 8ec12435e..c8534ebc2 100644
--- a/src/helpers/date.js
+++ b/src/helpers/date.ts
@@ -1,4 +1,4 @@
-import dayjs, { isDayjs } from "dayjs";
+import dayjs, { type Dayjs, isDayjs } from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat";
import LocalizedFormat from "dayjs/plugin/localizedFormat";
import quarterOfYear from "dayjs/plugin/quarterOfYear";
@@ -13,9 +13,9 @@ dayjs.extend(timezone);
export const DateFormat = {
DATE_ISO: "YYYY-MM-DD",
-};
+} as const;
-export const isValidTimeZoneName = (timezoneName) => {
+export const isValidTimeZoneName = (timezoneName: string): boolean => {
try {
dayjs.tz(new Date(), timezoneName);
} catch {
@@ -25,20 +25,20 @@ export const isValidTimeZoneName = (timezoneName) => {
return true;
};
-export const formatDate = (date, format = DateFormat.DATE_ISO) => {
+export const formatDate = (date: Date | Dayjs | string, format: string = DateFormat.DATE_ISO): string => {
return dayjs(date).format(format);
};
-export const formatTime = (time, format = "h:mm a") => {
- const stringTime = String(time).padStart(4, 0); // 700 to 0700
+export const formatTime = (time: string | number, format = "h:mm a"): string => {
+ const stringTime = String(time).padStart(4, "0");
return dayjs(stringTime, "hhmm").format(format);
};
-export const dateFromObjectId = (id) => {
+export const dateFromObjectId = (id: string): Dayjs => {
return dayjs(new Date(Number.parseInt(id.slice(0, 8), 16) * 1000));
};
-export const now = (date, timezone) => {
+export const now = (date?: Date | Dayjs | string | number, timezone?: string): Dayjs => {
if (!date) {
return timezone ? dayjs().tz(timezone).startOf("day") : dayjs();
}
@@ -64,9 +64,9 @@ export const now = (date, timezone) => {
return timezone ? dayjs().tz(timezone) : dayjs();
};
-const padNumber = (value) => value.toString().padStart(2, "0");
+const padNumber = (value: number): string => value.toString().padStart(2, "0");
-export const dateToString = (date) => {
+export const dateToString = (date: Date): string => {
const dateString = `${date.getFullYear()}-${padNumber(date.getMonth() + 1)}-${padNumber(date.getDate())}`;
const timeString = `${padNumber(date.getHours())}:${padNumber(date.getMinutes())}:${padNumber(date.getSeconds())}`;
return `${dateString} ${timeString}`;
@@ -75,7 +75,7 @@ export const dateToString = (date) => {
const DayTimeStart = "T00:00:00";
const DayTimeEnd = "T23:59:59";
-export const toDate = (date, isStartDate = true) => {
+export const toDate = (date: Date | Dayjs | string, isStartDate = true): Date => {
const suffix = isStartDate ? DayTimeStart : DayTimeEnd;
if (isDayjs(date)) {
@@ -85,9 +85,9 @@ export const toDate = (date, isStartDate = true) => {
return new Date(formatDate(date) + suffix);
};
-export const isSame = (date1, date2, unit = "day") => {
+export const isSame = (date1: Date | Dayjs, date2: Date | Dayjs, unit = "day"): boolean => {
if (isDayjs(date1) && isDayjs(date2)) {
- return date1.isSame(date2, unit);
+ return date1.isSame(date2, unit as any);
}
return false;
diff --git a/src/helpers/numbers.js b/src/helpers/numbers.ts
similarity index 63%
rename from src/helpers/numbers.js
rename to src/helpers/numbers.ts
index ee1dadf98..1a203229e 100644
--- a/src/helpers/numbers.js
+++ b/src/helpers/numbers.ts
@@ -1,25 +1,30 @@
-import getUserLocale from "get-user-locale";
+import * as getUserLocaleModule from "get-user-locale";
import { round } from "lodash";
import { isZeroDecimal } from "./currency";
+const getUserLocale = getUserLocaleModule.default || getUserLocaleModule;
const userLocale = getUserLocale();
-export const almostZero = (number) => {
+export const almostZero = (number: number): boolean => {
const absAmount = Math.abs(number);
return absAmount >= 0 && absAmount <= 0.001;
};
export const numberFormat = (
- amount,
- currency = null,
- locale = userLocale,
+ amount: number,
+ currency: string | null = null,
+ locale: string = userLocale,
maximumFractionDigits = 2,
isCompact = false,
isNarrowSymbolForm = false,
-) => {
+): string => {
const style = currency ? "currency" : "decimal";
- const params = { style, minimumFractionDigits: maximumFractionDigits, maximumFractionDigits };
+ const params: Intl.NumberFormatOptions = {
+ style,
+ minimumFractionDigits: maximumFractionDigits,
+ maximumFractionDigits,
+ };
if (currency) {
params.currency = currency;
@@ -29,7 +34,7 @@ export const numberFormat = (
return isCompact ? compactNumber(amount, locale) : new Intl.NumberFormat(locale, params).format(amount);
};
-export const roundNumber = (currency, amount) => {
+export const roundNumber = (currency: string, amount: number): number => {
let number = Number(amount);
if (isZeroDecimal(currency)) {
@@ -44,6 +49,6 @@ export const roundNumber = (currency, amount) => {
return round(number, 2);
};
-export const compactNumber = (value, locale = userLocale) => {
+export const compactNumber = (value: number, locale: string = userLocale): string => {
return new Intl.NumberFormat(locale, { notation: "compact", maximumFractionDigits: 2 }).format(value);
};
diff --git a/src/helpers/phone.js b/src/helpers/phone.ts
similarity index 86%
rename from src/helpers/phone.js
rename to src/helpers/phone.ts
index 52091c308..4df6340fc 100644
--- a/src/helpers/phone.js
+++ b/src/helpers/phone.ts
@@ -3,7 +3,7 @@ import phoneLib from "google-libphonenumber";
const PNF = phoneLib.PhoneNumberFormat;
const phoneUtil = phoneLib.PhoneNumberUtil.getInstance();
-export const getRegionCode = (number, countryCode = "US") => {
+export const getRegionCode = (number: string, countryCode = "US"): string | undefined => {
try {
const phoneObject = phoneUtil.parseAndKeepRawInput(number, countryCode);
@@ -21,7 +21,7 @@ export const getRegionCode = (number, countryCode = "US") => {
*
* @return {string}
*/
-export const formatPhoneNumber = (number, countryCode = "US") => {
+export const formatPhoneNumber = (number: string, countryCode = "US"): string => {
try {
let phoneObject = phoneUtil.parseAndKeepRawInput(number, countryCode);
@@ -32,7 +32,8 @@ export const formatPhoneNumber = (number, countryCode = "US") => {
}
// Parse number for display in the region's format
- let formattedNumber;
+
+ let formattedNumber: string;
if (regionCode) {
const format = regionCode === countryCode ? PNF.NATIONAL : PNF.INTERNATIONAL;
diff --git a/src/hooks/useId.js b/src/hooks/useId.ts
similarity index 85%
rename from src/hooks/useId.js
rename to src/hooks/useId.ts
index 205adbcd8..869297d7e 100644
--- a/src/hooks/useId.js
+++ b/src/hooks/useId.ts
@@ -2,7 +2,7 @@ import { useContext, useState } from "react";
import { Context } from "../components/Provider";
// TODO: Can be removed once the project is upgraded to React 18
-export const useId = (prefix) => {
+export const useId = (prefix: string): string => {
const { generateId } = useContext(Context);
const [id] = useState(() => `${prefix}-${generateId()}`);
return id;
diff --git a/src/hooks/useIsClient.js b/src/hooks/useIsClient.ts
similarity index 80%
rename from src/hooks/useIsClient.js
rename to src/hooks/useIsClient.ts
index 1211eac19..1978a8fee 100644
--- a/src/hooks/useIsClient.js
+++ b/src/hooks/useIsClient.ts
@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
-export const useIsClient = () => {
+export const useIsClient = (): boolean => {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
diff --git a/src/hooks/useViewportHeight.js b/src/hooks/useViewportHeight.ts
similarity index 58%
rename from src/hooks/useViewportHeight.js
rename to src/hooks/useViewportHeight.ts
index 4ea31c6db..2de790136 100644
--- a/src/hooks/useViewportHeight.js
+++ b/src/hooks/useViewportHeight.ts
@@ -1,24 +1,18 @@
import { useEffect, useState } from "react";
-export const useViewportHeight = () => {
+export const useViewportHeight = (): number => {
const [viewportHeight, setViewportHeight] = useState(0);
useEffect(() => {
const detectViewportHeight = () => {
- // Use window.innerHeight for a more accurate viewport height
- // eslint-disable-next-line no-undef
setViewportHeight(window.innerHeight);
};
- // Detect height on mount
detectViewportHeight();
- // Optional: Re-detect on orientation change for mobile devices
- // eslint-disable-next-line no-undef
window.addEventListener("orientationchange", detectViewportHeight);
return () => {
- // eslint-disable-next-line no-undef
window.removeEventListener("orientationchange", detectViewportHeight);
};
}, []);
diff --git a/src/icons/src/AccountIcon.jsx b/src/icons/src/AccountIcon.tsx
similarity index 100%
rename from src/icons/src/AccountIcon.jsx
rename to src/icons/src/AccountIcon.tsx
diff --git a/src/icons/src/AddNoteIcon.jsx b/src/icons/src/AddNoteIcon.tsx
similarity index 100%
rename from src/icons/src/AddNoteIcon.jsx
rename to src/icons/src/AddNoteIcon.tsx
diff --git a/src/icons/src/AddSquareIcon.jsx b/src/icons/src/AddSquareIcon.tsx
similarity index 100%
rename from src/icons/src/AddSquareIcon.jsx
rename to src/icons/src/AddSquareIcon.tsx
diff --git a/src/icons/src/AnnounceIcon.jsx b/src/icons/src/AnnounceIcon.tsx
similarity index 100%
rename from src/icons/src/AnnounceIcon.jsx
rename to src/icons/src/AnnounceIcon.tsx
diff --git a/src/icons/src/AppIcon.jsx b/src/icons/src/AppIcon.tsx
similarity index 100%
rename from src/icons/src/AppIcon.jsx
rename to src/icons/src/AppIcon.tsx
diff --git a/src/icons/src/ArchiveIcon.jsx b/src/icons/src/ArchiveIcon.tsx
similarity index 100%
rename from src/icons/src/ArchiveIcon.jsx
rename to src/icons/src/ArchiveIcon.tsx
diff --git a/src/icons/src/ArrowCcwIcon.jsx b/src/icons/src/ArrowCcwIcon.tsx
similarity index 100%
rename from src/icons/src/ArrowCcwIcon.jsx
rename to src/icons/src/ArrowCcwIcon.tsx
diff --git a/src/icons/src/ArrowCwIcon.jsx b/src/icons/src/ArrowCwIcon.tsx
similarity index 100%
rename from src/icons/src/ArrowCwIcon.jsx
rename to src/icons/src/ArrowCwIcon.tsx
diff --git a/src/icons/src/ArrowDownIcon.jsx b/src/icons/src/ArrowDownIcon.tsx
similarity index 100%
rename from src/icons/src/ArrowDownIcon.jsx
rename to src/icons/src/ArrowDownIcon.tsx
diff --git a/src/icons/src/ArrowRightIcon.jsx b/src/icons/src/ArrowRightIcon.tsx
similarity index 100%
rename from src/icons/src/ArrowRightIcon.jsx
rename to src/icons/src/ArrowRightIcon.tsx
diff --git a/src/icons/src/ArrowTopRightIcon.jsx b/src/icons/src/ArrowTopRightIcon.tsx
similarity index 100%
rename from src/icons/src/ArrowTopRightIcon.jsx
rename to src/icons/src/ArrowTopRightIcon.tsx
diff --git a/src/icons/src/ArrowUpIcon.jsx b/src/icons/src/ArrowUpIcon.tsx
similarity index 100%
rename from src/icons/src/ArrowUpIcon.jsx
rename to src/icons/src/ArrowUpIcon.tsx
diff --git a/src/icons/src/AtIcon.jsx b/src/icons/src/AtIcon.tsx
similarity index 100%
rename from src/icons/src/AtIcon.jsx
rename to src/icons/src/AtIcon.tsx
diff --git a/src/icons/src/BalloonIcon.jsx b/src/icons/src/BalloonIcon.tsx
similarity index 100%
rename from src/icons/src/BalloonIcon.jsx
rename to src/icons/src/BalloonIcon.tsx
diff --git a/src/icons/src/BankCheckIcon.jsx b/src/icons/src/BankCheckIcon.tsx
similarity index 100%
rename from src/icons/src/BankCheckIcon.jsx
rename to src/icons/src/BankCheckIcon.tsx
diff --git a/src/icons/src/BarGraphIcon.jsx b/src/icons/src/BarGraphIcon.tsx
similarity index 100%
rename from src/icons/src/BarGraphIcon.jsx
rename to src/icons/src/BarGraphIcon.tsx
diff --git a/src/icons/src/BellIcon.jsx b/src/icons/src/BellIcon.tsx
similarity index 100%
rename from src/icons/src/BellIcon.jsx
rename to src/icons/src/BellIcon.tsx
diff --git a/src/icons/src/BookIcon.jsx b/src/icons/src/BookIcon.tsx
similarity index 100%
rename from src/icons/src/BookIcon.jsx
rename to src/icons/src/BookIcon.tsx
diff --git a/src/icons/src/BookmarkIcon.jsx b/src/icons/src/BookmarkIcon.tsx
similarity index 100%
rename from src/icons/src/BookmarkIcon.jsx
rename to src/icons/src/BookmarkIcon.tsx
diff --git a/src/icons/src/BoxIcon.jsx b/src/icons/src/BoxIcon.tsx
similarity index 100%
rename from src/icons/src/BoxIcon.jsx
rename to src/icons/src/BoxIcon.tsx
diff --git a/src/icons/src/BriefcaseIcon.jsx b/src/icons/src/BriefcaseIcon.tsx
similarity index 100%
rename from src/icons/src/BriefcaseIcon.jsx
rename to src/icons/src/BriefcaseIcon.tsx
diff --git a/src/icons/src/ButtonCodeIcon.jsx b/src/icons/src/ButtonCodeIcon.tsx
similarity index 100%
rename from src/icons/src/ButtonCodeIcon.jsx
rename to src/icons/src/ButtonCodeIcon.tsx
diff --git a/src/icons/src/CakeIcon.jsx b/src/icons/src/CakeIcon.tsx
similarity index 100%
rename from src/icons/src/CakeIcon.jsx
rename to src/icons/src/CakeIcon.tsx
diff --git a/src/icons/src/CalendarDayIcon.jsx b/src/icons/src/CalendarDayIcon.tsx
similarity index 100%
rename from src/icons/src/CalendarDayIcon.jsx
rename to src/icons/src/CalendarDayIcon.tsx
diff --git a/src/icons/src/CalendarIcon.jsx b/src/icons/src/CalendarIcon.tsx
similarity index 100%
rename from src/icons/src/CalendarIcon.jsx
rename to src/icons/src/CalendarIcon.tsx
diff --git a/src/icons/src/CalendarListIcon.jsx b/src/icons/src/CalendarListIcon.tsx
similarity index 100%
rename from src/icons/src/CalendarListIcon.jsx
rename to src/icons/src/CalendarListIcon.tsx
diff --git a/src/icons/src/CalendarMonthIcon.jsx b/src/icons/src/CalendarMonthIcon.tsx
similarity index 100%
rename from src/icons/src/CalendarMonthIcon.jsx
rename to src/icons/src/CalendarMonthIcon.tsx
diff --git a/src/icons/src/CalendarWeekIcon.jsx b/src/icons/src/CalendarWeekIcon.tsx
similarity index 100%
rename from src/icons/src/CalendarWeekIcon.jsx
rename to src/icons/src/CalendarWeekIcon.tsx
diff --git a/src/icons/src/CapacityIcon.jsx b/src/icons/src/CapacityIcon.tsx
similarity index 100%
rename from src/icons/src/CapacityIcon.jsx
rename to src/icons/src/CapacityIcon.tsx
diff --git a/src/icons/src/CardAltIcon.jsx b/src/icons/src/CardAltIcon.tsx
similarity index 100%
rename from src/icons/src/CardAltIcon.jsx
rename to src/icons/src/CardAltIcon.tsx
diff --git a/src/icons/src/CardIcon.jsx b/src/icons/src/CardIcon.tsx
similarity index 100%
rename from src/icons/src/CardIcon.jsx
rename to src/icons/src/CardIcon.tsx
diff --git a/src/icons/src/CartIcon.jsx b/src/icons/src/CartIcon.tsx
similarity index 100%
rename from src/icons/src/CartIcon.jsx
rename to src/icons/src/CartIcon.tsx
diff --git a/src/icons/src/CashIcon.jsx b/src/icons/src/CashIcon.tsx
similarity index 100%
rename from src/icons/src/CashIcon.jsx
rename to src/icons/src/CashIcon.tsx
diff --git a/src/icons/src/CheckIcon.jsx b/src/icons/src/CheckIcon.tsx
similarity index 100%
rename from src/icons/src/CheckIcon.jsx
rename to src/icons/src/CheckIcon.tsx
diff --git a/src/icons/src/CheckboxIcon.jsx b/src/icons/src/CheckboxIcon.tsx
similarity index 100%
rename from src/icons/src/CheckboxIcon.jsx
rename to src/icons/src/CheckboxIcon.tsx
diff --git a/src/icons/src/ChecklistIcon.jsx b/src/icons/src/ChecklistIcon.tsx
similarity index 100%
rename from src/icons/src/ChecklistIcon.jsx
rename to src/icons/src/ChecklistIcon.tsx
diff --git a/src/icons/src/ChevronDownIcon.jsx b/src/icons/src/ChevronDownIcon.tsx
similarity index 100%
rename from src/icons/src/ChevronDownIcon.jsx
rename to src/icons/src/ChevronDownIcon.tsx
diff --git a/src/icons/src/ChevronLeftIcon.jsx b/src/icons/src/ChevronLeftIcon.tsx
similarity index 100%
rename from src/icons/src/ChevronLeftIcon.jsx
rename to src/icons/src/ChevronLeftIcon.tsx
diff --git a/src/icons/src/ChevronRightIcon.jsx b/src/icons/src/ChevronRightIcon.tsx
similarity index 100%
rename from src/icons/src/ChevronRightIcon.jsx
rename to src/icons/src/ChevronRightIcon.tsx
diff --git a/src/icons/src/ChevronUpIcon.jsx b/src/icons/src/ChevronUpIcon.tsx
similarity index 100%
rename from src/icons/src/ChevronUpIcon.jsx
rename to src/icons/src/ChevronUpIcon.tsx
diff --git a/src/icons/src/ChipIcon.jsx b/src/icons/src/ChipIcon.tsx
similarity index 100%
rename from src/icons/src/ChipIcon.jsx
rename to src/icons/src/ChipIcon.tsx
diff --git a/src/icons/src/CircleCheckFilledIcon.jsx b/src/icons/src/CircleCheckFilledIcon.tsx
similarity index 100%
rename from src/icons/src/CircleCheckFilledIcon.jsx
rename to src/icons/src/CircleCheckFilledIcon.tsx
diff --git a/src/icons/src/CircleCheckIcon.jsx b/src/icons/src/CircleCheckIcon.tsx
similarity index 100%
rename from src/icons/src/CircleCheckIcon.jsx
rename to src/icons/src/CircleCheckIcon.tsx
diff --git a/src/icons/src/CircleCrossIcon.jsx b/src/icons/src/CircleCrossIcon.tsx
similarity index 100%
rename from src/icons/src/CircleCrossIcon.jsx
rename to src/icons/src/CircleCrossIcon.tsx
diff --git a/src/icons/src/CircleCrownIcon.jsx b/src/icons/src/CircleCrownIcon.tsx
similarity index 100%
rename from src/icons/src/CircleCrownIcon.jsx
rename to src/icons/src/CircleCrownIcon.tsx
diff --git a/src/icons/src/CircleDollarIcon.jsx b/src/icons/src/CircleDollarIcon.tsx
similarity index 100%
rename from src/icons/src/CircleDollarIcon.jsx
rename to src/icons/src/CircleDollarIcon.tsx
diff --git a/src/icons/src/CircleDotIcon.jsx b/src/icons/src/CircleDotIcon.tsx
similarity index 100%
rename from src/icons/src/CircleDotIcon.jsx
rename to src/icons/src/CircleDotIcon.tsx
diff --git a/src/icons/src/CircleInfinityIcon.jsx b/src/icons/src/CircleInfinityIcon.tsx
similarity index 100%
rename from src/icons/src/CircleInfinityIcon.jsx
rename to src/icons/src/CircleInfinityIcon.tsx
diff --git a/src/icons/src/CircleInfoIcon.jsx b/src/icons/src/CircleInfoIcon.tsx
similarity index 100%
rename from src/icons/src/CircleInfoIcon.jsx
rename to src/icons/src/CircleInfoIcon.tsx
diff --git a/src/icons/src/CircleKeyIcon.jsx b/src/icons/src/CircleKeyIcon.tsx
similarity index 100%
rename from src/icons/src/CircleKeyIcon.jsx
rename to src/icons/src/CircleKeyIcon.tsx
diff --git a/src/icons/src/CircleNotch.jsx b/src/icons/src/CircleNotch.tsx
similarity index 79%
rename from src/icons/src/CircleNotch.jsx
rename to src/icons/src/CircleNotch.tsx
index e7a5f22ac..6782206e9 100644
--- a/src/icons/src/CircleNotch.jsx
+++ b/src/icons/src/CircleNotch.tsx
@@ -1,6 +1,6 @@
import React from "react";
-export const CircleNotch = (props) => {
+export const CircleNotch = (props: React.SVGProps) => {
return (
|