Merge branch 'feat/filesystem_and_search' of github.com:toeverything/AFFiNE into feat/filesystem_and_search

This commit is contained in:
alt0
2022-12-20 00:04:46 +08:00
6 changed files with 364 additions and 11 deletions
@@ -0,0 +1,200 @@
import { EmailIcon } from '@blocksuite/icons';
import { styled } from '@/styles';
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button';
import Input from '@/ui/input';
import { useState } from 'react';
interface LoginModalProps {
open: boolean;
onClose: () => void;
}
export const debounce = <T extends (...args: any) => any>(
fn: T,
time?: number,
immediate?: boolean
): ((...args: any) => any) => {
let timeoutId: null | number;
let defaultImmediate = immediate || false;
let delay = time || 300;
return (...args: any) => {
if (defaultImmediate) {
fn.apply(this, args); // 确保引用函数的指向正确,并且函数的参数也不变
defaultImmediate = false;
return;
}
if (timeoutId) {
clearTimeout(timeoutId);
}
// @ts-ignore
timeoutId = setTimeout(() => {
fn.apply(this, args);
timeoutId = null;
}, delay);
};
};
const gmailReg = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@gmail\.com$/;
export const InviteMembers = ({ open, onClose }: LoginModalProps) => {
const [canInvite, setCanInvite] = useState<boolean>(true);
const [showMember, setShowMember] = useState<boolean>(true);
const [showTip, setShowTip] = useState<boolean>(true);
const InputChange = (value: string) => {
setShowMember(true);
if (gmailReg.test(value)) {
setShowTip(false);
}
};
return (
<div>
<Modal open={open} onClose={onClose}>
<ModalWrapper width={460} height={236}>
<Header>
<ModalCloseButton
top={6}
right={6}
onClick={() => {
onClose();
}}
/>
</Header>
<Content>
<ContentTitle>Invite members</ContentTitle>
<InviteBox>
<Input
width={360}
onChange={InputChange}
onBlur={() => {
setShowMember(false);
}}
placeholder="Search mail (Gmail support only)"
></Input>
{showMember ? (
<Members>
{showTip ? (
<NoFind>Non-Gmail is not supported</NoFind>
) : (
<Member>
<MemberIcon>
<EmailIcon></EmailIcon>
</MemberIcon>
<Email>huojoe@gmail.com</Email>
<div>invited</div>
</Member>
)}
</Members>
) : (
<></>
)}
</InviteBox>
</Content>
<Footer>
<Button shape="circle" type="primary" disabled={canInvite}>
Invite
</Button>
</Footer>
</ModalWrapper>
</Modal>
</div>
);
};
const Header = styled('div')({
position: 'relative',
height: '44px',
});
const Content = styled('div')({
display: 'flex',
padding: '0 48px',
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',
textAlign: 'center',
});
const InviteBox = styled('div')({
position: 'relative',
});
const Members = styled('div')(({ theme }) => {
return {
position: 'absolute',
width: '100%',
background: theme.colors.pageBackground,
textAlign: 'left',
zIndex: 1,
borderRadius: '0px 10px 10px 10px',
height: '56px',
padding: '8px 12px',
input: {
'&::placeholder': {
color: theme.colors.placeHolderColor,
},
},
};
});
const NoFind = styled('div')(({ theme }) => {
return {
color: theme.colors.iconColor,
fontSize: theme.font.sm,
lineHeight: '40px',
userSelect: 'none',
width: '100%',
};
});
const Member = styled('div')(({ theme }) => {
return {
color: theme.colors.iconColor,
fontSize: theme.font.sm,
lineHeight: '40px',
userSelect: 'none',
display: 'flex',
};
});
const MemberIcon = styled('div')(({ theme }) => {
return {
width: '40px',
height: '40px',
borderRadius: '50%',
color: theme.colors.primaryColor,
background: '#F5F5F5',
marginRight: '8px',
textAlign: 'center',
lineHeight: '45px',
// icon size
fontSize: '20px',
overflow: 'hidden',
img: {
width: '100%',
height: '100%',
},
};
});
const Email = styled('div')(({ theme }) => {
return {
flex: '1',
color: theme.colors.popoverColor,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
};
});
@@ -1,13 +1,16 @@
import { styled } from '@/styles';
import { Button } from '@/ui/button';
import MuiAvatar from '@mui/material/Avatar';
export const StyledSettingContent = styled('div')(({ theme }) => {
export const StyledSettingContainer = styled('div')(({ theme }) => {
return {
position: 'relative',
display: 'flex',
padding: '0px',
width: '1080px',
width: '961px',
background: theme.colors.popoverBackground,
borderRadius: '12px',
overflow: 'hidden',
};
});
@@ -15,7 +18,23 @@ export const StyledSettingSidebar = styled('div')(({ theme }) => {
{
return {
width: '236px',
height: '780px',
height: '620px',
background: theme.mode === 'dark' ? '#272727' : '#FBFBFC',
};
}
});
export const StyledSettingContent = styled('div')(({ theme }) => {
return {
paddingLeft: '48px',
};
});
export const StyledSetting = styled('div')(({ theme }) => {
{
return {
width: '236px',
height: '620px',
background: theme.mode === 'dark' ? '#272727' : '#FBFBFC',
};
}
@@ -71,10 +90,47 @@ export const StyledSettingTagIconContainer = styled('div')(({ theme }) => {
};
});
export const StyledSettingH2 = styled('h2')(({ theme }) => {
export const StyledSettingH2 = styled('h2')<{ marginTop?: number }>(
({ marginTop, theme }) => {
return {
fontWeight: '500',
fontSize: '18px',
lineHeight: '26px',
marginTop: marginTop ? `${marginTop}px` : '0px',
};
}
);
export const StyledSettingAvatarContent = styled('div')(({ theme }) => {
return {
fontWeight: '500',
fontSize: '18px',
lineHeight: '26px',
marginTop: '12px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
height: '72px',
};
});
export const StyledSettingAvatar = styled(MuiAvatar)(({ theme }) => {
return { height: '72px', width: '72px' };
});
export const StyledAvatarUploadBtn = styled(Button)(({ theme }) => {
return {
backgroundColor: theme.colors.hoverBackground,
color: theme.colors.primaryColor,
margin: '0 12px 0 24px',
};
});
export const StyledSettingInputContainer = styled('div')(({ theme }) => {
return {
marginTop: '12px',
};
});
export const StyledDeleteButtonContainer = styled('div')(({ theme }) => {
return {
marginTop: '158px',
};
});
@@ -1,7 +1,13 @@
import Modal from '@/ui/modal';
import {
StyledAvatarUploadBtn,
StyledDeleteButtonContainer,
StyledSettingAvatar,
StyledSettingAvatarContent,
StyledSettingContainer,
StyledSettingContent,
StyledSettingH2,
StyledSettingInputContainer,
StyledSettingSidebar,
StyledSettingSidebarHeader,
StyledSettingTabContainer,
@@ -10,6 +16,8 @@ import {
} from './style';
import { EditIcon, UsersIcon, PublishIcon } from '@blocksuite/icons';
import { useState } from 'react';
import { Button } from '@/ui/button';
import Input from '@/ui/input';
enum ActiveTab {
'general' = 'general',
@@ -69,7 +77,7 @@ export const WorkspaceSetting = () => {
};
return (
<Modal open={true}>
<StyledSettingContent>
<StyledSettingContainer>
<StyledSettingSidebar>
<StyledSettingSidebarHeader>
Workspace Settings
@@ -79,8 +87,10 @@ export const WorkspaceSetting = () => {
onTabChange={handleTabChange}
/>
</StyledSettingSidebar>
{activeTab === ActiveTab.general && <GeneralPage />}
</StyledSettingContent>
<StyledSettingContent>
{activeTab === ActiveTab.general && <GeneralPage />}
</StyledSettingContent>
</StyledSettingContainer>
</Modal>
);
};
@@ -88,7 +98,25 @@ export const WorkspaceSetting = () => {
const GeneralPage = () => {
return (
<div>
<StyledSettingH2>Workspace Avatar</StyledSettingH2>
<StyledSettingH2 marginTop={56}>Workspace Avatar</StyledSettingH2>
<StyledSettingAvatarContent>
<StyledSettingAvatar alt="workspace avatar">W</StyledSettingAvatar>
<StyledAvatarUploadBtn shape="round">upload</StyledAvatarUploadBtn>
<Button shape="round">remove</Button>
</StyledSettingAvatarContent>
<StyledSettingH2 marginTop={36}>Workspace Name</StyledSettingH2>
<StyledSettingInputContainer>
<Input width={327} placeholder="Workspace Name"></Input>
</StyledSettingInputContainer>
<StyledSettingH2 marginTop={36}>Workspace Owner</StyledSettingH2>
<StyledSettingInputContainer>
<Input width={327} placeholder="Workspace Owner"></Input>
</StyledSettingInputContainer>
<StyledDeleteButtonContainer>
<Button type="danger" shape="circle">
Delete Workspace
</Button>
</StyledDeleteButtonContainer>
</div>
);
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

+63
View File
@@ -0,0 +1,63 @@
import Icon from './Icon.png';
import { styled } from '@/styles';
const User = ({ name, avatar }: { name: string; avatar?: string }) => {
return (
<UserContent>
<UserIcon>{name.slice(0, 1)}</UserIcon> {name}
</UserContent>
);
};
export default function DevPage() {
return (
<Invited>
<div>
<ImageStyle src={Icon.src}></ImageStyle>
<Content>
<User name={'Svaney'}></User> invited you to join
<User name={'Dev Space'}></User>
<p>Successfully joined</p>
</Content>
</div>
</Invited>
);
}
const ImageStyle = styled('img')({
width: '400px',
height: '400px',
});
const UserIcon = styled('span')({
display: 'inline-block',
width: '28px',
height: '28px',
borderRadius: '50%',
backgroundColor: '#FFF5AB',
textAlign: 'center',
color: '#896406',
lineHeight: '28px',
});
const Invited = styled('div')({
height: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center',
});
const Content = styled('div')({
fontSize: '16px',
});
const UserContent = styled('span')({
fontSize: '18px',
margin: '0 5px',
});
const InviteStatus = styled('div')({
fontSize: '16px',
marginTop: '16px',
});
+6
View File
@@ -8,6 +8,7 @@ type inputProps = {
width?: number;
maxLength?: number;
onChange?: (value: string) => void;
onBlur?: (e: any) => void;
};
export const Input = (props: inputProps) => {
@@ -18,12 +19,16 @@ export const Input = (props: inputProps) => {
maxLength,
width = 260,
onChange,
onBlur,
} = props;
const [value, setValue] = useState<string>(valueProp || '');
const handleChange: InputHTMLAttributes<HTMLInputElement>['onChange'] = e => {
setValue(e.target.value);
onChange && onChange(e.target.value);
};
const handleBlur: InputHTMLAttributes<HTMLInputElement>['onBlur'] = e => {
onBlur && onBlur(e);
};
return (
<StyledInput
value={value}
@@ -32,6 +37,7 @@ export const Input = (props: inputProps) => {
width={width}
maxLength={maxLength}
onChange={handleChange}
onBlur={handleBlur}
></StyledInput>
);
};