[WEB-5459] feat(codemods): add function declaration transformer with tests (#8137)

- Add jscodeshift-based codemod to convert arrow function components to function declarations
- Support React.FC, observer-wrapped, and forwardRef components
- Include comprehensive test suite covering edge cases
- Add npm script to run transformer across codebase
- Target only .tsx files in source directories, excluding node_modules and declaration files

* [WEB-5459] chore: updates after running codemod

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
Aaron 2025-11-20 19:09:40 +07:00 committed by GitHub
parent 90866fb925
commit 83fdebf64d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1771 changed files with 17003 additions and 13856 deletions

View file

@ -15,7 +15,7 @@ export interface AuthConfirmPasswordInputProps
onPasswordMatchChange?: (matches: boolean) => void;
}
export const AuthConfirmPasswordInput: React.FC<AuthConfirmPasswordInputProps> = ({
export function AuthConfirmPasswordInput({
password,
label = "Confirm Password",
error,
@ -27,7 +27,7 @@ export const AuthConfirmPasswordInput: React.FC<AuthConfirmPasswordInputProps> =
onChange,
onPasswordMatchChange,
...props
}) => {
}: AuthConfirmPasswordInputProps) {
const [isFocused, setIsFocused] = useState(false);
const confirmPassword = value as string;
@ -74,4 +74,4 @@ export const AuthConfirmPasswordInput: React.FC<AuthConfirmPasswordInputProps> =
{confirmPassword && passwordsMatch && <p className="text-sm text-green-500">Passwords match</p>}
</div>
);
};
}

View file

