mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-22 15:41:48 +03:00
feat: modify default avatar (#2034)
This commit is contained in:
parent
f8d1513bb6
commit
4a1c15c1e9
@ -42,6 +42,7 @@ export const StyledAvatar = styled('div')(
|
||||
backgroundColor: 'rgba(60, 61, 63, 0.5)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
zIndex: 10,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
import clsx from 'clsx';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
DefaultAvatarBottomItemStyle,
|
||||
DefaultAvatarBottomItemWithAnimationStyle,
|
||||
DefaultAvatarContainerStyle,
|
||||
DefaultAvatarMiddleItemStyle,
|
||||
DefaultAvatarMiddleItemWithAnimationStyle,
|
||||
DefaultAvatarTopItemStyle,
|
||||
} from './index.css';
|
||||
|
||||
const colorsSchema = [
|
||||
['#FF0000', '#FF00E5', '#FFAE73'],
|
||||
['#FF5C00', '#FFC700', '#FFE073'],
|
||||
['#FFDA16', '#FFFBA6', '#FFBE73'],
|
||||
['#8CD317', '#FCFF5C', '#67CAE9'],
|
||||
['#28E19F', '#89FFC6', '#39A880'],
|
||||
['#35B7E0', '#77FFCE', '#5076FF'],
|
||||
['#3D39FF', '#77BEFF', '#3502FF'],
|
||||
['#BD08EB', '#755FFF', '#6967E4'],
|
||||
];
|
||||
|
||||
export const DefaultAvatar = ({ name: propsName }: { name: string }) => {
|
||||
// Sometimes name is ' '
|
||||
const name = propsName || 'A';
|
||||
const colors = useMemo(() => {
|
||||
const index = name[0].toUpperCase().charCodeAt(0);
|
||||
return colorsSchema[index % colorsSchema.length];
|
||||
}, [name]);
|
||||
|
||||
const timer = useRef<ReturnType<typeof setTimeout>>();
|
||||
|
||||
const [topColor, middleColor, bottomColor] = colors;
|
||||
const [isHover, setIsHover] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={DefaultAvatarContainerStyle}
|
||||
onMouseEnter={() => {
|
||||
timer.current = setTimeout(() => {
|
||||
setIsHover(true);
|
||||
}, 300);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
clearTimeout(timer.current);
|
||||
setIsHover(false);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={DefaultAvatarTopItemStyle}
|
||||
style={{ background: bottomColor }}
|
||||
></div>
|
||||
<div
|
||||
className={clsx(DefaultAvatarMiddleItemStyle, {
|
||||
[DefaultAvatarMiddleItemWithAnimationStyle]: isHover,
|
||||
})}
|
||||
style={{ background: middleColor }}
|
||||
></div>
|
||||
<div
|
||||
className={clsx(DefaultAvatarBottomItemStyle, {
|
||||
[DefaultAvatarBottomItemWithAnimationStyle]: isHover,
|
||||
})}
|
||||
style={{ background: topColor }}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default DefaultAvatar;
|
@ -1,4 +1,4 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
import { keyframes, style } from '@vanilla-extract/css';
|
||||
export const avatarStyle = style({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
@ -16,16 +16,130 @@ export const avatarImageStyle = style({
|
||||
objectPosition: 'center',
|
||||
});
|
||||
|
||||
export const avatarTextStyle = style({
|
||||
const bottomAnimation = keyframes({
|
||||
'0%': {
|
||||
top: '-44%',
|
||||
left: '-11%',
|
||||
transform: 'matrix(-0.29, -0.96, 0.94, -0.35, 0, 0)',
|
||||
},
|
||||
'16%': {
|
||||
left: '-18%',
|
||||
top: '-51%',
|
||||
transform: 'matrix(-0.73, -0.69, 0.64, -0.77, 0, 0)',
|
||||
},
|
||||
'32%': {
|
||||
left: '-7%',
|
||||
top: '-40%',
|
||||
transform: 'matrix(-0.97, -0.23, 0.16, -0.99, 0, 0)',
|
||||
},
|
||||
'48%': {
|
||||
left: '-15%',
|
||||
top: '-39%',
|
||||
transform: 'matrix(-0.88, 0.48, -0.6, -0.8, 0, 0)',
|
||||
},
|
||||
'64%': {
|
||||
left: '-7%',
|
||||
top: '-40%',
|
||||
transform: 'matrix(-0.97, -0.23, 0.16, -0.99, 0, 0)',
|
||||
},
|
||||
'80%': {
|
||||
left: '-18%',
|
||||
top: '-51%',
|
||||
transform: 'matrix(-0.73, -0.69, 0.64, -0.77, 0, 0)',
|
||||
},
|
||||
'100%': {
|
||||
top: '-44%',
|
||||
left: '-11%',
|
||||
transform: 'matrix(-0.29, -0.96, 0.94, -0.35, 0, 0)',
|
||||
},
|
||||
});
|
||||
const middleAnimation = keyframes({
|
||||
'0%': {
|
||||
left: '-30px',
|
||||
top: '-30px',
|
||||
transform: 'matrix(-0.48, -0.88, 0.8, -0.6, 0, 0)',
|
||||
},
|
||||
'16%': {
|
||||
left: '-37px',
|
||||
top: '-37px',
|
||||
transform: 'matrix(-0.86, -0.52, 0.39, -0.92, 0, 0)',
|
||||
},
|
||||
'32%': {
|
||||
left: '-20px',
|
||||
top: '-10px',
|
||||
transform: 'matrix(-1, -0.02, -0.12, -0.99, 0, 0)',
|
||||
},
|
||||
'48%': {
|
||||
left: '-27px',
|
||||
top: '-2px',
|
||||
transform: 'matrix(-0.88, 0.48, -0.6, -0.8, 0, 0)',
|
||||
},
|
||||
'64%': {
|
||||
left: '-20px',
|
||||
top: '-10px',
|
||||
transform: 'matrix(-1, -0.02, -0.12, -0.99, 0, 0)',
|
||||
},
|
||||
'80%': {
|
||||
left: '-37px',
|
||||
top: '-37px',
|
||||
transform: 'matrix(-0.86, -0.52, 0.39, -0.92, 0, 0)',
|
||||
},
|
||||
'100%': {
|
||||
left: '-30px',
|
||||
top: '-30px',
|
||||
transform: 'matrix(-0.48, -0.88, 0.8, -0.6, 0, 0)',
|
||||
},
|
||||
});
|
||||
|
||||
export const DefaultAvatarContainerStyle = style({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
border: '1px solid #fff',
|
||||
textAlign: 'center',
|
||||
color: '#fff',
|
||||
position: 'relative',
|
||||
borderRadius: '50%',
|
||||
display: 'inline-flex',
|
||||
lineHeight: '1',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
userSelect: 'none',
|
||||
overflow: 'hidden',
|
||||
});
|
||||
|
||||
export const DefaultAvatarMiddleItemStyle = style({
|
||||
width: '83%',
|
||||
height: '81%',
|
||||
position: 'absolute',
|
||||
left: '-30%',
|
||||
top: '-30%',
|
||||
transform: 'matrix(-0.48, -0.88, 0.8, -0.6, 0, 0)',
|
||||
opacity: '0.8',
|
||||
filter: 'blur(12px)',
|
||||
transformOrigin: 'center center',
|
||||
animation: `${middleAnimation} 3s ease-in-out forwards infinite`,
|
||||
animationPlayState: 'paused',
|
||||
});
|
||||
export const DefaultAvatarMiddleItemWithAnimationStyle = style({
|
||||
animationPlayState: 'running',
|
||||
});
|
||||
export const DefaultAvatarBottomItemStyle = style({
|
||||
width: '98%',
|
||||
height: '97%',
|
||||
position: 'absolute',
|
||||
top: '-44%',
|
||||
left: '-11%',
|
||||
transform: 'matrix(-0.29, -0.96, 0.94, -0.35, 0, 0)',
|
||||
opacity: '0.8',
|
||||
filter: 'blur(12px)',
|
||||
transformOrigin: 'center center',
|
||||
willChange: 'left, top, transform',
|
||||
animation: `${bottomAnimation} 3s ease-in-out forwards infinite`,
|
||||
animationPlayState: 'paused',
|
||||
});
|
||||
export const DefaultAvatarBottomItemWithAnimationStyle = style({
|
||||
animationPlayState: 'running',
|
||||
});
|
||||
export const DefaultAvatarTopItemStyle = style({
|
||||
width: '104%',
|
||||
height: '94%',
|
||||
position: 'absolute',
|
||||
right: '-30%',
|
||||
top: '-30%',
|
||||
opacity: '0.8',
|
||||
filter: 'blur(12px)',
|
||||
transform: 'matrix(-0.28, -0.96, 0.93, -0.37, 0, 0)',
|
||||
transformOrigin: 'center center',
|
||||
});
|
||||
|
@ -10,28 +10,8 @@ import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-
|
||||
import clsx from 'clsx';
|
||||
import type React from 'react';
|
||||
|
||||
import { avatarImageStyle, avatarStyle, avatarTextStyle } from './index.css';
|
||||
|
||||
function stringToColour(str: string) {
|
||||
str = str || 'affine';
|
||||
let colour = '#';
|
||||
let hash = 0;
|
||||
// str to hash
|
||||
for (
|
||||
let i = 0;
|
||||
i < str.length;
|
||||
hash = str.charCodeAt(i++) + ((hash << 5) - hash)
|
||||
);
|
||||
|
||||
// int/hash to hex
|
||||
for (
|
||||
let i = 0;
|
||||
i < 3;
|
||||
colour += ('00' + ((hash >> (i++ * 8)) & 0xff).toString(16)).slice(-2)
|
||||
);
|
||||
|
||||
return colour;
|
||||
}
|
||||
import { DefaultAvatar } from './DefaultAvatar';
|
||||
import { avatarImageStyle, avatarStyle } from './index.css';
|
||||
|
||||
export type WorkspaceAvatarProps = {
|
||||
size?: number;
|
||||
@ -64,13 +44,8 @@ export const BlockSuiteWorkspaceAvatar: React.FC<BlockSuiteWorkspaceAvatar> = ({
|
||||
}}
|
||||
>
|
||||
<RadixAvatar.Image className={avatarImageStyle} src={avatar} alt={name} />
|
||||
<RadixAvatar.Fallback
|
||||
className={avatarTextStyle}
|
||||
style={{
|
||||
backgroundColor: stringToColour(name),
|
||||
}}
|
||||
>
|
||||
{name.substring(0, 1)}
|
||||
<RadixAvatar.Fallback>
|
||||
<DefaultAvatar name={name}></DefaultAvatar>
|
||||
</RadixAvatar.Fallback>
|
||||
</RadixAvatar.Root>
|
||||
);
|
||||
@ -99,13 +74,8 @@ export const WorkspaceAvatar: React.FC<WorkspaceAvatarProps> = ({
|
||||
width: size,
|
||||
}}
|
||||
>
|
||||
<RadixAvatar.Fallback
|
||||
className={avatarTextStyle}
|
||||
style={{
|
||||
backgroundColor: stringToColour('A'),
|
||||
}}
|
||||
>
|
||||
A
|
||||
<RadixAvatar.Fallback>
|
||||
<DefaultAvatar name="A"></DefaultAvatar>
|
||||
</RadixAvatar.Fallback>
|
||||
</RadixAvatar.Root>
|
||||
);
|
||||
|
@ -24,9 +24,6 @@ test('should create a page with a local first avatar', async ({ page }) => {
|
||||
await page.getByTestId('workspace-name').click();
|
||||
await page.getByTestId('workspace-card').nth(0).click();
|
||||
await page.waitForTimeout(1000);
|
||||
const text = await page.getByTestId('workspace-avatar').textContent();
|
||||
// default avatar for default workspace
|
||||
expect(text).toBe('D');
|
||||
await page.getByTestId('workspace-name').click();
|
||||
await page.getByTestId('workspace-card').nth(1).click();
|
||||
const blobUrl = await page
|
||||
|
Loading…
Reference in New Issue
Block a user