diff --git a/packages/app/src/components/invite-members/index.tsx b/packages/app/src/components/invite-members/index.tsx new file mode 100644 index 0000000000..3376c9b0ea --- /dev/null +++ b/packages/app/src/components/invite-members/index.tsx @@ -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 = 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(true); + const [showMember, setShowMember] = useState(true); + const [showTip, setShowTip] = useState(true); + + const InputChange = (value: string) => { + setShowMember(true); + if (gmailReg.test(value)) { + setShowTip(false); + } + }; + return ( +
+ + +
+ { + onClose(); + }} + /> +
+ + Invite members + + { + setShowMember(false); + }} + placeholder="Search mail (Gmail support only)" + > + {showMember ? ( + + {showTip ? ( + Non-Gmail is not supported + ) : ( + + + + + huojoe@gmail.com +
invited
+
+ )} +
+ ) : ( + <> + )} +
+
+
+ +
+
+
+
+ ); +}; + +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', + }; +}); diff --git a/packages/app/src/components/workspace-setting/style.ts b/packages/app/src/components/workspace-setting/style.ts index ec41b17dae..22c1d6e344 100644 --- a/packages/app/src/components/workspace-setting/style.ts +++ b/packages/app/src/components/workspace-setting/style.ts @@ -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', }; }); diff --git a/packages/app/src/components/workspace-setting/workspace-setting.tsx b/packages/app/src/components/workspace-setting/workspace-setting.tsx index 8e059d6838..d405510491 100644 --- a/packages/app/src/components/workspace-setting/workspace-setting.tsx +++ b/packages/app/src/components/workspace-setting/workspace-setting.tsx @@ -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 ( - + Workspace Settings @@ -79,8 +87,10 @@ export const WorkspaceSetting = () => { onTabChange={handleTabChange} /> - {activeTab === ActiveTab.general && } - + + {activeTab === ActiveTab.general && } + + ); }; @@ -88,7 +98,25 @@ export const WorkspaceSetting = () => { const GeneralPage = () => { return (
- Workspace Avatar + Workspace Avatar + + W + upload + + + Workspace Name + + + + Workspace Owner + + + + + +
); }; diff --git a/packages/app/src/pages/invite/Icon.png b/packages/app/src/pages/invite/Icon.png new file mode 100644 index 0000000000..15dea37003 Binary files /dev/null and b/packages/app/src/pages/invite/Icon.png differ diff --git a/packages/app/src/pages/invite/index.tsx b/packages/app/src/pages/invite/index.tsx new file mode 100644 index 0000000000..e40c0f1b0f --- /dev/null +++ b/packages/app/src/pages/invite/index.tsx @@ -0,0 +1,63 @@ +import Icon from './Icon.png'; +import { styled } from '@/styles'; + +const User = ({ name, avatar }: { name: string; avatar?: string }) => { + return ( + + {name.slice(0, 1)} {name} + + ); +}; + +export default function DevPage() { + return ( + +
+ + + invited you to join + +

Successfully joined

+
+
+
+ ); +} + +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', +}); diff --git a/packages/app/src/ui/input/input.tsx b/packages/app/src/ui/input/input.tsx index 5cda33d51e..920599881d 100644 --- a/packages/app/src/ui/input/input.tsx +++ b/packages/app/src/ui/input/input.tsx @@ -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(valueProp || ''); const handleChange: InputHTMLAttributes['onChange'] = e => { setValue(e.target.value); onChange && onChange(e.target.value); }; + const handleBlur: InputHTMLAttributes['onBlur'] = e => { + onBlur && onBlur(e); + }; return ( { width={width} maxLength={maxLength} onChange={handleChange} + onBlur={handleBlur} > ); };