refactor: move WorkspaceCard (#1803)

This commit is contained in:
Himself65
2023-04-03 18:13:43 -05:00
committed by GitHub
parent dbcadbaf60
commit 9a20f50b05
16 changed files with 143 additions and 25 deletions

View File

@@ -59,6 +59,7 @@ __metadata:
"@storybook/react-vite": 7.0.0
"@storybook/test-runner": 0.10.0-next.12
"@storybook/testing-library": 0.0.14-next.2
"@toeverything/hooks": "workspace:*"
"@types/react": ^18.0.31
"@types/react-dnd": ^3.0.2
"@types/react-dom": 18.0.11
@@ -5707,7 +5708,7 @@ __metadata:
languageName: node
linkType: hard
"@toeverything/hooks@workspace:../../packages/hooks":
"@toeverything/hooks@workspace:*, @toeverything/hooks@workspace:../../packages/hooks":
version: 0.0.0-use.local
resolution: "@toeverything/hooks@workspace:../../packages/hooks"
languageName: unknown

View File

@@ -1,4 +1,5 @@
import { Button, FlexWrapper, MuiFade } from '@affine/component';
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import { useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-blocksuite-workspace-avatar-url';
@@ -13,7 +14,6 @@ import {
JoinedWorkspaceIcon,
LocalWorkspaceIcon,
} from '../../../../pure/icons';
import { WorkspaceAvatar } from '../../../../pure/workspace-avatar';
import type { PanelProps } from '../../index';
import { StyledRow, StyledSettingKey } from '../../style';
import { WorkspaceDeleteModal } from './delete';

View File

@@ -1,128 +0,0 @@
import { UNTITLED_WORKSPACE_NAME } from '@affine/env';
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-blocksuite-workspace-avatar-url';
import type React from 'react';
import { memo } from 'react';
import type { AllWorkspace, BlockSuiteWorkspace } from '../../../shared';
import { stringToColour } from '../../../utils';
interface AvatarProps {
size: number;
name: string;
avatar_url: string;
style?: React.CSSProperties;
}
export const Avatar: React.FC<AvatarProps> = memo<AvatarProps>(function Avatar({
size: _size,
avatar_url,
style,
name,
...props
}) {
const size = _size || 20;
const sizeStr = size + 'px';
return (
<>
{avatar_url ? (
<div
{...props}
style={{
...style,
width: sizeStr,
height: sizeStr,
color: '#fff',
borderRadius: '50%',
overflow: 'hidden',
display: 'inline-block',
verticalAlign: 'middle',
}}
>
<picture>
<img
style={{ width: sizeStr, height: sizeStr }}
src={avatar_url}
alt=""
referrerPolicy="no-referrer"
/>
</picture>
</div>
) : (
<div
{...props}
style={{
...style,
width: sizeStr,
height: sizeStr,
border: '1px solid #fff',
color: '#fff',
fontSize: Math.ceil(0.5 * size) + 'px',
background: stringToColour(name || 'AFFiNE'),
borderRadius: '50%',
display: 'inline-flex',
lineHeight: '1',
justifyContent: 'center',
alignItems: 'center',
userSelect: 'none',
}}
>
{(name || 'AFFiNE').substring(0, 1)}
</div>
)}
</>
);
});
export type WorkspaceUnitAvatarProps = {
size?: number;
workspace: AllWorkspace | null;
style?: React.CSSProperties;
};
export type BlockSuiteWorkspaceAvatar = Omit<
WorkspaceUnitAvatarProps,
'workspace'
> & {
workspace: BlockSuiteWorkspace;
};
export const BlockSuiteWorkspaceAvatar: React.FC<BlockSuiteWorkspaceAvatar> = ({
size = 20,
workspace,
style,
...props
}) => {
const [avatar] = useBlockSuiteWorkspaceAvatarUrl(workspace);
return (
<Avatar
{...props}
size={size}
name={workspace.meta.name ?? UNTITLED_WORKSPACE_NAME}
avatar_url={avatar ?? ''}
style={style}
/>
);
};
export const WorkspaceAvatar: React.FC<WorkspaceUnitAvatarProps> = ({
size = 20,
workspace,
style,
...props
}) => {
if (workspace && 'blockSuiteWorkspace' in workspace) {
return (
<BlockSuiteWorkspaceAvatar
{...props}
size={size}
workspace={workspace.blockSuiteWorkspace}
style={style}
/>
);
}
return (
<Avatar {...props} size={size} name="UNKNOWN" avatar_url="" style={style} />
);
};

View File

@@ -1,113 +0,0 @@
import { useTranslation } from '@affine/i18n';
import { PermissionType } from '@affine/workspace/affine/api';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { SettingsIcon } from '@blocksuite/icons';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-blocksuite-workspace-name';
import type React from 'react';
import { useCallback } from 'react';
import type { AllWorkspace } from '../../../shared';
import {
CloudWorkspaceIcon,
JoinedWorkspaceIcon,
LocalDataIcon,
LocalWorkspaceIcon,
PublishIcon,
} from '../icons';
import { WorkspaceAvatar } from '../workspace-avatar';
import {
StyledCard,
StyledSettingLink,
StyleWorkspaceInfo,
StyleWorkspaceTitle,
} from './styles';
export type WorkspaceTypeProps = {
workspace: AllWorkspace;
};
const WorkspaceType: React.FC<WorkspaceTypeProps> = ({ workspace }) => {
const { t } = useTranslation();
let isOwner = true;
if (workspace.flavour === WorkspaceFlavour.AFFINE) {
isOwner = workspace.permission === PermissionType.Owner;
} else if (workspace.flavour === WorkspaceFlavour.LOCAL) {
isOwner = true;
}
if (workspace.flavour === WorkspaceFlavour.LOCAL) {
return (
<p title={t('Local Workspace')}>
<LocalWorkspaceIcon />
<span>{t('Local Workspace')}</span>
</p>
);
}
return isOwner ? (
<p title={t('Cloud Workspace')}>
<CloudWorkspaceIcon />
<span>{t('Cloud Workspace')}</span>
</p>
) : (
<p title={t('Joined Workspace')}>
<JoinedWorkspaceIcon />
<span>{t('Joined Workspace')}</span>
</p>
);
};
export type WorkspaceCardProps = {
currentWorkspaceId: string | null;
workspace: AllWorkspace;
onClick: (workspace: AllWorkspace) => void;
onSettingClick: (workspace: AllWorkspace) => void;
};
export const WorkspaceCard: React.FC<WorkspaceCardProps> = ({
workspace,
onClick,
onSettingClick,
currentWorkspaceId,
}) => {
const { t } = useTranslation();
const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace);
return (
<StyledCard
data-testid="workspace-card"
onClick={useCallback(() => {
onClick(workspace);
}, [onClick, workspace])}
active={workspace.id === currentWorkspaceId}
>
<WorkspaceAvatar size={58} workspace={workspace} />
<StyleWorkspaceInfo>
<StyleWorkspaceTitle>{name}</StyleWorkspaceTitle>
<WorkspaceType workspace={workspace} />
{workspace.flavour === WorkspaceFlavour.LOCAL && (
<p title={t('Available Offline')}>
<LocalDataIcon />
<span>{t('Available Offline')}</span>
</p>
)}
{workspace.flavour === WorkspaceFlavour.AFFINE && workspace.public && (
<p title={t('Published to Web')}>
<PublishIcon />
<span>{t('Published to Web')}</span>
</p>
)}
</StyleWorkspaceInfo>
<StyledSettingLink
className="setting-entry"
hoverBackground="#fff"
onClick={e => {
e.stopPropagation();
onSettingClick(workspace);
}}
>
<SettingsIcon />
</StyledSettingLink>
</StyledCard>
);
};

View File

@@ -1,92 +0,0 @@
import { displayFlex, styled, textEllipsis } from '@affine/component';
import { IconButton } from '@affine/component';
export const StyleWorkspaceInfo = styled('div')(({ theme }) => {
return {
marginLeft: '15px',
width: '202px',
p: {
height: '20px',
fontSize: theme.font.sm,
...displayFlex('flex-start', 'center'),
},
svg: {
marginRight: '10px',
fontSize: '16px',
flexShrink: 0,
},
span: {
flexGrow: 1,
...textEllipsis(1),
},
};
});
export const StyleWorkspaceTitle = styled('div')(({ theme }) => {
return {
fontSize: theme.font.base,
fontWeight: 600,
lineHeight: '24px',
marginBottom: '10px',
maxWidth: '200px',
...textEllipsis(1),
};
});
export const StyledCard = styled('div')<{
active?: boolean;
}>(({ theme, active }) => {
const borderColor = active ? theme.colors.primaryColor : 'transparent';
return {
width: '310px',
height: '124px',
cursor: 'pointer',
padding: '16px',
boxShadow: '0px 0px 8px rgba(0, 0, 0, 0.1)',
borderRadius: '12px',
border: `1px solid ${borderColor}`,
...displayFlex('flex-start', 'flex-start'),
marginBottom: '24px',
transition: 'background .2s',
background: theme.palette.mode === 'light' ? '#FFF' : '#2C2C2C',
position: 'relative',
':hover': {
background: theme.colors.cardHoverBackground,
'.add-icon': {
borderColor: theme.colors.primaryColor,
color: theme.colors.primaryColor,
},
'.setting-entry': {
opacity: 1,
pointerEvents: 'auto',
},
},
};
});
export const StyledModalHeader = styled('div')(() => {
return {
width: '100%',
height: '72px',
position: 'absolute',
left: 0,
top: 0,
borderRadius: '24px 24px 0 0',
padding: '0 40px',
...displayFlex('space-between', 'center'),
};
});
export const StyledSettingLink = styled(IconButton)(({ theme }) => {
return {
position: 'absolute',
right: '6px',
bottom: '6px',
opacity: 0,
pointerEvents: 'none',
transition: 'all .15s',
':hover': {
background: theme.colors.pageBackground,
},
};
});

View File

@@ -4,13 +4,13 @@ import {
ModalWrapper,
Tooltip,
} from '@affine/component';
import { WorkspaceCard } from '@affine/component/workspace-card';
import { useTranslation } from '@affine/i18n';
import type { AccessTokenMessage } from '@affine/workspace/affine/login';
import { HelpIcon, PlusIcon } from '@blocksuite/icons';
import type { AllWorkspace } from '../../../shared';
import { Footer } from '../footer';
import { WorkspaceCard } from '../workspace-card';
import { LanguageMenu } from './language-menu';
import {
StyledCreateWorkspaceCard,

View File

@@ -1,10 +1,10 @@
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import { CloudWorkspaceIcon, LocalWorkspaceIcon } from '@blocksuite/icons';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-blocksuite-workspace-name';
import type React from 'react';
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
import type { AllWorkspace } from '../../../../shared';
import { WorkspaceAvatar } from '../../workspace-avatar';
import {
StyledSelectorContainer,
StyledSelectorWrapper,

View File

@@ -1,4 +1,4 @@
import '../styles/globals.css';
import '@affine/component/theme/global.css';
import { config, setupGlobal } from '@affine/env';
import { createI18n, I18nextProvider } from '@affine/i18n';

View File

@@ -1,225 +0,0 @@
* {
-webkit-overflow-scrolling: touch;
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
box-sizing: border-box;
/*transition: all 0.1s;*/
}
html,
body,
h1,
h2,
h3,
h4,
h5,
h6,
div,
dl,
dt,
dd,
ul,
ol,
li,
p,
blockquote,
pre,
hr,
figure,
table,
caption,
th,
td,
form,
fieldset,
legend,
input,
button,
textarea,
menu {
margin: 0;
padding: 0;
}
header,
footer,
section,
article,
aside,
nav,
hgroup,
address,
figure,
figcaption,
menu,
details {
display: block;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
caption,
th {
text-align: left;
font-weight: normal;
}
html,
body,
fieldset,
img,
iframe,
abbr {
border: 0;
}
i,
cite,
em,
var,
address,
dfn {
font-style: normal;
}
[hidefocus],
summary {
outline: 0;
}
li {
list-style: none;
}
h1,
h2,
h3,
h4,
h5,
h6,
small {
font-size: 100%;
}
sup,
sub {
font-size: 83%;
}
pre,
code,
kbd,
samp {
font-family: inherit;
}
q:before,
q:after {
content: none;
}
textarea {
overflow: auto;
resize: none;
}
label,
summary {
cursor: default;
}
a,
button {
cursor: pointer;
}
h1,
h2,
h3,
h4,
h5,
h6,
strong,
b {
font-weight: bold;
}
del,
ins,
u,
s,
a,
a:hover {
text-decoration: none;
}
body,
textarea,
input,
button,
select,
keygen,
legend {
background-color: unset;
outline: 0;
border: 0;
font-size: var(--affine-font-base);
line-height: var(--affine-line-height);
font-family: var(--affine-font-family);
}
body {
background: transparent;
overflow: hidden;
}
input {
border: none;
-moz-appearance: none;
-webkit-appearance: none; /*Solve the rounded corners of buttons on ios*/
border-radius: 0; /*Solve the problem of rounded corners of the input box on ios*/
outline: medium; /*Remove the default yellow border on mouse click*/
background-color: transparent;
}
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px white inset;
}
input[type='number'] {
-moz-appearance: textfield;
}
input[type='number']::-webkit-inner-spin-button,
input[type='number']::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
* {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE 10+ */
}
::-webkit-scrollbar {
display: none; /* Chrome Safari */
}
.editor-wrapper {
position: relative;
width: 100%;
height: 100%;
padding: 0 2rem;
}
.affine-default-page-block-title-container {
margin-top: 78px;
margin-bottom: 40px;
transition: margin-top 0.2s;
}
.affine-default-page-block-container {
transition: max-width 0.2s;
min-width: 550px;
}
affine-block-hub {
position: unset !important;
}
.block-hub-menu-container {
position: unset !important;
}
@media (max-width: 768px) {
.affine-default-page-block-title-container {
margin-top: 24px;
}
.editor-wrapper {
width: 100%;
height: 100%;
padding: 0 0.75rem;
}
}