mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-17 01:56:27 +08:00
Merge branch 'feat/filesystem_and_search' of github.com:toeverything/AFFiNE into feat/filesystem_and_search
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import { NotFoundTitle, PageContainer } from './styles';
|
||||
|
||||
export const NotfoundPage = () => {
|
||||
return (
|
||||
<PageContainer>
|
||||
<NotFoundTitle>404 - Page Not Found</NotFoundTitle>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotfoundPage;
|
||||
@@ -0,0 +1,21 @@
|
||||
import { absoluteCenter, displayFlex, styled } from '@/styles';
|
||||
|
||||
export const PageContainer = styled('div')(({ theme }) => {
|
||||
return {
|
||||
width: '100%',
|
||||
height: 'calc(100vh)',
|
||||
backgroundColor: theme.colors.pageBackground,
|
||||
};
|
||||
});
|
||||
|
||||
export const NotFoundTitle = styled('h1')(({ theme }) => {
|
||||
return {
|
||||
position: 'relative',
|
||||
top: 'calc(50% - 100px)',
|
||||
height: '100px',
|
||||
fontSize: '60px',
|
||||
lineHeight: '100px',
|
||||
color: theme.colors.textColor,
|
||||
textAlign: 'center',
|
||||
};
|
||||
});
|
||||
@@ -22,6 +22,9 @@ import { getWarningMessage, shouldShowWarning } from './utils';
|
||||
import { Menu, MenuItem } from '@/ui/menu';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useConfirm } from '@/providers/confirm-provider';
|
||||
import { useModal } from '@/providers/global-modal-provider';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import { SyncIcon } from './sync-icon';
|
||||
|
||||
const PopoverContent = () => {
|
||||
const { editor, mode, setMode } = useEditor();
|
||||
@@ -131,6 +134,7 @@ const HeaderRight = () => {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<SyncIcon />
|
||||
<ThemeModeSwitch />
|
||||
<Menu content={<PopoverContent />} placement="bottom-end">
|
||||
<IconButton>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { CloudUnsyncedIcon, CloudInsyncIcon } from '@blocksuite/icons';
|
||||
import { useModal } from '@/providers/global-modal-provider';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import { IconButton } from '@/ui/button';
|
||||
|
||||
export const SyncIcon = () => {
|
||||
const { triggerLoginModal } = useModal();
|
||||
const appState = useAppState();
|
||||
|
||||
return appState.user ? (
|
||||
<IconButton iconSize="middle" disabled>
|
||||
<CloudInsyncIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<IconButton iconSize="middle" onClick={triggerLoginModal}>
|
||||
<CloudUnsyncedIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
import { signInWithGoogle, login, setToken } from '@pathfinder/data-services';
|
||||
import { styled } from '@/styles';
|
||||
import { Button } from '@/ui/button';
|
||||
import { useModal } from '@/providers/global-modal-provider';
|
||||
import { GoogleIcon, StayLogOutIcon } from './icons';
|
||||
|
||||
export const GoogleLoginButton = () => {
|
||||
const { triggerLoginModal } = useModal();
|
||||
return (
|
||||
<StyledGoogleButton
|
||||
onClick={() => {
|
||||
signInWithGoogle()
|
||||
.then(async user => {
|
||||
const idToken = await user.user.getIdToken();
|
||||
const token = await login({ token: idToken, type: 'Google' });
|
||||
setToken({
|
||||
accessToken: token.token,
|
||||
refreshToken: token.refresh,
|
||||
});
|
||||
triggerLoginModal();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('sign google error', error);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<ButtonWrapper>
|
||||
<IconWrapper>
|
||||
<GoogleIcon />
|
||||
</IconWrapper>
|
||||
<TextWrapper>
|
||||
<Title>Continue with Google</Title>
|
||||
<Description>Set up an AFFINE account to sync data</Description>
|
||||
</TextWrapper>
|
||||
</ButtonWrapper>
|
||||
</StyledGoogleButton>
|
||||
);
|
||||
};
|
||||
|
||||
export const StayLogOutButton = () => {
|
||||
return (
|
||||
<StyledStayLogOutButton>
|
||||
<ButtonWrapper>
|
||||
<IconWrapper>
|
||||
<StayLogOutIcon />
|
||||
</IconWrapper>
|
||||
<TextWrapper>
|
||||
<Title>Stay logged out</Title>
|
||||
<Description>All changes are saved locally</Description>
|
||||
</TextWrapper>
|
||||
</ButtonWrapper>
|
||||
</StyledStayLogOutButton>
|
||||
);
|
||||
};
|
||||
|
||||
const StyledGoogleButton = styled(Button)(({ theme }) => {
|
||||
return {
|
||||
width: '361px',
|
||||
height: '56px',
|
||||
padding: '4px',
|
||||
background: '#6880FF',
|
||||
color: '#fff',
|
||||
|
||||
'& > span': {
|
||||
marginLeft: 0,
|
||||
},
|
||||
|
||||
':hover': {
|
||||
background: '#516BF4',
|
||||
color: '#fff',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const StyledStayLogOutButton = styled(Button)(({ theme }) => {
|
||||
return {
|
||||
width: '361px',
|
||||
height: '56px',
|
||||
padding: '4px',
|
||||
|
||||
'& > span': {
|
||||
marginLeft: 0,
|
||||
},
|
||||
|
||||
':hover': {
|
||||
borderColor: '#6880FF',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const ButtonWrapper = styled('div')({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
});
|
||||
|
||||
const IconWrapper = styled('div')({
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
flex: '0 48px',
|
||||
borderRadius: '5px',
|
||||
overflow: 'hidden',
|
||||
marginRight: '12px',
|
||||
});
|
||||
|
||||
const TextWrapper = styled('div')({
|
||||
flex: 1,
|
||||
textAlign: 'left',
|
||||
});
|
||||
|
||||
const Title = styled('h1')(({ theme }) => {
|
||||
return {
|
||||
fontSize: '18px',
|
||||
lineHeight: '26px',
|
||||
fontWeight: 500,
|
||||
};
|
||||
});
|
||||
|
||||
const Description = styled('p')(({ theme }) => {
|
||||
return {
|
||||
fontSize: '16px',
|
||||
lineHeight: '22px',
|
||||
fontWeight: 400,
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.3055 10.0415H21.5V10H12.5V14H18.1515C17.327 16.3285 15.1115 18 12.5 18C9.1865 18 6.5 15.3135 6.5 12C6.5 8.6865 9.1865 6 12.5 6C14.0295 6 15.421 6.577 16.4805 7.5195L19.309 4.691C17.523 3.0265 15.134 2 12.5 2C6.9775 2 2.5 6.4775 2.5 12C2.5 17.5225 6.9775 22 12.5 22C18.0225 22 22.5 17.5225 22.5 12C22.5 11.3295 22.431 10.675 22.3055 10.0415Z" fill="#FFC107"/>
|
||||
<path d="M3.65234 7.3455L6.93784 9.755C7.82684 7.554 9.97984 6 12.4993 6C14.0288 6 15.4203 6.577 16.4798 7.5195L19.3083 4.691C17.5223 3.0265 15.1333 2 12.4993 2C8.65834 2 5.32734 4.1685 3.65234 7.3455Z" fill="#FF3D00"/>
|
||||
<path d="M12.5002 22.0003C15.0832 22.0003 17.4302 21.0118 19.2047 19.4043L16.1097 16.7853C15.0719 17.5745 13.8039 18.0014 12.5002 18.0003C9.89916 18.0003 7.69066 16.3418 6.85866 14.0273L3.59766 16.5398C5.25266 19.7783 8.61366 22.0003 12.5002 22.0003Z" fill="#4CAF50"/>
|
||||
<path d="M22.3055 10.0415H21.5V10H12.5V14H18.1515C17.7571 15.1082 17.0467 16.0766 16.108 16.7855L16.1095 16.7845L19.2045 19.4035C18.9855 19.6025 22.5 17 22.5 12C22.5 11.3295 22.431 10.675 22.3055 10.0415Z" fill="#1976D2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,42 @@
|
||||
import { CloudUnsyncedIcon } from '@blocksuite/icons';
|
||||
import { styled } from '@/styles';
|
||||
import GoogleSvg from './google.svg';
|
||||
|
||||
export const GoogleIcon = () => {
|
||||
return (
|
||||
<GoogleIconWrapper>
|
||||
<picture>
|
||||
<img src={GoogleSvg.src} alt="Google" />
|
||||
</picture>
|
||||
</GoogleIconWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const GoogleIconWrapper = styled('div')(({ theme }) => ({
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
background: theme.colors.pageBackground,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}));
|
||||
|
||||
export const StayLogOutIcon = () => {
|
||||
return (
|
||||
<StayLogOutWrapper>
|
||||
<CloudUnsyncedIcon />
|
||||
</StayLogOutWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const StayLogOutWrapper = styled('div')(({ theme }) => {
|
||||
return {
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '24px',
|
||||
background: theme.colors.hoverBackground,
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import { ResetIcon } from '@blocksuite/icons';
|
||||
import { styled } from '@/styles';
|
||||
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
|
||||
import { TextButton, Button } from '@/ui/button';
|
||||
import { GoogleLoginButton, StayLogOutButton } from './LoginOptionButton';
|
||||
|
||||
interface LoginModalProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const LoginModal = ({ open, onClose }: LoginModalProps) => {
|
||||
return (
|
||||
<Modal open={open} onClose={onClose}>
|
||||
<ModalWrapper width={620} height={334}>
|
||||
<Header>
|
||||
<ModalCloseButton
|
||||
top={6}
|
||||
right={6}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
</Header>
|
||||
<Content>
|
||||
<ContentTitle>Currently not logged in</ContentTitle>
|
||||
<GoogleLoginButton />
|
||||
<StayLogOutButton />
|
||||
</Content>
|
||||
<Footer>
|
||||
<TextButton icon={<StyledResetIcon />}>Clear local data</TextButton>
|
||||
</Footer>
|
||||
</ModalWrapper>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
const Header = styled('div')({
|
||||
position: 'relative',
|
||||
height: '44px',
|
||||
});
|
||||
|
||||
const Content = styled('div')({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '16px',
|
||||
});
|
||||
|
||||
const ContentTitle = styled('h1')({
|
||||
fontSize: '20px',
|
||||
lineHeight: '28px',
|
||||
fontWeight: 600,
|
||||
textAlign: 'center',
|
||||
paddingBottom: '16px',
|
||||
});
|
||||
|
||||
const Footer = styled('div')({
|
||||
height: '70px',
|
||||
paddingLeft: '24px',
|
||||
marginTop: '32px',
|
||||
});
|
||||
|
||||
const StyledResetIcon = styled(ResetIcon)({
|
||||
marginRight: '12px',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
});
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
import { InformationIcon, LogOutIcon } from '@blocksuite/icons';
|
||||
import { styled } from '@/styles';
|
||||
import { Divider } from '@/ui/divider';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import { SelectorPopperContainer } from './styles';
|
||||
import {
|
||||
PrivateWorkspaceItem,
|
||||
WorkspaceItem,
|
||||
CreateWorkspaceItem,
|
||||
ListItem,
|
||||
LoginItem,
|
||||
} from './WorkspaceItem';
|
||||
|
||||
const workspaces = [
|
||||
{
|
||||
name: 'Design',
|
||||
icon: '',
|
||||
},
|
||||
{
|
||||
name: 'Operation',
|
||||
icon: '',
|
||||
},
|
||||
{
|
||||
name: 'Something is too long to show in this box',
|
||||
icon: '',
|
||||
},
|
||||
];
|
||||
|
||||
export const SelectorPopperContent = () => {
|
||||
const { user, workspaces } = useAppState();
|
||||
return !user ? (
|
||||
<SelectorPopperContainer placement="bottom-start">
|
||||
<LoginItem />
|
||||
<StyledDivider />
|
||||
<ListItem
|
||||
icon={<InformationIcon />}
|
||||
name="About AFFiNE"
|
||||
onClick={() => console.log('About AFFiNE')}
|
||||
/>
|
||||
</SelectorPopperContainer>
|
||||
) : (
|
||||
<SelectorPopperContainer placement="bottom-start">
|
||||
<PrivateWorkspaceItem />
|
||||
<StyledDivider />
|
||||
<WorkspaceGroupTitle>Workspace</WorkspaceGroupTitle>
|
||||
{workspaces.map(workspace => {
|
||||
return (
|
||||
<WorkspaceItem
|
||||
key={workspace.id}
|
||||
id={workspace.id}
|
||||
name={`workspace-${workspace.id}`}
|
||||
icon={''}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<CreateWorkspaceItem />
|
||||
<StyledDivider />
|
||||
<ListItem
|
||||
icon={<InformationIcon />}
|
||||
name="About AFFiNE"
|
||||
onClick={() => console.log('About AFFiNE')}
|
||||
/>
|
||||
<ListItem
|
||||
icon={<LogOutIcon />}
|
||||
name="Sign out"
|
||||
onClick={() => console.log('Sign out')}
|
||||
/>
|
||||
</SelectorPopperContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const StyledDivider = styled(Divider)({
|
||||
margin: '8px 12px',
|
||||
width: 'calc(100% - 24px)',
|
||||
});
|
||||
|
||||
const WorkspaceGroupTitle = styled('div')(({ theme }) => {
|
||||
return {
|
||||
color: theme.colors.iconColor,
|
||||
fontSize: theme.font.sm,
|
||||
lineHeight: '30px',
|
||||
height: '30px',
|
||||
padding: '0 12px',
|
||||
};
|
||||
});
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { useState } from 'react';
|
||||
import { AddIcon } from '@blocksuite/icons';
|
||||
import { styled } from '@/styles';
|
||||
import {
|
||||
WorkspaceItemAvatar,
|
||||
WorkspaceItemWrapper,
|
||||
WorkspaceItemContent,
|
||||
} from '../styles';
|
||||
import { WorkspaceCreate } from './workspace-create';
|
||||
|
||||
const name = 'Create new Workspace';
|
||||
|
||||
export const CreateWorkspaceItem = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<WorkspaceItemWrapper onClick={() => setOpen(true)}>
|
||||
<WorkspaceItemAvatar>
|
||||
<AddIcon />
|
||||
</WorkspaceItemAvatar>
|
||||
<WorkspaceItemContent>
|
||||
<Name title={name}>{name}</Name>
|
||||
</WorkspaceItemContent>
|
||||
</WorkspaceItemWrapper>
|
||||
<WorkspaceCreate open={open} onClose={() => setOpen(false)} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Name = styled('div')(({ theme }) => {
|
||||
return {
|
||||
color: theme.colors.quoteColor,
|
||||
fontSize: theme.font.base,
|
||||
fontWeight: 400,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
};
|
||||
});
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './CreateWorkspaceItem';
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './workspace-create';
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
import { styled } from '@/styles';
|
||||
|
||||
export const StyledModalWrapper = styled('div')(({ theme }) => {
|
||||
return {
|
||||
position: 'relative',
|
||||
padding: '0px',
|
||||
width: '460px',
|
||||
background: theme.colors.popoverBackground,
|
||||
borderRadius: '12px',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledModalHeader = styled('div')(({ theme }) => {
|
||||
return {
|
||||
margin: '44px 0px 12px 0px',
|
||||
width: '460px',
|
||||
fontWeight: '600',
|
||||
fontSize: '20px;',
|
||||
textAlign: 'center',
|
||||
color: theme.colors.popoverColor,
|
||||
};
|
||||
});
|
||||
|
||||
// export const StyledModalContent = styled('div')(({ theme }) => {});
|
||||
|
||||
export const StyledTextContent = styled('div')(({ theme }) => {
|
||||
return {
|
||||
margin: 'auto',
|
||||
width: '425px',
|
||||
fontFamily: 'Avenir Next',
|
||||
fontStyle: 'normal',
|
||||
fontWeight: '400',
|
||||
fontSize: '18px',
|
||||
lineHeight: '26px',
|
||||
textAlign: 'center',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledInputContent = styled('div')(({ theme }) => {
|
||||
return {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
margin: '40px 0 24px 0',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledButtonContent = styled('div')(({ theme }) => {
|
||||
return {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
margin: '0px 0 32px 0',
|
||||
};
|
||||
});
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import { createWorkspace } from '@pathfinder/data-services';
|
||||
import Modal from '@/ui/modal';
|
||||
import Input from '@/ui/input';
|
||||
import { Button } from '@/ui/button';
|
||||
import {
|
||||
StyledModalHeader,
|
||||
StyledTextContent,
|
||||
StyledModalWrapper,
|
||||
StyledInputContent,
|
||||
StyledButtonContent,
|
||||
} from './style';
|
||||
import { useState } from 'react';
|
||||
import { ModalCloseButton } from '@/ui/modal';
|
||||
|
||||
interface WorkspaceCreateProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => {
|
||||
const [workspaceName, setWorkspaceId] = useState<string>('');
|
||||
const handlerInputChange = (workspaceName: string) => {
|
||||
setWorkspaceId(workspaceName);
|
||||
};
|
||||
console.log('workspaceName', workspaceName);
|
||||
return (
|
||||
<Modal open={open} onClose={onClose}>
|
||||
<StyledModalWrapper>
|
||||
<ModalCloseButton />
|
||||
<StyledModalHeader>Create new Workspace</StyledModalHeader>
|
||||
<StyledTextContent>
|
||||
Workspaces are shared environments where teams can collaborate. After
|
||||
creating a Workspace, you can invite others to join.
|
||||
</StyledTextContent>
|
||||
<StyledInputContent>
|
||||
<Input
|
||||
onChange={handlerInputChange}
|
||||
placeholder="Set a Workspace name"
|
||||
value={workspaceName}
|
||||
></Input>
|
||||
</StyledInputContent>
|
||||
<StyledButtonContent>
|
||||
<Button
|
||||
disabled={!workspaceName.length}
|
||||
style={{ width: '260px' }}
|
||||
onClick={() => {
|
||||
createWorkspace({ name: workspaceName, avatar: '' });
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</StyledButtonContent>
|
||||
</StyledModalWrapper>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkspaceCreate;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { styled } from '@/styles';
|
||||
import { WorkspaceItemWrapper, WorkspaceItemContent } from './styles';
|
||||
|
||||
interface ListItemProps {
|
||||
name: string;
|
||||
icon: ReactNode;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export const ListItem = ({ name, icon, onClick }: ListItemProps) => {
|
||||
return (
|
||||
<WorkspaceItemWrapper onClick={onClick}>
|
||||
<StyledIconWrapper>{icon}</StyledIconWrapper>
|
||||
<WorkspaceItemContent>
|
||||
<Name title={name}>{name}</Name>
|
||||
</WorkspaceItemContent>
|
||||
</WorkspaceItemWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const Name = styled('div')(({ theme }) => {
|
||||
return {
|
||||
color: theme.colors.quoteColor,
|
||||
fontSize: theme.font.sm,
|
||||
fontWeight: 400,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
};
|
||||
});
|
||||
|
||||
const StyledIconWrapper = styled('div')({
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
fontSize: '20px',
|
||||
});
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { useModal } from '@/providers/global-modal-provider';
|
||||
import { styled } from '@/styles';
|
||||
import {
|
||||
WorkspaceItemAvatar,
|
||||
LoginItemWrapper,
|
||||
WorkspaceItemContent,
|
||||
} from './styles';
|
||||
|
||||
export const LoginItem = () => {
|
||||
const { triggerLoginModal } = useModal();
|
||||
return (
|
||||
<LoginItemWrapper onClick={() => triggerLoginModal()}>
|
||||
<WorkspaceItemAvatar alt="AFFiNE" src={''}>
|
||||
A
|
||||
</WorkspaceItemAvatar>
|
||||
<WorkspaceItemContent>
|
||||
<Name title="AFFiNE">AFFiNE</Name>
|
||||
<Description
|
||||
title="Log in to sync with affine"
|
||||
className="login-description"
|
||||
>
|
||||
Log in to sync with affine
|
||||
</Description>
|
||||
</WorkspaceItemContent>
|
||||
</LoginItemWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const Name = styled('div')(({ theme }) => {
|
||||
return {
|
||||
color: theme.colors.quoteColor,
|
||||
fontSize: theme.font.base,
|
||||
fontWeight: 500,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
};
|
||||
});
|
||||
|
||||
const Description = styled('div')(({ theme }) => {
|
||||
return {
|
||||
color: theme.colors.iconColor,
|
||||
fontSize: theme.font.sm,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
};
|
||||
});
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { styled } from '@/styles';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import {
|
||||
WorkspaceItemAvatar,
|
||||
PrivateWorkspaceWrapper,
|
||||
WorkspaceItemContent,
|
||||
} from './styles';
|
||||
|
||||
export const PrivateWorkspaceItem = () => {
|
||||
const { user } = useAppState();
|
||||
|
||||
return !user ? null : (
|
||||
<PrivateWorkspaceWrapper>
|
||||
<WorkspaceItemAvatar alt={user.name} src={user.avatar_url}>
|
||||
{user.name.charAt(0)}
|
||||
</WorkspaceItemAvatar>
|
||||
<WorkspaceItemContent>
|
||||
<Name title={user.name}>{user.name}</Name>
|
||||
<Email title={user.email}>{user.email}</Email>
|
||||
</WorkspaceItemContent>
|
||||
</PrivateWorkspaceWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const Name = styled('div')(({ theme }) => {
|
||||
return {
|
||||
color: theme.colors.quoteColor,
|
||||
fontSize: theme.font.base,
|
||||
fontWeight: 500,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
};
|
||||
});
|
||||
|
||||
const Email = styled('div')(({ theme }) => {
|
||||
return {
|
||||
color: theme.colors.iconColor,
|
||||
fontSize: theme.font.sm,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
};
|
||||
});
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { SettingsIcon } from '@blocksuite/icons';
|
||||
import { styled } from '@/styles';
|
||||
import { IconButton } from '@/ui/button';
|
||||
|
||||
export const FooterSetting = () => {
|
||||
return (
|
||||
<Wrapper className="footer-setting">
|
||||
<SettingsIcon />
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const Wrapper = styled(IconButton)(({ theme }) => {
|
||||
return {
|
||||
fontSize: '20px',
|
||||
};
|
||||
});
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { UsersIcon } from '@blocksuite/icons';
|
||||
import { styled } from '@/styles';
|
||||
import { IconButton } from '@/ui/button';
|
||||
|
||||
export const FooterUsers = () => {
|
||||
return (
|
||||
<Wrapper className="footer-users">
|
||||
<>
|
||||
<UsersIcon />
|
||||
<Tip>99+</Tip>
|
||||
</>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const Wrapper = styled(IconButton)({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '16px',
|
||||
});
|
||||
|
||||
const Tip = styled('span')({
|
||||
fontSize: '12px',
|
||||
});
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import { styled } from '@/styles';
|
||||
import {
|
||||
WorkspaceItemAvatar,
|
||||
WorkspaceItemWrapper,
|
||||
WorkspaceItemContent,
|
||||
} from '../styles';
|
||||
import { FooterSetting } from './FooterSetting';
|
||||
import { FooterUsers } from './FooterUsers';
|
||||
|
||||
interface WorkspaceItemProps {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export const WorkspaceItem = ({ name, icon }: WorkspaceItemProps) => {
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<WorkspaceItemAvatar alt={name} src={icon}>
|
||||
{name.charAt(0)}
|
||||
</WorkspaceItemAvatar>
|
||||
<WorkspaceItemContent>
|
||||
<Name title={name}>{name}</Name>
|
||||
</WorkspaceItemContent>
|
||||
<Footer>
|
||||
<FooterUsers />
|
||||
<FooterSetting />
|
||||
</Footer>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const Name = styled('div')(({ theme }) => {
|
||||
return {
|
||||
color: theme.colors.quoteColor,
|
||||
fontSize: theme.font.sm,
|
||||
fontWeight: 400,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
};
|
||||
});
|
||||
|
||||
const StyledWrapper = styled(WorkspaceItemWrapper)({
|
||||
'& .footer-setting': {
|
||||
display: 'none',
|
||||
},
|
||||
':hover .footer-users': {
|
||||
display: 'none',
|
||||
},
|
||||
':hover .footer-setting': {
|
||||
display: 'block',
|
||||
},
|
||||
});
|
||||
|
||||
const Footer = styled('div')({
|
||||
width: '42px',
|
||||
flex: '0 42px',
|
||||
fontSize: '20px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginLeft: '12px',
|
||||
});
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './WorkspaceItem';
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
export * from './PrivateWorkspaceItem';
|
||||
export * from './WorkspaceItem';
|
||||
export * from './CreateWorkspaceItem';
|
||||
export * from './ListItem';
|
||||
export * from './LoginItem';
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import MuiAvatar from '@mui/material/Avatar';
|
||||
import { styled } from '@/styles';
|
||||
|
||||
export const WorkspaceItemWrapper = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
borderRadius: '5px',
|
||||
padding: '6px 12px',
|
||||
|
||||
':hover': {
|
||||
color: theme.colors.primaryColor,
|
||||
backgroundColor: theme.colors.hoverBackground,
|
||||
},
|
||||
}));
|
||||
|
||||
export const PrivateWorkspaceWrapper = styled(WorkspaceItemWrapper)({
|
||||
padding: '10px 12px',
|
||||
});
|
||||
|
||||
export const LoginItemWrapper = styled(WorkspaceItemWrapper)(({ theme }) => {
|
||||
return {
|
||||
padding: '10px 12px',
|
||||
|
||||
':hover .login-description': {
|
||||
color: theme.colors.primaryColor,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const WorkspaceItemAvatar = styled(MuiAvatar)({
|
||||
height: '40px',
|
||||
width: '40px',
|
||||
});
|
||||
|
||||
export const WorkspaceItemContent = styled('div')({
|
||||
minWidth: 0,
|
||||
marginLeft: '12px',
|
||||
flexGrow: 1,
|
||||
});
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { Popper } from '@/ui/popper';
|
||||
import { Avatar, WorkspaceName, SelectorWrapper } from './styles';
|
||||
import { SelectorPopperContent } from './SelectorPopperContent';
|
||||
|
||||
export const WorkspaceSelector = () => {
|
||||
return (
|
||||
<Popper
|
||||
content={<SelectorPopperContent />}
|
||||
zIndex={1000}
|
||||
placement="bottom-start"
|
||||
>
|
||||
<SelectorWrapper>
|
||||
<Avatar alt="Affine" />
|
||||
<WorkspaceName>AFFiNE</WorkspaceName>
|
||||
</SelectorWrapper>
|
||||
</Popper>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './WorkspaceSelector';
|
||||
@@ -0,0 +1,38 @@
|
||||
import MuiAvatar from '@mui/material/Avatar';
|
||||
import { styled } from '@/styles';
|
||||
import { StyledPopperContainer } from '@/ui/shared/Container';
|
||||
|
||||
export const SelectorWrapper = styled('div')({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
export const Avatar = styled(MuiAvatar)({
|
||||
height: '28px',
|
||||
width: '28px',
|
||||
});
|
||||
|
||||
export const WorkspaceName = styled('span')(({ theme }) => {
|
||||
return {
|
||||
marginLeft: '12px',
|
||||
lineHeight: 1,
|
||||
fontSize: '18px',
|
||||
fontWeight: 500,
|
||||
color: theme.colors.iconColor,
|
||||
};
|
||||
});
|
||||
|
||||
export const SelectorPopperContainer = styled(StyledPopperContainer)(
|
||||
({ theme }) => {
|
||||
return {
|
||||
width: '334px',
|
||||
boxShadow: theme.shadow.tooltip,
|
||||
padding: '24px 12px',
|
||||
backgroundColor: theme.colors.pageBackground,
|
||||
fontSize: theme.font.xs,
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
StyledArrowButton,
|
||||
StyledLink,
|
||||
StyledListItem,
|
||||
StyledListItemForWorkspace,
|
||||
StyledNewPageButton,
|
||||
StyledSliderBar,
|
||||
StyledSubListItem,
|
||||
@@ -25,6 +26,7 @@ import { useEditor } from '@/providers/editor-provider';
|
||||
import { useModal } from '@/providers/global-modal-provider';
|
||||
|
||||
import { IconButton } from '@/ui/button';
|
||||
import { WorkspaceSelector } from './WorkspaceSelector';
|
||||
import useLocalStorage from '@/hooks/use-local-storage';
|
||||
import { useTranslation } from '@/libs/i18n';
|
||||
const FavoriteList = ({ showList }: { showList: boolean }) => {
|
||||
@@ -69,6 +71,9 @@ export const WorkSpaceSliderBar = () => {
|
||||
return (
|
||||
<>
|
||||
<StyledSliderBar show={show}>
|
||||
<StyledListItemForWorkspace>
|
||||
<WorkspaceSelector />
|
||||
</StyledListItemForWorkspace>
|
||||
<Tooltip content="Search and quickly jump to a page" placement="right">
|
||||
<StyledListItem
|
||||
onClick={() => {
|
||||
@@ -80,7 +85,7 @@ export const WorkSpaceSliderBar = () => {
|
||||
</Tooltip>
|
||||
<Link href={{ pathname: '/page-list/all' }}>
|
||||
<StyledListItem active={router.pathname === '/page-list/all'}>
|
||||
<AllPagesIcon /> {t('All pages')}
|
||||
<AllPagesIcon /> <span>All pages</span>
|
||||
</StyledListItem>
|
||||
</Link>
|
||||
<StyledListItem active={router.pathname === '/page-list/favorite'}>
|
||||
|
||||
@@ -67,6 +67,10 @@ export const StyledListItem = styled.button<{ active?: boolean }>(
|
||||
}
|
||||
);
|
||||
|
||||
export const StyledListItemForWorkspace = styled(StyledListItem)({
|
||||
height: '52px',
|
||||
});
|
||||
|
||||
export const StyledLink = styled(Link)(({ theme }) => {
|
||||
return {
|
||||
flexGrow: 1,
|
||||
|
||||
Reference in New Issue
Block a user