mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-17 01:56:27 +08:00
feat: refactor ui components
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import {
|
||||
HTMLAttributes,
|
||||
cloneElement,
|
||||
ReactElement,
|
||||
Children,
|
||||
CSSProperties,
|
||||
forwardRef,
|
||||
} from 'react';
|
||||
import { StyledIconButton } from './styles';
|
||||
|
||||
const SIZE_SMALL = 'small' as const;
|
||||
const SIZE_MIDDLE = 'middle' as const;
|
||||
const SIZE_NORMAL = 'normal' as const;
|
||||
// TODO: Designer is not sure about the size of the icon button
|
||||
const SIZE_CONFIG = {
|
||||
[SIZE_SMALL]: {
|
||||
iconSize: 16,
|
||||
areaSize: 24,
|
||||
},
|
||||
[SIZE_MIDDLE]: {
|
||||
iconSize: 20,
|
||||
areaSize: 28,
|
||||
},
|
||||
[SIZE_NORMAL]: {
|
||||
iconSize: 24,
|
||||
areaSize: 32,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type IconButtonProps = {
|
||||
size?:
|
||||
| typeof SIZE_SMALL
|
||||
| typeof SIZE_MIDDLE
|
||||
| typeof SIZE_NORMAL
|
||||
| [number, number];
|
||||
iconSize?:
|
||||
| typeof SIZE_SMALL
|
||||
| typeof SIZE_MIDDLE
|
||||
| typeof SIZE_NORMAL
|
||||
| [number, number];
|
||||
disabled?: boolean;
|
||||
hoverBackground?: string;
|
||||
hoverColor?: string;
|
||||
hoverStyle?: CSSProperties;
|
||||
children: ReactElement<HTMLAttributes<SVGElement>, 'svg'>;
|
||||
} & HTMLAttributes<HTMLButtonElement>;
|
||||
|
||||
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
||||
(
|
||||
{
|
||||
size = 'normal',
|
||||
iconSize = 'normal',
|
||||
disabled = false,
|
||||
children,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const [width, height] = Array.isArray(size)
|
||||
? size
|
||||
: [SIZE_CONFIG[size]['areaSize'], SIZE_CONFIG[size]['areaSize']];
|
||||
const [iconWidth, iconHeight] = Array.isArray(iconSize)
|
||||
? iconSize
|
||||
: [SIZE_CONFIG[iconSize]['iconSize'], SIZE_CONFIG[iconSize]['iconSize']];
|
||||
|
||||
return (
|
||||
<StyledIconButton
|
||||
ref={ref}
|
||||
disabled={disabled}
|
||||
width={width}
|
||||
height={height}
|
||||
borderRadius={iconWidth / 4}
|
||||
{...props}
|
||||
>
|
||||
{cloneElement(Children.only(children), {
|
||||
width: iconWidth,
|
||||
height: iconHeight,
|
||||
})}
|
||||
</StyledIconButton>
|
||||
);
|
||||
}
|
||||
);
|
||||
IconButton.displayName = 'IconButton';
|
||||
|
||||
export default IconButton;
|
||||
@@ -0,0 +1 @@
|
||||
export * from './icon-button';
|
||||
@@ -0,0 +1,55 @@
|
||||
import { absoluteCenter, displayFlex, styled } from '@/styles';
|
||||
import { CSSProperties } from 'react';
|
||||
|
||||
export const StyledIconButton = styled.button<{
|
||||
width: number;
|
||||
height: number;
|
||||
borderRadius: number;
|
||||
disabled?: boolean;
|
||||
hoverBackground?: string;
|
||||
hoverColor?: string;
|
||||
hoverStyle?: CSSProperties;
|
||||
}>(
|
||||
({
|
||||
theme,
|
||||
width,
|
||||
height,
|
||||
disabled,
|
||||
hoverBackground,
|
||||
hoverColor,
|
||||
hoverStyle,
|
||||
}) => {
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
color: theme.colors.iconColor,
|
||||
...displayFlex('center', 'center'),
|
||||
position: 'relative',
|
||||
...(disabled ? { cursor: 'not-allowed', pointerEvents: 'none' } : {}),
|
||||
transition: 'background .15s',
|
||||
|
||||
// TODO: we need to add @emotion/babel-plugin
|
||||
'::after': {
|
||||
content: '""',
|
||||
width,
|
||||
height,
|
||||
borderRadius: width / 5,
|
||||
transition: 'background .15s',
|
||||
...absoluteCenter({ horizontal: true, vertical: true }),
|
||||
},
|
||||
|
||||
svg: {
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
},
|
||||
|
||||
':hover': {
|
||||
color: hoverColor ?? theme.colors.primaryColor,
|
||||
'::after': {
|
||||
background: hoverBackground ?? theme.colors.hoverBackground,
|
||||
},
|
||||
...(hoverStyle ?? {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './menu';
|
||||
export { StyledMenuItem as MenuItem } from './styles';
|
||||
// export { StyledMenuItem as MenuItem } from './styles';
|
||||
export * from './menu-item';
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
cloneElement,
|
||||
forwardRef,
|
||||
HTMLAttributes,
|
||||
PropsWithChildren,
|
||||
ReactElement,
|
||||
} from 'react';
|
||||
import { StyledMenuItem, StyledArrow } from './styles';
|
||||
|
||||
export type IconMenuProps = PropsWithChildren<{
|
||||
isDir?: boolean;
|
||||
icon?: ReactElement;
|
||||
}> &
|
||||
HTMLAttributes<HTMLButtonElement>;
|
||||
|
||||
export const MenuItem = forwardRef<HTMLButtonElement, IconMenuProps>(
|
||||
({ isDir = false, icon, children, ...props }, ref) => {
|
||||
return (
|
||||
<StyledMenuItem ref={ref} {...props}>
|
||||
{icon &&
|
||||
cloneElement(icon, {
|
||||
width: 16,
|
||||
height: 16,
|
||||
style: {
|
||||
marginRight: 14,
|
||||
},
|
||||
})}
|
||||
{children}
|
||||
{isDir ? <StyledArrow /> : null}
|
||||
</StyledMenuItem>
|
||||
);
|
||||
}
|
||||
);
|
||||
MenuItem.displayName = 'MenuItem';
|
||||
export default MenuItem;
|
||||
@@ -1,5 +1,6 @@
|
||||
import { styled } from '@/styles';
|
||||
import { displayFlex, styled } from '@/styles';
|
||||
import StyledPopperContainer from '../shared/Container';
|
||||
import { MiddleArrowRightIcon } from '@blocksuite/icons';
|
||||
|
||||
export const StyledMenuWrapper = styled(StyledPopperContainer)(({ theme }) => {
|
||||
return {
|
||||
@@ -12,23 +13,39 @@ export const StyledMenuWrapper = styled(StyledPopperContainer)(({ theme }) => {
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledMenuItem = styled('div')<{ popperVisible?: boolean }>(
|
||||
({ theme, popperVisible }) => {
|
||||
return {
|
||||
borderRadius: '5px',
|
||||
padding: '0 14px',
|
||||
export const StyledArrow = styled(MiddleArrowRightIcon)(({ theme }) => {
|
||||
return {
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
margin: 'auto',
|
||||
};
|
||||
});
|
||||
|
||||
color: popperVisible
|
||||
? theme.colors.primaryColor
|
||||
: theme.colors.popoverColor,
|
||||
backgroundColor: popperVisible
|
||||
? theme.colors.hoverBackground
|
||||
: 'transparent',
|
||||
export const StyledMenuItem = styled.button<{
|
||||
popperVisible?: boolean;
|
||||
isDir?: boolean;
|
||||
}>(({ theme, popperVisible, isDir = false }) => {
|
||||
return {
|
||||
width: '100%',
|
||||
borderRadius: '5px',
|
||||
padding: '0 14px',
|
||||
fontSize: '14px',
|
||||
height: '32px',
|
||||
...displayFlex('flex-start', 'center'),
|
||||
cursor: isDir ? 'pointer' : '',
|
||||
position: 'relative',
|
||||
color: popperVisible
|
||||
? theme.colors.primaryColor
|
||||
: theme.colors.popoverColor,
|
||||
backgroundColor: popperVisible
|
||||
? theme.colors.hoverBackground
|
||||
: 'transparent',
|
||||
|
||||
':hover': {
|
||||
color: theme.colors.primaryColor,
|
||||
backgroundColor: theme.colors.hoverBackground,
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
':hover': {
|
||||
color: theme.colors.primaryColor,
|
||||
backgroundColor: theme.colors.hoverBackground,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -48,11 +48,11 @@ export const StyledTableRow = styled.tr(({ theme }) => {
|
||||
td: {
|
||||
transition: 'background .15s',
|
||||
},
|
||||
'td:first-child': {
|
||||
'td:first-of-type': {
|
||||
borderTopLeftRadius: '10px',
|
||||
borderBottomLeftRadius: '10px',
|
||||
},
|
||||
'td:last-child': {
|
||||
'td:last-of-type': {
|
||||
borderTopRightRadius: '10px',
|
||||
borderBottomRightRadius: '10px',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user