refactor: remove React.FC for component package (#3575)

This commit is contained in:
Garfield Lee
2023-08-04 23:00:28 +08:00
committed by GitHub
parent 7ec4b8fb8c
commit 65fc0ed59c
21 changed files with 165 additions and 149 deletions

View File

@@ -10,7 +10,6 @@ import {
} from '@blocksuite/icons';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import { useStaticBlockSuiteWorkspace } from '@toeverything/infra/__internal__/react';
import type { FC } from 'react';
import { useCallback } from 'react';
import { WorkspaceAvatar } from '../../workspace-avatar';
@@ -21,9 +20,6 @@ import {
StyleWorkspaceTitle,
} from './styles';
export type WorkspaceTypeProps = {
flavour: WorkspaceFlavour;
};
const JoinedWorkspaceIcon = () => {
return <DefaultJoinedWorkspaceIcon style={{ color: '#FF646B' }} />;
};
@@ -39,7 +35,11 @@ const LocalDataIcon = () => {
return <DefaultLocalDataIcon style={{ color: '#62CD80' }} />;
};
const WorkspaceType: FC<WorkspaceTypeProps> = ({ flavour }) => {
export interface WorkspaceTypeProps {
flavour: WorkspaceFlavour;
}
const WorkspaceType = ({ flavour }: WorkspaceTypeProps) => {
const t = useAFFiNEI18N();
// fixme: cloud regression
const isOwner = true;
@@ -66,19 +66,19 @@ const WorkspaceType: FC<WorkspaceTypeProps> = ({ flavour }) => {
);
};
export type WorkspaceCardProps = {
export interface WorkspaceCardProps {
currentWorkspaceId: string | null;
meta: RootWorkspaceMetadata;
onClick: (workspaceId: string) => void;
onSettingClick: (workspaceId: string) => void;
};
}
export const WorkspaceCard: FC<WorkspaceCardProps> = ({
export const WorkspaceCard = ({
onClick,
onSettingClick,
currentWorkspaceId,
meta,
}) => {
}: WorkspaceCardProps) => {
const t = useAFFiNEI18N();
const workspace = useStaticBlockSuiteWorkspace(meta.id);
const [name] = useBlockSuiteWorkspaceName(workspace);

View File

@@ -1,10 +1,9 @@
import { lottieAtom } from '@affine/jotai';
import { useAtomValue } from 'jotai';
import type { AnimationItem } from 'lottie-web';
import type { FC } from 'react';
import { useEffect, useRef } from 'react';
type CustomLottieProps = {
interface CustomLottieProps {
options: {
loop?: boolean | number | undefined;
autoReverse?: boolean | undefined;
@@ -18,15 +17,15 @@ type CustomLottieProps = {
speed?: number | undefined;
width?: number | string | undefined;
height?: number | string | undefined;
};
}
export const InternalLottie: FC<CustomLottieProps> = ({
export const InternalLottie = ({
options,
isStopped,
speed,
width,
height,
}) => {
}: CustomLottieProps) => {
const element = useRef<HTMLDivElement>(null);
const lottieInstance = useRef<AnimationItem>();
const lottie = useAtomValue(lottieAtom);

View File

@@ -28,6 +28,17 @@ import type { ListData, PageListProps, TrashListData } from './type';
import { useSorter } from './use-sorter';
import { formatDate, useIsSmallDevices } from './utils';
interface AllPagesHeadProps {
isPublicWorkspace: boolean;
sorter: ReturnType<typeof useSorter<ListData>>;
createNewPage: () => void;
createNewEdgeless: () => void;
importFile: () => void;
getPageInfo: GetPageInfoById;
propertiesMeta: PropertiesMeta;
workspaceId: string;
}
const AllPagesHead = ({
isPublicWorkspace,
sorter,
@@ -37,16 +48,7 @@ const AllPagesHead = ({
getPageInfo,
propertiesMeta,
workspaceId,
}: {
isPublicWorkspace: boolean;
sorter: ReturnType<typeof useSorter<ListData>>;
createNewPage: () => void;
createNewEdgeless: () => void;
importFile: () => void;
getPageInfo: GetPageInfoById;
propertiesMeta: PropertiesMeta;
workspaceId: string;
}) => {
}: AllPagesHeadProps) => {
const t = useAFFiNEI18N();
const titleList = useMemo(
() => [
@@ -235,10 +237,15 @@ const TrashListHead = () => {
);
};
export const PageListTrashView: React.FC<{
interface PageListTrashViewProps {
list: TrashListData[];
fallback?: React.ReactNode;
}> = ({ list, fallback }) => {
}
export const PageListTrashView = ({
list,
fallback,
}: PageListTrashViewProps) => {
const t = useAFFiNEI18N();
const theme = useTheme();

View File

@@ -8,7 +8,6 @@ import {
OpenInNewIcon,
ResetIcon,
} from '@blocksuite/icons';
import type React from 'react';
import { useState } from 'react';
import {
@@ -21,7 +20,7 @@ import {
} from '../../..';
import { DisablePublicSharing, MoveToTrash } from './operation-menu-items';
export type OperationCellProps = {
export interface OperationCellProps {
title: string;
favorite: boolean;
isPublic: boolean;
@@ -29,9 +28,9 @@ export type OperationCellProps = {
onToggleFavoritePage: () => void;
onRemoveToTrash: () => void;
onDisablePublicSharing: () => void;
};
}
export const OperationCell: React.FC<OperationCellProps> = ({
export const OperationCell = ({
title,
favorite,
isPublic,
@@ -39,7 +38,7 @@ export const OperationCell: React.FC<OperationCellProps> = ({
onToggleFavoritePage,
onRemoveToTrash,
onDisablePublicSharing,
}) => {
}: OperationCellProps) => {
const t = useAFFiNEI18N();
const [open, setOpen] = useState(false);
const [openDisableShared, setOpenDisableShared] = useState(false);
@@ -118,16 +117,16 @@ export const OperationCell: React.FC<OperationCellProps> = ({
);
};
export type TrashOperationCellProps = {
export interface TrashOperationCellProps {
onPermanentlyDeletePage: () => void;
onRestorePage: () => void;
onOpenPage: () => void;
};
}
export const TrashOperationCell: React.FC<TrashOperationCellProps> = ({
export const TrashOperationCell = ({
onPermanentlyDeletePage,
onRestorePage,
}) => {
}: TrashOperationCellProps) => {
const t = useAFFiNEI18N();
const [open, setOpen] = useState(false);
return (

View File

@@ -10,7 +10,7 @@ import { ProviderComposer } from '..';
test('ProviderComposer', async () => {
const Context = createContext('null');
const Provider: React.FC<React.PropsWithChildren> = ({ children }) => {
const Provider = ({ children }: React.PropsWithChildren) => {
return <Context.Provider value="test1">{children}</Context.Provider>;
};
const ConsumerComponent = () => {

View File

@@ -1,11 +1,15 @@
import type { FC, PropsWithChildren, ReactNode } from 'react';
import type { ReactNode } from 'react';
import { cloneElement } from 'react';
export const ProviderComposer: FC<
PropsWithChildren<{
contexts: any;
}>
> = ({ contexts, children }) =>
interface ProviderComposerProps {
contexts: any;
children: ReactNode;
}
export const ProviderComposer = ({
contexts,
children,
}: ProviderComposerProps) =>
contexts.reduceRight(
(kids: ReactNode, parent: any) =>
cloneElement(parent, {

View File

@@ -1,17 +1,17 @@
import { Modal, ModalCloseButton, ModalWrapper } from '@affine/component';
import type { FC, PropsWithChildren } from 'react';
import type { PropsWithChildren } from 'react';
import { useCallback } from 'react';
export type SettingModalProps = {
export interface SettingModalProps {
open: boolean;
setOpen: (value: boolean) => void;
};
}
export const SettingModal: FC<PropsWithChildren<SettingModalProps>> = ({
export const SettingModal = ({
children,
open,
setOpen,
}) => {
}: PropsWithChildren<SettingModalProps>) => {
const handleClose = useCallback(() => {
setOpen(false);
}, [setOpen]);

View File

@@ -1,13 +1,18 @@
import type { FC, HTMLAttributes, ReactNode } from 'react';
import type { HTMLAttributes, ReactNode } from 'react';
import { settingHeader } from './share.css';
export const SettingHeader: FC<
{ title: ReactNode; subtitle?: ReactNode } & Omit<
HTMLAttributes<HTMLDivElement>,
'title'
>
> = ({ title, subtitle, ...otherProps }) => {
interface SettingHeaderProps
extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
title: ReactNode;
subtitle?: ReactNode;
}
export const SettingHeader = ({
title,
subtitle,
...otherProps
}: SettingHeaderProps) => {
return (
<div className={settingHeader} {...otherProps}>
<div className="title">{title}</div>

View File

@@ -1,18 +1,18 @@
import clsx from 'clsx';
import type { CSSProperties, FC, PropsWithChildren, ReactNode } from 'react';
import type { CSSProperties, PropsWithChildren, ReactNode } from 'react';
import { settingRow } from './share.css';
export const SettingRow: FC<
PropsWithChildren<{
name: ReactNode;
desc: ReactNode;
style?: CSSProperties;
onClick?: () => void;
spreadCol?: boolean;
testId?: string;
}>
> = ({
interface SettingRowProps {
name: ReactNode;
desc: ReactNode;
style?: CSSProperties;
onClick?: () => void;
spreadCol?: boolean;
testId?: string;
}
export const SettingRow = ({
name,
desc,
children,
@@ -20,7 +20,7 @@ export const SettingRow: FC<
style,
spreadCol = true,
testId = '',
}) => {
}: PropsWithChildren<SettingRowProps>) => {
return (
<div
className={clsx(settingRow, {

View File

@@ -1,11 +1,15 @@
import type { FC, PropsWithChildren, ReactNode } from 'react';
import type { PropsWithChildren, ReactNode } from 'react';
import { wrapper } from './share.css';
export const SettingWrapper: FC<
PropsWithChildren<{
title?: ReactNode;
}>
> = ({ title, children }) => {
interface SettingWrapperProps {
title?: ReactNode;
}
export const SettingWrapper = ({
title,
children,
}: PropsWithChildren<SettingWrapperProps>) => {
return (
<div className={wrapper}>
{title ? <div className="title">{title}</div> : null}

View File

@@ -1,5 +1,4 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { FC } from 'react';
import {
ExportToHtmlMenuItem,
@@ -8,9 +7,9 @@ import {
ExportToPngMenuItem,
} from '../page-list/operation-menu-items/export';
import { actionsStyle, descriptionStyle, menuItemStyle } from './index.css';
import type { ShareMenuProps } from './share-menu';
// import type { ShareMenuProps } from './share-menu';
export const Export: FC<ShareMenuProps> = () => {
export const Export = () => {
const t = useAFFiNEI18N();
return (
<div className={menuItemStyle}>

View File

@@ -5,8 +5,7 @@ import type {
import { ExportIcon, PublishIcon, ShareIcon } from '@blocksuite/icons';
import type { Page } from '@blocksuite/store';
import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public';
import type { FC } from 'react';
import { useRef } from 'react';
import { type ReactElement, useRef } from 'react';
import { useCallback, useState } from 'react';
import { Button } from '../../ui/button';
@@ -16,22 +15,27 @@ import { containerStyle, indicatorContainerStyle, tabStyle } from './index.css';
import { SharePage } from './share-page';
import { ShareWorkspace } from './share-workspace';
import { StyledIndicator, TabItem } from './styles';
type SharePanel = 'SharePage' | 'Export' | 'ShareWorkspace';
const MenuItems: Record<SharePanel, FC<ShareMenuProps>> = {
type ShareMenuComponent<T> = (props: T) => ReactElement;
const MenuItems: Record<SharePanel, ShareMenuComponent<ShareMenuProps>> = {
SharePage: SharePage,
Export: Export,
ShareWorkspace: ShareWorkspace,
};
const tabIcons = {
SharePage: <ShareIcon />,
Export: <ExportIcon />,
ShareWorkspace: <PublishIcon />,
};
export type ShareMenuProps<
export interface ShareMenuProps<
Workspace extends AffineCloudWorkspace | LocalWorkspace =
| AffineCloudWorkspace
| LocalWorkspace,
> = {
> {
workspace: Workspace;
currentPage: Page;
onEnableAffineCloud: (workspace: LocalWorkspace) => void;
@@ -41,7 +45,7 @@ export type ShareMenuProps<
workspace: Workspace,
publish: boolean
) => Promise<void>;
};
}
function assertInstanceOf<T, U extends T>(
obj: T,
@@ -52,7 +56,7 @@ function assertInstanceOf<T, U extends T>(
}
}
export const ShareMenu: FC<ShareMenuProps> = props => {
export const ShareMenu = (props: ShareMenuProps) => {
const [activeItem, setActiveItem] = useState<SharePanel>('SharePage');
const [isPublic] = useBlockSuiteWorkspacePageIsPublic(props.currentPage);
const [open, setOpen] = useState(false);
@@ -84,7 +88,7 @@ export const ShareMenu: FC<ShareMenuProps> = props => {
activeItem: SharePanel;
onChangeTab: (selectedItem: SharePanel) => void;
}
const ShareMenu: FC<ShareMenuProps> = ({ activeItem, onChangeTab }) => {
const ShareMenu = ({ activeItem, onChangeTab }: ShareMenuProps) => {
const handleButtonClick = (itemName: SharePanel) => {
onChangeTab(itemName);
setActiveItem(itemName);

View File

@@ -3,7 +3,6 @@ import { WorkspaceFlavour } from '@affine/env/workspace';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public';
import type { FC } from 'react';
import { useState } from 'react';
import { useCallback, useMemo } from 'react';
@@ -17,7 +16,7 @@ import {
import type { ShareMenuProps } from './share-menu';
import { StyledDisableButton, StyledInput, StyledLinkSpan } from './styles';
export const LocalSharePage: FC<ShareMenuProps> = props => {
export const LocalSharePage = (props: ShareMenuProps) => {
const t = useAFFiNEI18N();
return (
<div className={menuItemStyle}>
@@ -35,7 +34,7 @@ export const LocalSharePage: FC<ShareMenuProps> = props => {
);
};
export const AffineSharePage: FC<ShareMenuProps> = props => {
export const AffineSharePage = (props: ShareMenuProps) => {
const [isPublic, setIsPublic] = useBlockSuiteWorkspacePageIsPublic(
props.currentPage
);
@@ -123,7 +122,7 @@ export const AffineSharePage: FC<ShareMenuProps> = props => {
);
};
export const SharePage: FC<ShareMenuProps> = props => {
export const SharePage = (props: ShareMenuProps) => {
if (props.workspace.flavour === WorkspaceFlavour.LOCAL) {
return <LocalSharePage {...props} />;
} else if (props.workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD) {

View File

@@ -4,13 +4,12 @@ import type {
} from '@affine/env/workspace';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { FC } from 'react';
import { Button } from '../../ui/button';
import { descriptionStyle, menuItemStyle } from './index.css';
import type { ShareMenuProps } from './share-menu';
const ShareLocalWorkspace: FC<ShareMenuProps<LocalWorkspace>> = props => {
const ShareLocalWorkspace = (props: ShareMenuProps<LocalWorkspace>) => {
const t = useAFFiNEI18N();
return (
<div className={menuItemStyle}>
@@ -29,9 +28,7 @@ const ShareLocalWorkspace: FC<ShareMenuProps<LocalWorkspace>> = props => {
);
};
const ShareAffineWorkspace: FC<
ShareMenuProps<AffineCloudWorkspace>
> = props => {
const ShareAffineWorkspace = (props: ShareMenuProps<AffineCloudWorkspace>) => {
// fixme: regression
const isPublicWorkspace = false;
const t = useAFFiNEI18N();
@@ -54,7 +51,7 @@ const ShareAffineWorkspace: FC<
);
};
export const ShareWorkspace: FC<ShareMenuProps> = props => {
export const ShareWorkspace = (props: ShareMenuProps) => {
if (props.workspace.flavour === WorkspaceFlavour.LOCAL) {
return (
<ShareLocalWorkspace {...(props as ShareMenuProps<LocalWorkspace>)} />

View File

@@ -1,7 +1,6 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowLeftSmallIcon, ArrowRightSmallIcon } from '@blocksuite/icons';
import clsx from 'clsx';
import type { FC } from 'react';
import { useState } from 'react';
import { Modal, ModalCloseButton, ModalWrapper } from '../..';
@@ -26,12 +25,12 @@ import {
videoStyle,
} from './index.css';
type TourModalProps = {
export interface TourModalProps {
open: boolean;
onClose: () => void;
};
}
export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
export const TourModal = ({ open, onClose }: TourModalProps) => {
const t = useAFFiNEI18N();
const [step, setStep] = useState(-1);
const handleClose = () => {

View File

@@ -1,24 +1,24 @@
import * as Avatar from '@radix-ui/react-avatar';
import clsx from 'clsx';
import type { CSSProperties, FC } from 'react';
import type { CSSProperties } from 'react';
import * as style from './style.css';
export type UserAvatar = {
export interface UserAvatar {
size?: number;
url?: string;
name?: string;
className?: string;
style?: CSSProperties;
};
}
export const UserAvatar: FC<UserAvatar> = ({
export const UserAvatar = ({
size = 20,
style: propsStyles = {},
url,
name,
className,
}) => {
}: UserAvatar) => {
return (
<Avatar.Root
style={{

View File

@@ -3,29 +3,26 @@ import * as RadixAvatar from '@radix-ui/react-avatar';
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import clsx from 'clsx';
import type React from 'react';
import { DefaultAvatar } from './default-avatar';
import { avatarImageStyle, avatarStyle } from './index.css';
export type WorkspaceAvatarProps = {
export interface WorkspaceAvatarProps {
size?: number;
workspace: Workspace | null;
className?: string;
};
}
export type BlockSuiteWorkspaceAvatar = Omit<
WorkspaceAvatarProps,
'workspace'
> & {
export interface BlockSuiteWorkspaceAvatar
extends Omit<WorkspaceAvatarProps, 'workspace'> {
workspace: Workspace;
};
}
export const BlockSuiteWorkspaceAvatar: React.FC<BlockSuiteWorkspaceAvatar> = ({
export const BlockSuiteWorkspaceAvatar = ({
size,
workspace,
...props
}) => {
}: BlockSuiteWorkspaceAvatar) => {
const [avatar] = useBlockSuiteWorkspaceAvatarUrl(workspace);
const [name] = useBlockSuiteWorkspaceName(workspace);
@@ -46,11 +43,11 @@ export const BlockSuiteWorkspaceAvatar: React.FC<BlockSuiteWorkspaceAvatar> = ({
);
};
export const WorkspaceAvatar: React.FC<WorkspaceAvatarProps> = ({
export const WorkspaceAvatar = ({
size = 20,
workspace,
...props
}) => {
}: WorkspaceAvatarProps) => {
if (workspace) {
return (
<BlockSuiteWorkspaceAvatar {...props} size={size} workspace={workspace} />

View File

@@ -11,26 +11,26 @@ import {
useSensors,
} from '@dnd-kit/core';
import { SortableContext, useSortable } from '@dnd-kit/sortable';
import type { CSSProperties, FC } from 'react';
import type { CSSProperties } from 'react';
import { useMemo } from 'react';
import { WorkspaceCard } from '../../components/card/workspace-card';
import { workspaceItemStyle } from './index.css';
export type WorkspaceListProps = {
export interface WorkspaceListProps {
disabled?: boolean;
currentWorkspaceId: string | null;
items: (AffineCloudWorkspace | LocalWorkspace)[];
onClick: (workspaceId: string) => void;
onSettingClick: (workspaceId: string) => void;
onDragEnd: (event: DragEndEvent) => void;
};
}
const SortableWorkspaceItem: FC<
Omit<WorkspaceListProps, 'items'> & {
item: RootWorkspaceMetadata;
}
> = props => {
interface SortableWorkspaceItemProps extends Omit<WorkspaceListProps, 'items'> {
item: RootWorkspaceMetadata;
}
const SortableWorkspaceItem = (props: SortableWorkspaceItemProps) => {
const { setNodeRef, attributes, listeners, transform } = useSortable({
id: props.item.id,
});
@@ -63,7 +63,7 @@ const SortableWorkspaceItem: FC<
);
};
export const WorkspaceList: FC<WorkspaceListProps> = props => {
export const WorkspaceList = (props: WorkspaceListProps) => {
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {

View File

@@ -1,26 +1,27 @@
import { clsx } from 'clsx';
import type {
FC,
HTMLAttributes,
PropsWithChildren,
ReactElement,
ReactNode,
} from 'react';
import { AppSidebarFallback } from '../app-sidebar';
import { appStyle, mainContainerStyle, toolStyle } from './index.css';
export type WorkspaceRootProps = PropsWithChildren<{
export interface WorkspaceRootProps {
resizing?: boolean;
useNoisyBackground?: boolean;
useBlurBackground?: boolean;
}>;
children: ReactNode;
}
export const AppContainer: FC<WorkspaceRootProps> = ({
export const AppContainer = ({
resizing,
useNoisyBackground,
useBlurBackground,
children,
}) => {
}: WorkspaceRootProps) => {
const noisyBackground = useNoisyBackground && environment.isDesktop;
return (
<div
@@ -36,18 +37,17 @@ export const AppContainer: FC<WorkspaceRootProps> = ({
);
};
export type MainContainerProps = PropsWithChildren<{
export interface MainContainerProps extends HTMLAttributes<HTMLDivElement> {
className?: string;
padding?: boolean;
}> &
HTMLAttributes<HTMLDivElement>;
}
export const MainContainer = ({
className,
padding,
children,
...props
}: MainContainerProps): ReactElement => {
}: PropsWithChildren<MainContainerProps>): ReactElement => {
return (
<div
{...props}

View File

@@ -1,6 +1,5 @@
import clsx from 'clsx';
import {
type FC,
forwardRef,
type HTMLAttributes,
type PropsWithChildren,
@@ -10,6 +9,7 @@ import {
import { Loading } from '../loading';
import { button, buttonIcon } from './style.css';
export type ButtonType =
| 'default'
| 'primary'
@@ -18,18 +18,21 @@ export type ButtonType =
| 'warning'
| 'success'
| 'processing';
export type ButtonSize = 'default' | 'large' | 'extraLarge';
export type ButtonProps = PropsWithChildren &
Omit<HTMLAttributes<HTMLButtonElement>, 'type'> & {
type?: ButtonType;
disabled?: boolean;
icon?: ReactElement;
iconPosition?: 'start' | 'end';
shape?: 'default' | 'round' | 'circle';
block?: boolean;
size?: ButtonSize;
loading?: boolean;
};
export interface ButtonProps
extends Omit<HTMLAttributes<HTMLButtonElement>, 'type'> {
type?: ButtonType;
disabled?: boolean;
icon?: ReactElement;
iconPosition?: 'start' | 'end';
shape?: 'default' | 'round' | 'circle';
block?: boolean;
size?: ButtonSize;
loading?: boolean;
}
const defaultProps = {
type: 'default',
disabled: false,
@@ -39,7 +42,7 @@ const defaultProps = {
loading: false,
};
const ButtonIcon: FC<ButtonProps> = props => {
const ButtonIcon = (props: PropsWithChildren<ButtonProps>) => {
const {
size,
icon,
@@ -65,6 +68,7 @@ const ButtonIcon: FC<ButtonProps> = props => {
</div>
);
};
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => {
const {

View File

@@ -1,14 +1,13 @@
import { assignInlineVars } from '@vanilla-extract/dynamic';
import type { FC } from 'react';
import { loading, speedVar } from './styles.css';
export type LoadingProps = {
export interface LoadingProps {
size?: number;
speed?: number;
};
}
export const Loading: FC<LoadingProps> = ({ size, speed = 1.2 }) => {
export const Loading = ({ size, speed = 1.2 }: LoadingProps) => {
return (
<svg
className={loading}