@ -8,12 +8,12 @@ export interface AuthForgotPasswordProps {
disabled?: boolean;
}
export const AuthForgotPassword: React.FC<AuthForgotPasswordProps> = ({
export function AuthForgotPassword({
onForgotPassword,
className = "",
text = "Forgot your password?",
disabled = false,
}) => {
}: AuthForgotPasswordProps) {
const handleClick = (e: React.MouseEvent) => {
e.preventDefault();
if (!disabled && onForgotPassword) {
@ -38,4 +38,4 @@ export const AuthForgotPassword: React.FC<AuthForgotPasswordProps> = ({
{text}
</button>
);
};
}

View file

@ -35,7 +35,7 @@ export interface AuthFormProps {
alternateModeButtonText?: string;
}
export const AuthForm: React.FC<AuthFormProps> = ({
export function AuthForm({
mode,
initialData = {},
onSubmit,
@ -52,7 +52,7 @@ export const AuthForm: React.FC<AuthFormProps> = ({
submitButtonText,
alternateModeText,
alternateModeButtonText,
}) => {
}: AuthFormProps) {
const [formData, setFormData] = useState<AuthFormData>({
email: initialData.email || "",
password: initialData.password || "",
@ -204,4 +204,4 @@ export const AuthForm: React.FC<AuthFormProps> = ({
</div>
</form>
);
};
}

View file

@ -13,7 +13,7 @@ export interface AuthInputProps extends Omit<React.InputHTMLAttributes<HTMLInput
const baseContainerClassName = "flex flex-col gap-1.5";
export const AuthInput: React.FC<AuthInputProps> = ({
export function AuthInput({
label,
error,
showPasswordToggle = false,
@ -21,7 +21,7 @@ export const AuthInput: React.FC<AuthInputProps> = ({
className = "",
type = "text",
...props
}) => {
}: AuthInputProps) {
const { id } = props;
const [showPassword, setShowPassword] = useState(false);
const isPasswordType = type === "password";
@ -63,4 +63,4 @@ export const AuthInput: React.FC<AuthInputProps> = ({
{error && <p className={cn("text-sm text-red-500", errorClassName)}>{error}</p>}
</div>
);
};
}

View file

@ -16,7 +16,7 @@ export interface AuthPasswordInputProps extends Omit<React.InputHTMLAttributes<H
onPasswordStrengthChange?: (strength: E_PASSWORD_STRENGTH) => void;
}
export const AuthPasswordInput: React.FC<AuthPasswordInputProps> = ({
export function AuthPasswordInput({
label = "Password",
error,
showPasswordStrength = true,
@ -29,7 +29,7 @@ export const AuthPasswordInput: React.FC<AuthPasswordInputProps> = ({
onPasswordChange,
onPasswordStrengthChange,
...props
}) => {
}: AuthPasswordInputProps) {
const [isFocused, setIsFocused] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
@ -74,4 +74,4 @@ export const AuthPasswordInput: React.FC<AuthPasswordInputProps> = ({
)}
</div>
);
};
}

View file

@ -32,7 +32,7 @@ type Props = {
size?: TAvatarSize;
};
export const AvatarGroup: React.FC<Props> = (props) => {
export function AvatarGroup(props: Props) {
const { children, max = 2, showTooltip = true, size = "md" } = props;
// calculate total length of avatars inside the group
@ -88,4 +88,4 @@ export const AvatarGroup: React.FC<Props> = (props) => {
)}
</div>
);
};
}

View file

@ -113,7 +113,7 @@ export const getBorderRadius = (shape: "circle" | "square") => {
*/
export const isAValidNumber = (value: any) => typeof value === "number" && !isNaN(value);
export const Avatar: React.FC<Props> = (props) => {
export function Avatar(props: Props) {
const {
name,
fallbackBackgroundColor,
@ -166,4 +166,4 @@ export const Avatar: React.FC<Props> = (props) => {
</div>
</Tooltip>
);
};
}

View file

@ -15,7 +15,7 @@ export interface BadgeProps extends React.ButtonHTMLAttributes<HTMLButtonElement
children: React.ReactNode;
}
const Badge = React.forwardRef<HTMLButtonElement, BadgeProps>((props, ref) => {
const Badge = React.forwardRef(function Badge(props: BadgeProps, ref: React.ForwardedRef<HTMLButtonElement>) {
const {
variant = "primary",
size = "md",

View file

@ -19,7 +19,7 @@ type TBreadcrumbBlockProps = {
};
// TODO: remove this component and use web Link component
const BreadcrumbBlock: React.FC<TBreadcrumbBlockProps> = (props) => {
function BreadcrumbBlock(props: TBreadcrumbBlockProps) {
const { label, icon, disableTooltip = false } = props;
return (
@ -30,7 +30,7 @@ const BreadcrumbBlock: React.FC<TBreadcrumbBlockProps> = (props) => {
</Breadcrumbs.ItemWrapper>
</>
);
};
}
export default meta;
type Story = StoryObj<typeof Breadcrumbs>;

View file

@ -10,16 +10,18 @@ type BreadcrumbsProps = {
isLoading?: boolean;
};
export const BreadcrumbItemLoader = () => (
<div className="flex items-center gap-2 h-7 animate-pulse">
<div className="group h-full flex items-center gap-2 rounded px-2 py-1 text-sm font-medium">
<span className="h-full w-5 bg-custom-background-80 rounded" />
<span className="h-full w-16 bg-custom-background-80 rounded" />
export function BreadcrumbItemLoader() {
return (
<div className="flex items-center gap-2 h-7 animate-pulse">
<div className="group h-full flex items-center gap-2 rounded px-2 py-1 text-sm font-medium">
<span className="h-full w-5 bg-custom-background-80 rounded" />
<span className="h-full w-16 bg-custom-background-80 rounded" />
</div>
</div>
</div>
);
);
}
const Breadcrumbs = ({ className, children, onBack, isLoading = false }: BreadcrumbsProps) => {
function Breadcrumbs({ className, children, onBack, isLoading = false }: BreadcrumbsProps) {
const [isSmallScreen, setIsSmallScreen] = React.useState(false);
React.useEffect(() => {
@ -82,7 +84,7 @@ const Breadcrumbs = ({ className, children, onBack, isLoading = false }: Breadcr
{isSmallScreen && childrenArray.length === 1 && childrenArray}
</div>
);
};
}
// breadcrumb item
type BreadcrumbItemProps = {
@ -91,7 +93,7 @@ type BreadcrumbItemProps = {
isLast?: boolean;
};
const BreadcrumbItem: React.FC<BreadcrumbItemProps> = (props) => {
function BreadcrumbItem(props: BreadcrumbItemProps) {
const { component, showSeparator = true, isLast = false } = props;
return (
<div className="flex items-center gap-0.5 h-6">
@ -99,7 +101,7 @@ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = (props) => {
{showSeparator && !isLast && <BreadcrumbSeparator />}
</div>
);
};
}
// breadcrumb icon
type BreadcrumbIconProps = {
@ -107,10 +109,10 @@ type BreadcrumbIconProps = {
className?: string;
};
const BreadcrumbIcon: React.FC<BreadcrumbIconProps> = (props) => {
function BreadcrumbIcon(props: BreadcrumbIconProps) {
const { children, className } = props;
return <div className={cn("flex size-4 items-center justify-start overflow-hidden", className)}>{children}</div>;
};
}
// breadcrumb label
type BreadcrumbLabelProps = {
@ -118,14 +120,14 @@ type BreadcrumbLabelProps = {
className?: string;
};
const BreadcrumbLabel: React.FC<BreadcrumbLabelProps> = (props) => {
function BreadcrumbLabel(props: BreadcrumbLabelProps) {
const { children, className } = props;
return (
<div className={cn("relative line-clamp-1 block max-w-[150px] overflow-hidden truncate", className)}>
{children}
</div>
);
};
}
// breadcrumb separator
type BreadcrumbSeparatorProps = {
@ -135,7 +137,7 @@ type BreadcrumbSeparatorProps = {
showDivider?: boolean;
};
const BreadcrumbSeparator: React.FC<BreadcrumbSeparatorProps> = (props) => {
function BreadcrumbSeparator(props: BreadcrumbSeparatorProps) {
const { className, containerClassName, iconClassName, showDivider = false } = props;
return (
<div className={cn("relative flex items-center justify-center h-full px-1.5 py-1", className)}>
@ -150,7 +152,7 @@ const BreadcrumbSeparator: React.FC<BreadcrumbSeparatorProps> = (props) => {
</div>
</div>
);
};
}
// breadcrumb wrapper
type BreadcrumbItemWrapperProps = {
@ -162,7 +164,7 @@ type BreadcrumbItemWrapperProps = {
isLast?: boolean;
};
const BreadcrumbItemWrapper: React.FC<BreadcrumbItemWrapperProps> = (props) => {
function BreadcrumbItemWrapper(props: BreadcrumbItemWrapperProps) {
const { label, disableTooltip = false, children, className, type = "link", isLast = false } = props;
return (
<Tooltip tooltipContent={label} position="bottom" disabled={!label || label === "" || disableTooltip}>
@ -179,7 +181,7 @@ const BreadcrumbItemWrapper: React.FC<BreadcrumbItemWrapperProps> = (props) => {
</div>
</Tooltip>
);
};
}
Breadcrumbs.Item = BreadcrumbItem;
Breadcrumbs.Icon = BreadcrumbIcon;

View file

@ -17,7 +17,7 @@ type TBreadcrumbNavigationDropdownProps = {
isLast?: boolean;
};
export const BreadcrumbNavigationDropdown = (props: TBreadcrumbNavigationDropdownProps) => {
export function BreadcrumbNavigationDropdown(props: TBreadcrumbNavigationDropdownProps) {
const { selectedItemKey, navigationItems, navigationDisabled = false, handleOnClick, isLast = false } = props;
const [isOpen, setIsOpen] = React.useState(false);
// derived values
@ -29,31 +29,33 @@ export const BreadcrumbNavigationDropdown = (props: TBreadcrumbNavigationDropdow
// if no selected item, return null
if (!selectedItem) return null;
const NavigationButton = () => (
<Tooltip tooltipContent={selectedItem.title} position="bottom" disabled={isOpen}>
<button
onClick={(e) => {
if (!isLast) {
e.preventDefault();
e.stopPropagation();
handleOnClick?.();
}
}}
className={cn(
"group h-full flex items-center gap-2 px-1.5 py-1 text-sm font-medium text-custom-text-300 cursor-pointer rounded rounded-r-none",
{
"hover:bg-custom-background-80 hover:text-custom-text-100": !isLast,
}
)}
>
<div className="flex @4xl:hidden text-custom-text-300">...</div>
<div className="hidden @4xl:flex gap-2">
{selectedItemIcon && <Breadcrumbs.Icon>{selectedItemIcon}</Breadcrumbs.Icon>}
<Breadcrumbs.Label>{selectedItem.title}</Breadcrumbs.Label>
</div>
</button>
</Tooltip>
);
function NavigationButton() {
return (
<Tooltip tooltipContent={selectedItem.title} position="bottom" disabled={isOpen}>
<button
onClick={(e) => {
if (!isLast) {
e.preventDefault();
e.stopPropagation();
handleOnClick?.();
}
}}
className={cn(
"group h-full flex items-center gap-2 px-1.5 py-1 text-sm font-medium text-custom-text-300 cursor-pointer rounded rounded-r-none",
{
"hover:bg-custom-background-80 hover:text-custom-text-100": !isLast,
}
)}
>
<div className="flex @4xl:hidden text-custom-text-300">...</div>
<div className="hidden @4xl:flex gap-2">
{selectedItemIcon && <Breadcrumbs.Icon>{selectedItemIcon}</Breadcrumbs.Icon>}
<Breadcrumbs.Label>{selectedItem.title}</Breadcrumbs.Label>
</div>
</button>
</Tooltip>
);
}
if (navigationDisabled) {
return <NavigationButton />;
@ -133,4 +135,4 @@ export const BreadcrumbNavigationDropdown = (props: TBreadcrumbNavigationDropdow
})}
</CustomMenu>
);
};
}

View file

@ -19,7 +19,7 @@ type TBreadcrumbNavigationSearchDropdownProps = {
shouldTruncate?: boolean;
};
export const BreadcrumbNavigationSearchDropdown: React.FC<TBreadcrumbNavigationSearchDropdownProps> = (props) => {
export function BreadcrumbNavigationSearchDropdown(props: TBreadcrumbNavigationSearchDropdownProps) {
const {
icon,
title,
@ -102,4 +102,4 @@ export const BreadcrumbNavigationSearchDropdown: React.FC<TBreadcrumbNavigationS
)}
/>
);
};
}

View file

@ -15,7 +15,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
children: React.ReactNode;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
const Button = React.forwardRef(function Button(props: ButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) {
const {
variant = "primary",
size = "md",

View file

@ -12,7 +12,7 @@ interface IToggleSwitchProps {
className?: string;
}
const ToggleSwitch: React.FC<IToggleSwitchProps> = (props) => {
function ToggleSwitch(props: IToggleSwitchProps) {
const { value, onChange, label, size = "sm", disabled, className } = props;
return (
@ -49,7 +49,7 @@ const ToggleSwitch: React.FC<IToggleSwitchProps> = (props) => {
/>
</Switch>
);
};
}
ToggleSwitch.displayName = "plane-ui-toggle-switch";

View file

@ -11,7 +11,7 @@ export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
const Card = React.forwardRef<HTMLDivElement, CardProps>((props, ref) => {
const Card = React.forwardRef(function Card(props: CardProps, ref: React.ForwardedRef<HTMLDivElement>) {
const {
variant = ECardVariant.WITH_SHADOW,
direction = ECardDirection.COLUMN,

View file

@ -15,7 +15,7 @@ type Props = {
ChevronIcon?: React.FC<ISvgIcons>;
};
export const CollapsibleButton: FC<Props> = (props) => {
export function CollapsibleButton(props: Props) {
const {
isOpen,
title,
@ -49,4 +49,4 @@ export const CollapsibleButton: FC<Props> = (props) => {
{actionItemElement && isOpen && actionItemElement}
</div>
);
};
}

View file

@ -13,7 +13,7 @@ export type TCollapsibleProps = {
defaultOpen?: boolean;
};
export const Collapsible: FC<TCollapsibleProps> = (props) => {
export function Collapsible(props: TCollapsibleProps) {
const { title, children, buttonRef, className, buttonClassName, isOpen, onToggle, defaultOpen } = props;
// state
const [localIsOpen, setLocalIsOpen] = useState<boolean>(isOpen || defaultOpen ? true : false);
@ -51,4 +51,4 @@ export const Collapsible: FC<TCollapsibleProps> = (props) => {
</Transition>
</Disclosure>
);
};
}

View file

@ -6,7 +6,7 @@ interface ColorPickerProps {
className?: string;
}
export const ColorPicker: React.FC<ColorPickerProps> = (props) => {
export function ColorPicker(props: ColorPickerProps) {
const { value, onChange, className = "" } = props;
// refs
const inputRef = React.useRef<HTMLInputElement>(null);
@ -35,4 +35,4 @@ export const ColorPicker: React.FC<ColorPickerProps> = (props) => {
/>
</div>
);
};
}

View file

@ -11,7 +11,10 @@ export interface ContentWrapperProps extends React.HTMLAttributes<HTMLDivElement
}
const DEFAULT_STYLE = "flex flex-col vertical-scrollbar scrollbar-lg h-full w-full overflow-y-auto";
const ContentWrapper = React.forwardRef<HTMLDivElement, ContentWrapperProps>((props, ref) => {
const ContentWrapper = React.forwardRef(function ContentWrapper(
props: ContentWrapperProps,
ref: React.ForwardedRef<HTMLDivElement>
) {
const { variant = ERowVariant.REGULAR, className = "", children, ...rest } = props;
return (

View file

@ -10,7 +10,10 @@ export type TControlLink = React.AnchorHTMLAttributes<HTMLAnchorElement> & {
draggable?: boolean;
};
export const ControlLink = React.forwardRef<HTMLAnchorElement, TControlLink>((props, ref) => {
export const ControlLink = React.forwardRef(function ControlLink(
props: TControlLink,
ref: React.ForwardedRef<HTMLAnchorElement>
) {
const { href, onClick, children, target = "_blank", disabled = false, className, draggable = false, ...rest } = props;
const LEFT_CLICK_EVENT_CODE = 0;

View file

@ -8,7 +8,10 @@ interface IDragHandle {
disabled?: boolean;
}
export const DragHandle = forwardRef<HTMLButtonElement | null, IDragHandle>((props, ref) => {
export const DragHandle = forwardRef(function DragHandle(
props: IDragHandle,
ref: React.ForwardedRef<HTMLButtonElement | null>
) {
const { className, disabled = false } = props;
if (disabled) {

View file

@ -6,7 +6,7 @@ type Props = {
classNames?: string;
};
export const DropIndicator = (props: Props) => {
export function DropIndicator(props: Props) {
const { isVisible, classNames = "" } = props;
return (
@ -22,4 +22,4 @@ export const DropIndicator = (props: Props) => {
)}
/>
);
};
}

View file

@ -4,7 +4,7 @@ import React, { Fragment } from "react";
import { cn } from "../../utils";
import type { IMultiSelectDropdownButton, ISingleSelectDropdownButton } from "../dropdown";
export const DropdownButton: React.FC<IMultiSelectDropdownButton | ISingleSelectDropdownButton> = (props) => {
export function DropdownButton(props: IMultiSelectDropdownButton | ISingleSelectDropdownButton) {
const {
isOpen,
buttonContent,
@ -34,4 +34,4 @@ export const DropdownButton: React.FC<IMultiSelectDropdownButton | ISingleSelect
</button>
</Combobox.Button>
);
};
}

View file

@ -16,7 +16,7 @@ interface IInputSearch {
isMobile: boolean;
}
export const InputSearch: FC<IInputSearch> = (props) => {
export function InputSearch(props: IInputSearch) {
const { isOpen, query, updateQuery, inputIcon, inputContainerClassName, inputClassName, inputPlaceholder, isMobile } =
props;
@ -58,4 +58,4 @@ export const InputSearch: FC<IInputSearch> = (props) => {
/>
</div>
);
};
}

View file

@ -1,10 +1,12 @@
import { range } from "lodash-es";
import React from "react";
export const DropdownOptionsLoader = () => (
<div className="flex flex-col gap-1 animate-pulse">
{range(6).map((index) => (
<div key={index} className="flex h-[1.925rem] w-full rounded px-1 py-1.5 bg-custom-background-90" />
))}
</div>
);
export function DropdownOptionsLoader() {
return (
<div className="flex flex-col gap-1 animate-pulse">
{range(6).map((index) => (
<div key={index} className="flex h-[1.925rem] w-full rounded px-1 py-1.5 bg-custom-background-90" />
))}
</div>
);
}

View file

@ -8,7 +8,7 @@ import type { IMultiSelectDropdownOptions, ISingleSelectDropdownOptions } from "
// components
import { DropdownOptionsLoader, InputSearch } from ".";
export const DropdownOptions: React.FC<IMultiSelectDropdownOptions | ISingleSelectDropdownOptions> = (props) => {
export function DropdownOptions(props: IMultiSelectDropdownOptions | ISingleSelectDropdownOptions) {
const {
isOpen,
query,
@ -88,4 +88,4 @@ export const DropdownOptions: React.FC<IMultiSelectDropdownOptions | ISingleSele
</div>
</>
);
};
}

View file

@ -12,7 +12,7 @@ import { DropdownButton } from "./common";
import { DropdownOptions } from "./common/options";
import type { IMultiSelectDropdown } from "./dropdown";
export const MultiSelectDropdown: FC<IMultiSelectDropdown> = (props) => {
export function MultiSelectDropdown(props: IMultiSelectDropdown) {
const {
value,
onChange,
@ -165,4 +165,4 @@ export const MultiSelectDropdown: FC<IMultiSelectDropdown> = (props) => {
)}
</Combobox>
);
};
}

View file

@ -12,7 +12,7 @@ import { DropdownButton } from "./common";
import { DropdownOptions } from "./common/options";
import type { ISingleSelectDropdown } from "./dropdown";
export const Dropdown: FC<ISingleSelectDropdown> = (props) => {
export function Dropdown(props: ISingleSelectDropdown) {
const {
value,
onChange,
@ -165,4 +165,4 @@ export const Dropdown: FC<ISingleSelectDropdown> = (props) => {
)}
</Combobox>
);
};
}

View file

@ -17,7 +17,7 @@ type Props = {
children: ReactNode;
};
const ComboDropDown = forwardRef((props: Props, ref) => {
const ComboDropDown = forwardRef(function ComboDropDown(props: Props, ref) {
const { button, renderByDefault = true, children, ...rest } = props;
const dropDownButtonRef = useRef<HTMLDivElement | null>(null);

View file

@ -14,7 +14,7 @@ type ContextMenuItemProps = {
item: TContextMenuItem;
};
export const ContextMenuItem: React.FC<ContextMenuItemProps> = (props) => {
export function ContextMenuItem(props: ContextMenuItemProps) {
const { handleActiveItem, handleClose, isActive, item } = props;
// Nested menu state
@ -243,4 +243,4 @@ export const ContextMenuItem: React.FC<ContextMenuItemProps> = (props) => {
)}
</>
);
};
}

View file

@ -28,7 +28,7 @@ interface PortalProps {
container?: Element | null;
}
export const Portal: React.FC<PortalProps> = ({ children, container }) => {
export function Portal({ children, container }: PortalProps) {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
@ -42,7 +42,7 @@ export const Portal: React.FC<PortalProps> = ({ children, container }) => {
const targetContainer = container || document.body;
return ReactDOM.createPortal(children, targetContainer);
};
}
// Context for managing nested menus
export const ContextMenuContext = React.createContext<{
@ -57,7 +57,7 @@ type ContextMenuProps = {
portalContainer?: Element | null;
};
const ContextMenuWithoutPortal: React.FC<ContextMenuProps> = (props) => {
function ContextMenuWithoutPortal(props: ContextMenuProps) {
const { parentRef, items, portalContainer } = props;
// states
const [isOpen, setIsOpen] = useState(false);
@ -227,11 +227,11 @@ const ContextMenuWithoutPortal: React.FC<ContextMenuProps> = (props) => {
</div>
</div>
);
};
}
export const ContextMenu: React.FC<ContextMenuProps> = (props) => {
export function ContextMenu(props: ContextMenuProps) {
let contextMenu = <ContextMenuWithoutPortal {...props} />;
const portal = document.querySelector("#context-menu-portal");
if (portal) contextMenu = ReactDOM.createPortal(contextMenu, portal);
return contextMenu;
};
}

View file

@ -25,7 +25,7 @@ interface PortalProps {
asChild?: boolean;
}
const Portal: React.FC<PortalProps> = ({ children, container, asChild = false }) => {
function Portal({ children, container, asChild = false }: PortalProps) {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
@ -44,7 +44,7 @@ const Portal: React.FC<PortalProps> = ({ children, container, asChild = false })
}
return ReactDOM.createPortal(<div data-radix-portal="">{children}</div>, targetContainer);
};
}
// Context for main menu to communicate with submenus
const MenuContext = React.createContext<{
@ -52,7 +52,7 @@ const MenuContext = React.createContext<{
registerSubmenu: (closeSubmenu: () => void) => () => void;
} | null>(null);
const CustomMenu = (props: ICustomMenuDropdownProps) => {
function CustomMenu(props: ICustomMenuDropdownProps) {
const {
ariaLabel,
buttonClassName = "",
@ -295,7 +295,7 @@ const CustomMenu = (props: ICustomMenuDropdownProps) => {
)}
</Menu>
);
};
}
// SubMenu context for closing submenu from nested items
const SubMenuContext = React.createContext<{ closeSubmenu: () => void } | null>(null);
@ -304,7 +304,7 @@ const SubMenuContext = React.createContext<{ closeSubmenu: () => void } | null>(
const useSubMenu = () => React.useContext(SubMenuContext);
// SubMenu implementation
const SubMenu: React.FC<ICustomSubMenuProps> = (props) => {
function SubMenu(props: ICustomSubMenuProps) {
const {
children,
trigger,
@ -447,9 +447,9 @@ const SubMenu: React.FC<ICustomSubMenuProps> = (props) => {
)}
</div>
);
};
}
const MenuItem: React.FC<ICustomMenuItemProps> = (props) => {
function MenuItem(props: ICustomMenuItemProps) {
const { children, disabled = false, onClick, className } = props;
const submenuContext = useSubMenu();
@ -479,9 +479,9 @@ const MenuItem: React.FC<ICustomMenuItemProps> = (props) => {
)}
</Menu.Item>
);
};
}
const SubMenuTrigger: React.FC<ICustomSubMenuTriggerProps> = (props) => {
function SubMenuTrigger(props: ICustomSubMenuTriggerProps) {
const { children, disabled = false, className } = props;
return (
@ -505,9 +505,9 @@ const SubMenuTrigger: React.FC<ICustomSubMenuTriggerProps> = (props) => {
)}
</Menu.Item>
);
};
}
const SubMenuContent: React.FC<ICustomSubMenuContentProps> = (props) => {
function SubMenuContent(props: ICustomSubMenuContentProps) {
const { children, className } = props;
return (
@ -520,7 +520,7 @@ const SubMenuContent: React.FC<ICustomSubMenuContentProps> = (props) => {
{children}
</div>
);
};
}
// Add all components as static properties for external use
CustomMenu.Portal = Portal;

View file

@ -12,7 +12,7 @@ import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down";
import { cn } from "../utils";
import type { ICustomSearchSelectProps } from "./helper";
export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
export function CustomSearchSelect(props: ICustomSearchSelectProps) {
const {
customButtonClassName = "",
buttonClassName = "",
@ -223,4 +223,4 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
}}
</Combobox>
);
};
}

View file

@ -13,7 +13,7 @@ import { cn } from "../utils";
// types
import type { ICustomSelectItemProps, ICustomSelectProps } from "./helper";
const CustomSelect = (props: ICustomSelectProps) => {
function CustomSelect(props: ICustomSelectProps) {
const {
customButtonClassName = "",
buttonClassName = "",
@ -131,9 +131,9 @@ const CustomSelect = (props: ICustomSelectProps) => {
)}
</Combobox>
);
};
}
const Option = (props: ICustomSelectItemProps) => {
function Option(props: ICustomSelectItemProps) {
const { children, value, className } = props;
return (
<Combobox.Option
@ -156,7 +156,7 @@ const Option = (props: ICustomSelectItemProps) => {
)}
</Combobox.Option>
);
};
}
CustomSelect.Option = Option;

View file

@ -10,7 +10,7 @@ type Props = {
selected: boolean;
};
export const FavoriteStar: React.FC<Props> = (props) => {
export function FavoriteStar(props: Props) {
const { buttonClassName, iconClassName, onClick, selected } = props;
return (
@ -26,4 +26,4 @@ export const FavoriteStar: React.FC<Props> = (props) => {
/>
</button>
);
};
}

View file

@ -8,7 +8,7 @@ export interface CheckboxProps extends React.InputHTMLAttributes<HTMLInputElemen
indeterminate?: boolean;
}
const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
const Checkbox = React.forwardRef(function Checkbox(props: CheckboxProps, ref: React.ForwardedRef<HTMLInputElement>) {
const {
id,
name,

View file

@ -19,7 +19,7 @@ export interface InputColorPickerProps {
placeholder: string;
}
export const InputColorPicker: React.FC<InputColorPickerProps> = (props) => {
export function InputColorPicker(props: InputColorPickerProps) {
const { value, hasError, onChange, name, className, style, placeholder } = props;
const [referenceElement, setReferenceElement] = React.useState<HTMLButtonElement | null>(null);
@ -111,4 +111,4 @@ export const InputColorPicker: React.FC<InputColorPickerProps> = (props) => {
</Popover>
</div>
);
};
}

View file

@ -10,7 +10,7 @@ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement>
autoComplete?: "on" | "off";
}
const Input = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {
const Input = React.forwardRef(function Input(props: InputProps, ref: React.ForwardedRef<HTMLInputElement>) {
const {
id,
type,

View file

@ -10,11 +10,11 @@ export interface PasswordStrengthIndicatorProps {
isFocused?: boolean;
}
export const PasswordStrengthIndicator: React.FC<PasswordStrengthIndicatorProps> = ({
export function PasswordStrengthIndicator({
password,
showCriteria = true,
isFocused = false,
}) => {
}: PasswordStrengthIndicatorProps) {
const strength = getPasswordStrength(password);
const criteria = getPasswordCriteria(password);
const strengthInfo = getStrengthInfo(strength);
@ -72,4 +72,4 @@ export const PasswordStrengthIndicator: React.FC<PasswordStrengthIndicatorProps>
)}
</div>
);
};
}

View file

@ -13,7 +13,7 @@ interface PasswordInputProps {
error?: boolean;
}
export const PasswordInput: React.FC<PasswordInputProps> = ({
export function PasswordInput({
id,
value,
onChange,
@ -21,7 +21,7 @@ export const PasswordInput: React.FC<PasswordInputProps> = ({
className,
showToggle = true,
error = false,
}) => {
}: PasswordInputProps) {
const [showPassword, setShowPassword] = useState(false);
return (
<div className="relative">
@ -66,4 +66,4 @@ export const PasswordInput: React.FC<PasswordInputProps> = ({
)}
</div>
);
};
}

View file

@ -8,11 +8,13 @@ interface LabelProps {
className?: string;
}
export const Label: React.FC<LabelProps> = ({ htmlFor, children, className }) => (
<label htmlFor={htmlFor} className={cn("block text-sm font-medium text-custom-text-100", className)}>
{children}
</label>
);
export function Label({ htmlFor, children, className }: LabelProps) {
return (
<label htmlFor={htmlFor} className={cn("block text-sm font-medium text-custom-text-100", className)}>
{children}
</label>
);
}
// Reusable Form Field Component
interface FormFieldProps {
@ -23,15 +25,17 @@ interface FormFieldProps {
optional?: boolean;
}
export const FormField: React.FC<FormFieldProps> = ({ label, htmlFor, children, className, optional = false }) => (
<div className={cn("flex flex-col gap-1.5", className)}>
<Label htmlFor={htmlFor}>
{label}
{optional && <span className="text-custom-text-400 text-sm"> (optional)</span>}
</Label>
{children}
</div>
);
export function FormField({ label, htmlFor, children, className, optional = false }: FormFieldProps) {
return (
<div className={cn("flex flex-col gap-1.5", className)}>
<Label htmlFor={htmlFor}>
{label}
{optional && <span className="text-custom-text-400 text-sm"> (optional)</span>}
</Label>
{children}
</div>
);
}
// Reusable Validation Message Component
interface ValidationMessageProps {
@ -40,17 +44,19 @@ interface ValidationMessageProps {
className?: string;
}
export const ValidationMessage: React.FC<ValidationMessageProps> = ({ type, message, className }) => (
<p
className={cn(
"text-sm",
{
"text-red-500": type === "error",
"text-green-500": type === "success",
},
className
)}
>
{message}
</p>
);
export function ValidationMessage({ type, message, className }: ValidationMessageProps) {
return (
<p
className={cn(
"text-sm",
{
"text-red-500": type === "error",
"text-green-500": type === "success",
},
className
)}
>
{message}
</p>
);
}

View file

@ -11,7 +11,10 @@ export interface TextAreaProps extends React.TextareaHTMLAttributes<HTMLTextArea
className?: string;
}
const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>((props, ref) => {
const TextArea = React.forwardRef(function TextArea(
props: TextAreaProps,
ref: React.ForwardedRef<HTMLTextAreaElement>
) {
const {
id,
name,

View file

@ -13,7 +13,8 @@ export interface HeaderProps {
}
const HeaderContext = React.createContext<THeaderVariant | null>(null);
const Header = (props: HeaderProps) => {
function Header(props: HeaderProps) {
const {
variant = EHeaderVariant.PRIMARY,
className = "",
@ -35,19 +36,22 @@ const Header = (props: HeaderProps) => {
</Row>
</HeaderContext.Provider>
);
};
}
const LeftItem = (props: HeaderProps) => (
<div
className={cn(
"flex flex-wrap items-center gap-2 overflow-ellipsis whitespace-nowrap max-w-[80%] flex-grow",
props.className
)}
>
{props.children}
</div>
);
const RightItem = (props: HeaderProps) => {
function LeftItem(props: HeaderProps) {
return (
<div
className={cn(
"flex flex-wrap items-center gap-2 overflow-ellipsis whitespace-nowrap max-w-[80%] flex-grow",
props.className
)}
>
{props.children}
</div>
);
}
function RightItem(props: HeaderProps) {
const variant = React.useContext(HeaderContext);
if (variant === undefined) throw new Error("RightItem must be used within Header");
return (
@ -63,7 +67,7 @@ const RightItem = (props: HeaderProps) => {
{props.children}
</div>
);
};
}
Header.LeftItem = LeftItem;
Header.RightItem = RightItem;

View file

@ -14,7 +14,7 @@ export type TLinkItemBlockProps = {
onClick?: () => void;
};
export const LinkItemBlock: FC<TLinkItemBlockProps> = (props) => {
export function LinkItemBlock(props: TLinkItemBlockProps) {
// props
const { title, url, createdAt, menuItems, onClick } = props;
// icons
@ -67,4 +67,4 @@ export const LinkItemBlock: FC<TLinkItemBlockProps> = (props) => {
)}
</div>
);
};
}

View file

@ -7,11 +7,13 @@ type Props = {
className?: string;
};
const Loader = ({ children, className = "" }: Props) => (
<div className={cn("animate-pulse", className)} role="status">
{children}
</div>
);
function Loader({ children, className = "" }: Props) {
return (
<div className={cn("animate-pulse", className)} role="status">
{children}
</div>
);
}
type ItemProps = {
height?: string;
@ -19,9 +21,11 @@ type ItemProps = {
className?: string;
};
const Item: React.FC<ItemProps> = ({ height = "auto", width = "auto", className = "" }) => (
<div className={cn("rounded-md bg-custom-background-80", className)} style={{ height: height, width: width }} />
);
function Item({ height = "auto", width = "auto", className = "" }: ItemProps) {
return (
<div className={cn("rounded-md bg-custom-background-80", className)} style={{ height: height, width: width }} />
);
}
Loader.Item = Item;

View file

@ -45,7 +45,7 @@ const VARIANT_CLASSES: Record<TModalVariant, string> = {
primary: "bg-custom-primary-100/20 text-custom-primary-100",
};
export const AlertModalCore: React.FC<Props> = (props) => {
export function AlertModalCore(props: Props) {
const {
content,
handleClose,
@ -94,4 +94,4 @@ export const AlertModalCore: React.FC<Props> = (props) => {
</div>
</ModalCore>
);
};
}

View file

@ -13,7 +13,7 @@ type Props = {
width?: EModalWidth;
className?: string;
};
export const ModalCore: React.FC<Props> = (props) => {
export function ModalCore(props: Props) {
const {
children,
handleClose,
@ -64,4 +64,4 @@ export const ModalCore: React.FC<Props> = (props) => {
</Dialog>
</Transition.Root>
);
};
}

View file

@ -7,7 +7,10 @@ export interface OAuthButtonProps extends React.ButtonHTMLAttributes<HTMLButtonE
compact?: boolean;
}
const OAuthButton = React.forwardRef<HTMLButtonElement, OAuthButtonProps>((props, ref) => {
const OAuthButton = React.forwardRef(function OAuthButton(
props: OAuthButtonProps,
ref: React.ForwardedRef<HTMLButtonElement>
) {
const { text, icon, compact = false, className = "", ...rest } = props;
return (

View file

@ -18,7 +18,7 @@ type OAuthOptionsProps = {
containerClassName?: string;
};
export const OAuthOptions = (props: OAuthOptionsProps) => {
export function OAuthOptions(props: OAuthOptionsProps) {
const { options, compact = false, className = "", containerClassName = "" } = props;
// Filter enabled options
@ -54,4 +54,4 @@ export const OAuthOptions = (props: OAuthOptionsProps) => {
</div>
</div>
);
};
}

View file

@ -6,7 +6,7 @@ import { Popover } from "./popover";
// types
import type { TPopoverMenu } from "./types";
export const PopoverMenu = <T,>(props: TPopoverMenu<T>) => {
export function PopoverMenu<T>(props: TPopoverMenu<T>) {
const {
popperPosition = "bottom-end",
popperPadding = 0,
@ -42,4 +42,4 @@ export const PopoverMenu = <T,>(props: TPopoverMenu<T>) => {
</Fragment>
</Popover>
);
};
}

View file

@ -8,7 +8,7 @@ import { cn } from "../utils";
// types
import type { TPopover } from "./types";
export const Popover = (props: TPopover) => {
export function Popover(props: TPopover) {
const {
popperPosition = "bottom-end",
popperPadding = 0,
@ -76,4 +76,4 @@ export const Popover = (props: TPopover) => {
</Transition>
</HeadlessReactPopover>
);
};
}

View file

@ -8,7 +8,7 @@ interface ICircularProgressIndicator {
children?: React.ReactNode;
}
export const CircularProgressIndicator: React.FC<ICircularProgressIndicator> = (props) => {
export function CircularProgressIndicator(props: ICircularProgressIndicator) {
const { size = 40, percentage = 25, strokeWidth = 6, strokeColor = "stroke-custom-primary-100", children } = props;
const sqSize = size;
@ -81,4 +81,4 @@ export const CircularProgressIndicator: React.FC<ICircularProgressIndicator> = (
</div>
</div>
);
};
}

View file

@ -11,14 +11,14 @@ type Props = {
barClassName?: string;
};
export const LinearProgressIndicator: React.FC<Props> = ({
export function LinearProgressIndicator({
data,
noTooltip = false,
inPercentage = false,
size = "sm",
className = "",
barClassName = "",
}) => {
}: Props) {
const total = data.reduce((acc: any, cur: any) => acc + cur.value, 0);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let progress = 0;
@ -54,4 +54,4 @@ export const LinearProgressIndicator: React.FC<Props> = ({
</div>
</div>
);
};
}

View file

@ -9,14 +9,14 @@ type Props = {
inactiveStrokeColor?: string;
};
export const ProgressBar: React.FC<Props> = ({
export function ProgressBar({
maxValue = 0,
value = 0,
radius = 8,
strokeWidth = 2,
activeStrokeColor = "#3e98c7",
inactiveStrokeColor = "#ddd",
}) => {
}: Props) {
// PIE Calc Fn
const generatePie = (value: any) => {
const x = radius - Math.cos((2 * Math.PI) / (100 / value)) * radius;
@ -65,4 +65,4 @@ export const ProgressBar: React.FC<Props> = ({
<circle r={radius - strokeWidth} cx={radius} cy={radius} className="progress-bar" />
</svg>
);
};
}

View file

@ -5,7 +5,7 @@ interface IRadialProgressBar {
progress: number;
}
export const RadialProgressBar: FC<IRadialProgressBar> = (props) => {
export function RadialProgressBar(props: IRadialProgressBar) {
const { progress } = props;
const [circumference, setCircumference] = useState(0);
@ -43,4 +43,4 @@ export const RadialProgressBar: FC<IRadialProgressBar> = (props) => {
</svg>
</div>
);
};
}

View file

@ -9,7 +9,7 @@ export interface RowProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
const Row = React.forwardRef<HTMLDivElement, RowProps>((props, ref) => {
const Row = React.forwardRef(function Row(props: RowProps, ref: React.ForwardedRef<HTMLDivElement>) {
const { variant = ERowVariant.REGULAR, className = "", children, ...rest } = props;
const style = rowStyle[variant];

View file

@ -24,7 +24,7 @@ const thumbSizeStyles = {
lg: "before:absolute before:left-1/2 before:top-1/2 before:size-full before:min-h-17 before:min-w-17 before:-translate-x-1/2 before:-translate-y-1/2",
};
export const ScrollArea: FC<TScrollAreaProps> = (props) => {
export function ScrollArea(props: TScrollAreaProps) {
const { type = "always", className = "", scrollHideDelay = 600, size = "md", children } = props;
return (
@ -64,4 +64,4 @@ export const ScrollArea: FC<TScrollAreaProps> = (props) => {
</RadixScrollArea.Scrollbar>
</RadixScrollArea.Root>
);
};
}

View file

@ -20,7 +20,8 @@ type Props = {
data: any; //@todo make this generic
className?: string;
};
const Draggable = ({ children, data, className }: Props) => {
function Draggable({ children, data, className }: Props) {
const ref = useRef<HTMLDivElement>(null);
const [dragging, setDragging] = useState<boolean>(false); // NEW
const [isDraggedOver, setIsDraggedOver] = useState(false);
@ -69,6 +70,6 @@ const Draggable = ({ children, data, className }: Props) => {
{<DropIndicator isVisible={isDraggedOver && closestEdge === "bottom"} />}
</div>
);
};
}
export { Draggable };

View file

@ -59,7 +59,7 @@ const moveItem = <T,>(
};
};
export const Sortable = <T,>({ data, render, onChange, keyExtractor, containerClassName, id }: Props<T>) => {
export function Sortable<T>({ data, render, onChange, keyExtractor, containerClassName, id }: Props<T>) {
useEffect(() => {
const unsubscribe = monitorForElements({
// @ts-expect-error Due to live server dependencies
@ -100,6 +100,6 @@ export const Sortable = <T,>({ data, render, onChange, keyExtractor, containerCl
))}
</>
);
};
}
export default Sortable;

View file

@ -6,30 +6,28 @@ interface ICircularBarSpinner extends React.SVGAttributes<SVGElement> {
className?: string | undefined;
}
export const CircularBarSpinner: React.FC<ICircularBarSpinner> = ({
height = "16px",
width = "16px",
className = "",
}) => (
<div role="status">
<svg xmlns="http://www.w3.org/2000/svg" width={width} height={height} viewBox="0 0 24 24" className={className}>
<g>
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.14} />
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.29} transform="rotate(30 12 12)" />
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.43} transform="rotate(60 12 12)" />
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.57} transform="rotate(90 12 12)" />
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.71} transform="rotate(120 12 12)" />
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.86} transform="rotate(150 12 12)" />
<rect width={2} height={5} x={11} y={1} fill="currentColor" transform="rotate(180 12 12)" />
<animateTransform
attributeName="transform"
calcMode="discrete"
dur="0.75s"
repeatCount="indefinite"
type="rotate"
values="0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12;360 12 12"
/>
</g>
</svg>
</div>
);
export function CircularBarSpinner({ height = "16px", width = "16px", className = "" }: ICircularBarSpinner) {
return (
<div role="status">
<svg xmlns="http://www.w3.org/2000/svg" width={width} height={height} viewBox="0 0 24 24" className={className}>
<g>
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.14} />
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.29} transform="rotate(30 12 12)" />
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.43} transform="rotate(60 12 12)" />
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.57} transform="rotate(90 12 12)" />
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.71} transform="rotate(120 12 12)" />
<rect width={2} height={5} x={11} y={1} fill="currentColor" opacity={0.86} transform="rotate(150 12 12)" />
<rect width={2} height={5} x={11} y={1} fill="currentColor" transform="rotate(180 12 12)" />
<animateTransform
attributeName="transform"
calcMode="discrete"
dur="0.75s"
repeatCount="indefinite"
type="rotate"
values="0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12;360 12 12"
/>
</g>
</svg>
</div>
);
}

View file

@ -8,26 +8,28 @@ export interface ISpinner extends React.SVGAttributes<SVGElement> {
className?: string | undefined;
}
export const Spinner: React.FC<ISpinner> = ({ height = "32px", width = "32px", className = "" }) => (
<div role="status">
<svg
aria-hidden="true"
height={height}
width={width}
className={cn("animate-spin fill-custom-primary-100 text-custom-text-200", className)}
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
<span className="sr-only">Loading...</span>
</div>
);
export function Spinner({ height = "32px", width = "32px", className = "" }: ISpinner) {
return (
<div role="status">
<svg
aria-hidden="true"
height={height}
width={width}
className={cn("animate-spin fill-custom-primary-100 text-custom-text-200", className)}
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
<span className="sr-only">Loading...</span>
</div>
);
}

View file

@ -4,7 +4,7 @@ import { cn } from "../utils";
// types
import type { TTableData } from "./types";
export const Table = <T,>(props: TTableData<T>) => {
export function Table<T>(props: TTableData<T>) {
const {
data,
columns,
@ -45,4 +45,4 @@ export const Table = <T,>(props: TTableData<T>) => {
</tbody>
</table>
);
};
}

View file

@ -22,51 +22,53 @@ type TTabListProps = {
onTabChange?: (key: string) => void;
};
export const TabList: FC<TTabListProps> = ({
export function TabList({
tabs,
tabListClassName,
tabClassName,
size = "md",
selectedTab,
onTabChange,
}) => (
<Tab.List
as="div"
className={cn(
"flex w-full min-w-fit items-center justify-between gap-1.5 rounded-md text-sm p-0.5 bg-custom-background-80/60",
tabListClassName
)}
>
{tabs.map((tab) => (
<Tab
className={({ selected }) =>
cn(
"flex items-center justify-center p-1 min-w-fit w-full font-medium text-custom-text-100 outline-none focus:outline-none cursor-pointer transition-all rounded",
(selectedTab ? selectedTab === tab.key : selected)
? "bg-custom-background-100 text-custom-text-100 shadow-sm"
: tab.disabled
? "text-custom-text-400 cursor-not-allowed"
: "text-custom-text-400 hover:text-custom-text-300 hover:bg-custom-background-80/60",
{
"text-xs": size === "sm",
"text-sm": size === "md",
"text-base": size === "lg",
},
tabClassName
)
}
key={tab.key}
onClick={() => {
if (!tab.disabled) {
onTabChange?.(tab.key);
tab.onClick?.();
}: TTabListProps) {
return (
<Tab.List
as="div"
className={cn(
"flex w-full min-w-fit items-center justify-between gap-1.5 rounded-md text-sm p-0.5 bg-custom-background-80/60",
tabListClassName
)}
>
{tabs.map((tab) => (
<Tab
className={({ selected }) =>
cn(
"flex items-center justify-center p-1 min-w-fit w-full font-medium text-custom-text-100 outline-none focus:outline-none cursor-pointer transition-all rounded",
(selectedTab ? selectedTab === tab.key : selected)
? "bg-custom-background-100 text-custom-text-100 shadow-sm"
: tab.disabled
? "text-custom-text-400 cursor-not-allowed"
: "text-custom-text-400 hover:text-custom-text-300 hover:bg-custom-background-80/60",
{
"text-xs": size === "sm",
"text-sm": size === "md",
"text-base": size === "lg",
},
tabClassName
)
}
}}
disabled={tab.disabled}
>
{tab.icon && <tab.icon className="size-4" />}
{tab.label}
</Tab>
))}
</Tab.List>
);
key={tab.key}
onClick={() => {
if (!tab.disabled) {
onTabChange?.(tab.key);
tab.onClick?.();
}
}}
disabled={tab.disabled}
>
{tab.icon && <tab.icon className="size-4" />}
{tab.label}
</Tab>
))}
</Tab.List>
);
}

View file

@ -28,7 +28,7 @@ type TTabsProps = {
storeInLocalStorage?: boolean;
};
export const Tabs: FC<TTabsProps> = (props: TTabsProps) => {
export function Tabs(props: TTabsProps) {
const {
tabs,
storageKey,
@ -87,4 +87,4 @@ export const Tabs: FC<TTabsProps> = (props: TTabsProps) => {
</Tab.Group>
</div>
);
};
}

View file

@ -10,7 +10,7 @@ export interface TagProps extends React.ComponentProps<"div"> {
children: React.ReactNode;
}
const Tag = React.forwardRef<HTMLDivElement, TagProps>((props, ref) => {
const Tag = React.forwardRef(function Tag(props: TagProps, ref: React.ForwardedRef<HTMLDivElement>) {
const { variant = ETagVariant.OUTLINED, className = "", size = ETagSize.SM, children, ...rest } = props;
const style = getTagStyle(variant, size);

View file

@ -33,7 +33,7 @@ interface ITooltipProps {
renderByDefault?: boolean;
}
export const Tooltip: React.FC<ITooltipProps> = ({
export function Tooltip({
tooltipHeading,
tooltipContent,
position = "top",
@ -43,8 +43,10 @@ export const Tooltip: React.FC<ITooltipProps> = ({
openDelay = 200,
closeDelay,
isMobile = false,
renderByDefault = true, //FIXME: tooltip should always render on hover and not by default, this is a temporary fix
}) => {
//FIXME: tooltip should always render on hover and not by default, this is a temporary fix
renderByDefault = true,
}: ITooltipProps) {
const toolTipRef = useRef<HTMLDivElement | null>(null);
const [shouldRender, setShouldRender] = useState(renderByDefault);
@ -107,4 +109,4 @@ export const Tooltip: React.FC<ITooltipProps> = ({
}
/>
);
};
}

View file

@ -6,10 +6,13 @@ type Props = {
className?: string;
noMargin?: boolean;
};
const SubHeading = ({ children, className, noMargin }: Props) => (
<h3 className={cn("text-xl font-medium text-custom-text-200 block leading-7", !noMargin && "mb-2", className)}>
{children}
</h3>
);
function SubHeading({ children, className, noMargin }: Props) {
return (
<h3 className={cn("text-xl font-medium text-custom-text-200 block leading-7", !noMargin && "mb-2", className)}>
{children}
</h3>
);
}
export { SubHeading };