Merge pull request #524 from toeverything/feat/modify

Feat/modify
This commit is contained in:
Qi 2022-11-04 11:57:22 +08:00 committed by GitHub
commit 1ccc46ec72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 256 additions and 187 deletions

View File

@ -2,6 +2,69 @@ import type { DOMAttributes, CSSProperties } from 'react';
type IconProps = {
style?: CSSProperties;
} & DOMAttributes<SVGElement>;
export const RightArrow = ({ style = {}, ...props }: IconProps) => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
style={style}
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.667 10.0007L8.33366 6.66732L8.33366 13.334L11.667 10.0007Z"
/>
</svg>
);
};
export const Export2Markdown = ({ style = {}, ...props }: IconProps) => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
style={style}
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.99963 1.66707V1.66699H10.833L16.6663 7.50033V7.50037L16.6663 7.50041V15.8337C16.6663 17.2145 15.5471 18.3337 14.1663 18.3337H5.83301C4.4523 18.3337 3.33301 17.2145 3.33301 15.8337V4.16707C3.33301 2.78636 4.4523 1.66707 5.83301 1.66707H9.99963ZM9.99963 3.00041H5.83301C5.18868 3.00041 4.66634 3.52274 4.66634 4.16707V15.8337C4.66634 16.4781 5.18868 17.0004 5.83301 17.0004H14.1663C14.8107 17.0004 15.333 16.4781 15.333 15.8337V8.33366H12.4996C11.1189 8.33366 9.99963 7.21437 9.99963 5.83366V3.00041ZM11.333 4.05265L14.2806 7.00033H12.4996C11.8553 7.00033 11.333 6.47799 11.333 5.83366V4.05265Z"
/>
<path d="M11.245 14.1671L11.2683 11.2446H11.2508L10.1775 14.1671H9.4775L8.43333 11.2446H8.41583L8.43917 14.1671H7.5V10.0371H8.9175L9.85667 12.6854H9.88L10.7783 10.0371H12.2192V14.1671H11.245Z" />
</svg>
);
};
export const Export2HTML = ({ style = {}, ...props }: IconProps) => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
style={style}
{...props}
>
<path d="M5.35987 18.3335V16.7055H3.84187V18.3335H2.90137V14.4395H3.84187V15.9135H5.35987V14.4395H6.30037V18.3335H5.35987Z" />
<path d="M8.76806 15.2425V18.3335H7.82756V15.2425H6.72756V14.4395H9.86806V15.2425H8.76806Z" />
<path d="M13.8284 18.3335L13.8504 15.578H13.8339L12.8219 18.3335H12.1619L11.1774 15.578H11.1609L11.1829 18.3335H10.2974V14.4395H11.6339L12.5194 16.9365H12.5414L13.3884 14.4395H14.7469V18.3335H13.8284Z" />
<path d="M15.5503 18.3335V14.4395H16.4963V17.514H18.0033V18.3335H15.5503Z" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.99963 1.66707V1.66699H10.833L11.333 2.16699L16.1663 7.00032L16.6663 7.50033V7.50037L16.6663 7.50041V13.3337H15.333V8.33366H15.333H12.4996C11.1189 8.33366 9.99963 7.21437 9.99963 5.83366V3.00041H5.83301C5.18868 3.00041 4.66634 3.52274 4.66634 4.16707V13.3337H3.33301V4.16707C3.33301 2.78636 4.4523 1.66707 5.83301 1.66707H9.99963ZM11.333 4.05265V5.83366C11.333 6.47799 11.8553 7.00032 12.4996 7.00032H14.2806L11.333 4.05265Z"
/>
</svg>
);
};
export const LogoIcon = ({ style = {}, ...props }: IconProps) => {
return (
<svg

View File

@ -1,18 +1,24 @@
import React, { useEffect, useState } from 'react';
import { LogoIcon, MoreIcon, ExportIcon } from './icons';
import {
LogoIcon,
MoreIcon,
ExportIcon,
Export2Markdown,
Export2HTML,
RightArrow,
} from './icons';
import {
StyledHeader,
StyledTitle,
StyledTitleWrapper,
StyledLogo,
StyledHeaderRightSide,
StyledMoreMenuItem,
IconButton,
StyledHeaderContainer,
StyledBrowserWarning,
StyledCloseButton,
StyledMenuItemWrapper,
} from './styles';
import { Popover } from '@/ui/popover';
import { useEditor } from '@/components/editor-provider';
import EditorModeSwitch from '@/components/editor-mode-switch';
import { EdgelessIcon, PaperIcon } from '../editor-mode-switch/icons';
@ -20,34 +26,57 @@ import ThemeModeSwitch from '@/components/theme-mode-switch';
import { useModal } from '@/components/global-modal-provider';
import CloseIcon from '@mui/icons-material/Close';
import { getWarningMessage, shouldShowWarning } from './utils';
import { Menu, MenuItem } from '@/ui/menu';
const PopoverContent = () => {
const { editor, mode, setMode } = useEditor();
return (
<>
<StyledMoreMenuItem
<MenuItem
onClick={() => {
setMode(mode === 'page' ? 'edgeless' : 'page');
}}
>
<StyledMenuItemWrapper>
{mode === 'page' ? <EdgelessIcon /> : <PaperIcon />}
Convert to {mode === 'page' ? 'Edgeless' : 'Page'}
</StyledMoreMenuItem>
<StyledMoreMenuItem
</StyledMenuItemWrapper>
</MenuItem>
<Menu
placement="left-start"
content={
<>
<MenuItem
onClick={() => {
editor && editor.contentParser.onExportHtml();
}}
>
<ExportIcon />
<StyledMenuItemWrapper>
<Export2HTML />
Export to HTML
</StyledMoreMenuItem>
<StyledMoreMenuItem
</StyledMenuItemWrapper>
</MenuItem>
<MenuItem
onClick={() => {
editor && editor.contentParser.onExportMarkdown();
}}
>
<ExportIcon />
<StyledMenuItemWrapper>
<Export2Markdown />
Export to Markdown
</StyledMoreMenuItem>
</StyledMenuItemWrapper>
</MenuItem>
</>
}
>
<MenuItem>
<StyledMenuItemWrapper>
<ExportIcon />
Export
<RightArrow />
</StyledMenuItemWrapper>
</MenuItem>
</Menu>
</>
);
};
@ -115,14 +144,11 @@ export const Header = () => {
<StyledHeaderRightSide>
<ThemeModeSwitch />
<Popover
popoverContent={<PopoverContent />}
style={{ marginLeft: '20px' }}
>
<Menu content={<PopoverContent />} placement="bottom-end">
<IconButton>
<MoreIcon />
</IconButton>
</Popover>
</Menu>
</StyledHeaderRightSide>
</StyledHeader>
</StyledHeaderContainer>

View File

@ -1,4 +1,5 @@
import { absoluteCenter, displayFlex, styled } from '@/styles';
import { displayFlex, styled } from '@/styles';
import { MenuItem } from '@/ui/menu';
export const StyledHeaderContainer = styled.div<{ hasWarning: boolean }>(
({ hasWarning }) => {
@ -20,7 +21,7 @@ export const StyledHeader = styled.div<{ hasWarning: boolean }>(
left: '0',
top: hasWarning ? '36px' : '0',
padding: '0 22px',
zIndex: theme.zIndex.modal,
zIndex: 99,
};
}
);
@ -61,23 +62,23 @@ export const StyledHeaderRightSide = styled('div')({
alignItems: 'center',
});
export const StyledMoreMenuItem = styled('div')(({ theme }) => {
export const StyledMenuItemWrapper = styled.div(({ theme }) => {
return {
height: '32px',
display: 'flex',
alignItems: 'center',
borderRadius: '5px',
fontSize: '14px',
color: theme.colors.popoverColor,
padding: '0 14px',
position: 'relative',
cursor: 'pointer',
...displayFlex('flex-start', 'center'),
svg: {
width: '16px',
height: '16px',
marginRight: '14px',
},
':hover': {
color: theme.colors.primaryColor,
background: theme.colors.hoverBackground,
'svg:nth-child(2)': {
position: 'absolute',
right: 0,
top: 0,
bottom: 0,
margin: 'auto',
},
};
});

View File

@ -5,7 +5,7 @@ export const ModalWrapper = styled.div(({ theme }) => {
return {
width: '348px',
height: '388px',
background: '#FFFFFF',
background: theme.colors.popoverBackground,
borderRadius: '28px',
position: 'relative',
backgroundImage: `url(${bg.src})`,

View File

@ -0,0 +1,2 @@
export * from './menu';
export { StyledMenuItem as MenuItem } from './styles';

View File

@ -0,0 +1,20 @@
import { Popper, type PopperProps } from '../popper';
import { TooltipProps } from '@mui/material';
import { StyledMenuWrapper } from '@/ui/menu/styles';
export const Menu = (props: PopperProps & Omit<TooltipProps, 'title'>) => {
const { content, placement = 'bottom-start', children } = props;
return content ? (
<Popper
{...props}
showArrow={false}
content={
<StyledMenuWrapper placement={placement}>{content}</StyledMenuWrapper>
}
>
{children}
</Popper>
) : null;
};
export default Menu;

View File

@ -0,0 +1,34 @@
import { styled } from '@/styles';
import StyledPopperContainer from '../shared/Container';
export const StyledMenuWrapper = styled(StyledPopperContainer)(({ theme }) => {
return {
background: theme.colors.popoverBackground,
padding: '8px 4px',
fontSize: '14px',
backgroundColor: theme.colors.popoverBackground,
boxShadow: theme.shadow.popover,
color: theme.colors.popoverColor,
};
});
export const StyledMenuItem = styled('div')<{ popperVisible?: boolean }>(
({ theme, popperVisible }) => {
return {
borderRadius: '5px',
padding: '0 14px',
color: popperVisible
? theme.colors.primaryColor
: theme.colors.popoverColor,
backgroundColor: popperVisible
? theme.colors.hoverBackground
: 'transparent',
':hover': {
color: theme.colors.primaryColor,
backgroundColor: theme.colors.hoverBackground,
},
};
}
);

View File

@ -1,65 +0,0 @@
import { useState } from 'react';
import type { CSSProperties, PropsWithChildren, ReactNode } from 'react';
import Grow from '@mui/material/Grow';
import ClickAwayListener from '@mui/base/ClickAwayListener';
import { styled } from '@/styles';
type PopoverProps = {
popoverContent?: ReactNode;
style?: CSSProperties;
};
const StyledPopoverContainer = styled('div')({
position: 'relative',
cursor: 'pointer',
});
const StyledPopoverWrapper = styled('div')({
position: 'absolute',
bottom: '0',
right: '0',
paddingTop: '46px',
zIndex: 1000,
});
const StyledPopover = styled('div')(({ theme }) => {
return {
width: '248px',
background: theme.colors.popoverBackground,
boxShadow: theme.shadow.popover,
color: theme.colors.popoverColor,
borderRadius: '10px 0px 10px 10px',
padding: '8px 4px',
position: 'absolute',
top: '46px',
right: '0',
};
});
export const Popover = ({
children,
popoverContent,
style = {},
}: PropsWithChildren<PopoverProps>) => {
const [show, setShow] = useState(false);
return (
<ClickAwayListener
onClickAway={() => {
setShow(false);
}}
>
<StyledPopoverContainer
onClick={() => {
setShow(!show);
}}
style={style}
>
{children}
<Grow in={show}>
<StyledPopoverWrapper>
<StyledPopover>{popoverContent}</StyledPopover>
</StyledPopoverWrapper>
</Grow>
</StyledPopoverContainer>
</ClickAwayListener>
);
};

View File

@ -4,6 +4,7 @@ import {
useMemo,
useRef,
useState,
cloneElement,
} from 'react';
import PopperUnstyled from '@mui/base/PopperUnstyled';
import ClickAwayListener from '@mui/base/ClickAwayListener';
@ -26,7 +27,6 @@ export const Popper = ({
onVisibleChange,
popoverStyle,
popoverClassName,
anchorStyle,
anchorClassName,
zIndex,
offset = [0, 5],
@ -36,12 +36,9 @@ export const Popper = ({
onClickAway,
...popperProps
}: PopperProps) => {
// @ts-ignore
const [anchorEl, setAnchorEl] = useState<VirtualElement>(null);
const [anchorEl, setAnchorEl] = useState<VirtualElement>();
const [visible, setVisible] = useState(defaultVisible);
// @ts-ignore
const [arrowRef, setArrowRef] = useState<HTMLElement>(null);
const popperRef = useRef();
const [arrowRef, setArrowRef] = useState<HTMLElement>();
const pointerLeaveTimer = useRef<number>();
const pointerEnterTimer = useRef<number>();
@ -95,7 +92,6 @@ export const Popper = ({
};
});
// @ts-ignore
return (
<ClickAwayListener
onClickAway={() => {
@ -107,27 +103,23 @@ export const Popper = ({
}}
>
<Container>
{isAnchorCustom ? null : (
<div
ref={(dom: HTMLDivElement) => setAnchorEl(dom)}
onClick={e => {
{cloneElement(children, {
ref: (dom: HTMLDivElement) => setAnchorEl(dom),
onClick: (e: MouseEvent) => {
if (!hasClickTrigger || visibleControlledByParent) {
// @ts-ignore
onClick?.(e);
return;
}
setVisible(!visible);
}}
onPointerEnter={onPointerEnterHandler}
onPointerLeave={onPointerLeaveHandler}
style={anchorStyle}
className={anchorClassName}
>
{children}
</div>
)}
},
onPointerEnter: onPointerEnterHandler,
onPointerLeave: onPointerLeaveHandler,
className: anchorClassName,
popperVisible: visible,
})}
{content && (
<BasicStyledPopper
// @ts-ignore
popperRef={popperRef}
open={visibleControlledByParent ? propsVisible : visible}
zIndex={zIndex}
anchorEl={isAnchorCustom ? propsAnchorEl : anchorEl}
@ -167,6 +159,7 @@ export const Popper = ({
</Grow>
)}
</BasicStyledPopper>
)}
</Container>
</ClickAwayListener>
);
@ -184,6 +177,6 @@ const BasicStyledPopper = styled(PopperUnstyled, {
zIndex?: number;
}>(({ zIndex, theme }) => {
return {
zIndex: zIndex,
zIndex: zIndex ?? theme.zIndex.popover,
};
});

View File

@ -1,4 +1,4 @@
import type { CSSProperties, ReactNode, Ref } from 'react';
import type { CSSProperties, ReactNode, Ref, ReactElement } from 'react';
import {
type PopperPlacementType,
type PopperUnstyledProps,
@ -18,10 +18,10 @@ export type PopperArrowProps = {
export type PopperProps = {
// Popover content
content: ReactNode;
content?: ReactNode;
// Popover trigger
children?: ReactNode;
children: ReactElement;
// Whether the default is implicit
defaultVisible?: boolean;
@ -47,9 +47,6 @@ export type PopperProps = {
// Popover container class name
popoverClassName?: string;
// Anchor style
anchorStyle?: CSSProperties;
// Anchor class name
anchorClassName?: string;
@ -63,4 +60,4 @@ export type PopperProps = {
popperHandlerRef?: Ref<PopperHandler>;
onClickAway?: () => void;
} & Omit<PopperUnstyledProps, 'open' | 'ref'>;
} & Omit<PopperUnstyledProps, 'open'>;

View File

@ -1,4 +1,3 @@
import { type PropsWithChildren } from 'react';
import StyledPopperContainer from '../shared/Container';
import { Popper, type PopperProps } from '../popper';
import { styled } from '@/styles';
@ -14,16 +13,15 @@ const StyledTooltip = styled(StyledPopperContainer)(({ theme }) => {
};
});
export const Tooltip = (
props: PropsWithChildren<PopperProps & Omit<TooltipProps, 'title'>>
) => {
const { content, placement = 'top-start' } = props;
// If there is no content, hide forever
return content ? (
export const Tooltip = (props: PopperProps & Omit<TooltipProps, 'title'>) => {
const { content, placement = 'top-start', children } = props;
return (
<Popper
{...props}
showArrow={false}
content={<StyledTooltip placement={placement}>{content}</StyledTooltip>}
/>
) : null;
>
{children}
</Popper>
);
};