feat: button add loading

This commit is contained in:
MingLiang Wang
2022-12-27 18:21:32 +08:00
parent a86d29ef16
commit 54a6f8658f
7 changed files with 110 additions and 27 deletions
@@ -3,7 +3,7 @@ import { FC, useRef, ChangeEvent, ReactElement } from 'react';
import { styled } from '@/styles'; import { styled } from '@/styles';
interface Props { interface Props {
uploadType?: string; uploadType?: string;
view?: ReactElement; children?: ReactElement;
accept?: string; accept?: string;
fileChange: (file: File) => void; fileChange: (file: File) => void;
} }
@@ -28,7 +28,7 @@ export const Upload: FC<Props> = props => {
}; };
return ( return (
<UploadStyle onClick={_chooseFile}> <UploadStyle onClick={_chooseFile}>
{props.view ?? <Button>Upload</Button>} {props.children ?? <Button>Upload</Button>}
<input <input
ref={input_ref} ref={input_ref}
type="file" type="file"
@@ -39,6 +39,7 @@ export const GeneralPage = ({
} = useAppState(); } = useAppState();
const [showDelete, setShowDelete] = useState<boolean>(false); const [showDelete, setShowDelete] = useState<boolean>(false);
const [showLeave, setShowLeave] = useState<boolean>(false); const [showLeave, setShowLeave] = useState<boolean>(false);
const [uploading, setUploading] = useState<boolean>(false);
const [workspaceName, setWorkspaceName] = useState<string>( const [workspaceName, setWorkspaceName] = useState<string>(
workspaces[workspace.id]?.meta.name || workspaces[workspace.id]?.meta.name ||
(workspace.type === WorkspaceType.Private && user ? user.name : '') (workspace.type === WorkspaceType.Private && user ? user.name : '')
@@ -75,11 +76,17 @@ export const GeneralPage = ({
}; };
const fileChange = async (file: File) => { const fileChange = async (file: File) => {
setUploading(true);
const blob = new Blob([file], { type: file.type }); const blob = new Blob([file], { type: file.type });
const blobId = await uploadBlob({ blob }); const blobId = await uploadBlob({ blob }).finally(() => {
currentWorkspace?.meta.setAvatar(blobId); setUploading(false);
workspaces[workspace.id]?.meta.setAvatar(blobId); });
debouncedRefreshWorkspacesMeta(); if (blobId) {
currentWorkspace?.meta.setAvatar(blobId);
workspaces[workspace.id]?.meta.setAvatar(blobId);
setUploading(false);
debouncedRefreshWorkspacesMeta();
}
}; };
return workspace ? ( return workspace ? (
@@ -99,7 +106,9 @@ export const GeneralPage = ({
<Upload <Upload
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg" accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
fileChange={fileChange} fileChange={fileChange}
></Upload> >
<Button loading={uploading}>Upload</Button>
</Upload>
{/* TODO: add upload logic */} {/* TODO: add upload logic */}
{/* {isOwner ? ( {/* {isOwner ? (
<StyledAvatarUploadBtn shape="round">upload</StyledAvatarUploadBtn> <StyledAvatarUploadBtn shape="round">upload</StyledAvatarUploadBtn>
@@ -29,7 +29,7 @@ const DefaultHeadImgColors = [
export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => { export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => {
const [workspaceName, setWorkspaceId] = useState<string>(''); const [workspaceName, setWorkspaceId] = useState<string>('');
const [canCreate, setCanCreate] = useState<boolean>(false); const [creating, setCreating] = useState<boolean>(false);
const { refreshWorkspacesMeta } = useAppState(); const { refreshWorkspacesMeta } = useAppState();
const handlerInputChange = (workspaceName: string) => { const handlerInputChange = (workspaceName: string) => {
setWorkspaceId(workspaceName); setWorkspaceId(workspaceName);
@@ -64,22 +64,26 @@ export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => {
}); });
}; };
const handleCreateWorkspace = async () => { const handleCreateWorkspace = async () => {
setCanCreate(true); setCreating(true);
const blobId = await createDefaultHeadImg(workspaceName); const blobId = await createDefaultHeadImg(workspaceName).catch(() => {
createWorkspace({ name: workspaceName, avatar: blobId }) setCreating(false);
.then(async data => { });
await refreshWorkspacesMeta(); if (blobId) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment createWorkspace({ name: workspaceName, avatar: blobId })
// @ts-ignore .then(async data => {
router.push(`/workspace/${data.id}`); await refreshWorkspacesMeta();
onClose(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment
}) // @ts-ignore
.catch(err => { router.push(`/workspace/${data.id}`);
console.log(err, 'err'); onClose();
}) })
.finally(() => { .catch(err => {
setCanCreate(false); console.log(err, 'err');
}); })
.finally(() => {
setCreating(false);
});
}
}; };
return ( return (
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>
@@ -99,8 +103,9 @@ export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => {
</StyledInputContent> </StyledInputContent>
<StyledButtonContent> <StyledButtonContent>
<StyledButton <StyledButton
disabled={!workspaceName.length || canCreate} disabled={!workspaceName.length || creating}
onClick={handleCreateWorkspace} onClick={handleCreateWorkspace}
loading={creating}
> >
Create Create
</StyledButton> </StyledButton>
+2
View File
@@ -57,6 +57,8 @@ const Affine = () => {
<Button icon={<FavouritedIcon />}></Button> <Button icon={<FavouritedIcon />}></Button>
<Button icon={<FavouritedIcon />} shape="round"></Button> <Button icon={<FavouritedIcon />} shape="round"></Button>
<Button loading={true}></Button>
<Button loading={true} type="primary"></Button>
</> </>
); );
}; };
+8 -2
View File
@@ -3,6 +3,7 @@ import { StyledButton } from './styles';
import { ButtonProps } from './interface'; import { ButtonProps } from './interface';
import { getSize } from './utils'; import { getSize } from './utils';
import { Loading } from './loading';
export const Button = forwardRef<HTMLButtonElement, ButtonProps>( export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
( (
@@ -17,6 +18,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
type = 'default', type = 'default',
children, children,
bold = false, bold = false,
loading = false,
...props ...props
}, },
ref ref
@@ -38,12 +40,16 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
bold={bold} bold={bold}
{...props} {...props}
> >
{icon && {loading ? (
<Loading type={type}></Loading>
) : (
icon &&
cloneElement(Children.only(icon), { cloneElement(Children.only(icon), {
width: iconSize, width: iconSize,
height: iconSize, height: iconSize,
className: `affine-button-icon ${icon.props.className ?? ''}`, className: `affine-button-icon ${icon.props.className ?? ''}`,
})} })
)}
{children && <span>{children}</span>} {children && <span>{children}</span>}
</StyledButton> </StyledButton>
); );
+1
View File
@@ -20,4 +20,5 @@ export type ButtonProps = PropsWithChildren &
shape?: 'default' | 'round' | 'circle'; shape?: 'default' | 'round' | 'circle';
type?: 'primary' | 'warning' | 'danger' | 'default'; type?: 'primary' | 'warning' | 'danger' | 'default';
bold?: boolean; bold?: boolean;
loading?: boolean;
}; };
+60
View File
@@ -0,0 +1,60 @@
import { styled } from '@/styles';
import { ButtonProps } from './interface';
import { getButtonColors } from './utils';
export const LoadingContainer = styled('div')<Pick<ButtonProps, 'type'>>(
({ theme, type = 'default' }) => {
const { color } = getButtonColors(theme, type);
return `
margin: 0px auto;
width: 38px;
text-align: center;
.load {
width: 8px;
height: 8px;
background-color: ${color};
border-radius: 100%;
display: inline-block;
-webkit-animation: bouncedelay 1.4s infinite ease-in-out;
animation: bouncedelay 1.4s infinite ease-in-out;
/* Prevent first frame from flickering when animation starts */
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.load1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
.load2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
@-webkit-keyframes bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0) }
40% { -webkit-transform: scale(1.0) }
}
@keyframes bouncedelay {
0%, 80%, 100% {
transform: scale(0);
-webkit-transform: scale(0);
} 40% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}
`;
}
);
export const Loading = ({ type }: Pick<ButtonProps, 'type'>) => {
return (
<LoadingContainer type={type} className="load-container">
<div className="load load1"></div>
<div className="load load2"></div>
<div className="load"></div>
</LoadingContainer>
);
};