diff --git a/packages/app/src/components/Header/icons.tsx b/packages/app/src/components/Header/icons.tsx index b0f5374169..deea320cd8 100644 --- a/packages/app/src/components/Header/icons.tsx +++ b/packages/app/src/components/Header/icons.tsx @@ -1,24 +1,17 @@ import type { DOMAttributes, CSSProperties } from 'react'; - type IconProps = { - color?: string; style?: CSSProperties; } & DOMAttributes; - -export const LogoIcon = ({ - color, - style: propsStyle = {}, - ...props -}: IconProps) => { - const style = { fill: color, ...propsStyle }; +export const LogoIcon = ({ style = {}, ...props }: IconProps) => { return ( { - const style = { fill: color, ...propsStyle }; - +export const MoonIcon = ({ style = {}, ...props }: IconProps) => { return ( - - - - ); -}; - -export const MoonIcon = ({ - color, - style: propsStyle = {}, - ...props -}: IconProps) => { - const style = { fill: color, ...propsStyle }; - - return ( - { - const style = { fill: color, ...propsStyle }; +export const SunIcon = ({ style = {}, ...props }: IconProps) => { return ( - - - - - ); -}; - -export const SunIcon = ({ - color, - style: propsStyle = {}, - ...props -}: IconProps) => { - const style = { fill: color, ...propsStyle }; - - return ( - { - const style = { fill: color, ...propsStyle, transform: 'rotate(90deg)' }; - +export const MoreIcon = ({ style = {}, ...props }: IconProps) => { return ( @@ -162,26 +78,19 @@ export const MoreIcon = ({ ); }; -export const ExportIcon = ({ - color, - style: propsStyle = {}, - ...props -}: IconProps) => { - const style = { fill: color, ...propsStyle }; +export const ExportIcon = ({ style = {}, ...props }: IconProps) => { return ( ); diff --git a/packages/app/src/components/Header/index.tsx b/packages/app/src/components/Header/index.tsx index a082915dd5..bf1fad41e2 100644 --- a/packages/app/src/components/Header/index.tsx +++ b/packages/app/src/components/Header/index.tsx @@ -1,8 +1,6 @@ import React, { useEffect, useState } from 'react'; import { LogoIcon, - PaperIcon, - EdgelessIcon, SunIcon, MoonIcon, MoreIcon, @@ -13,56 +11,14 @@ import { StyledTitle, StyledTitleWrapper, StyledLogo, - StyledModeSwitch, StyledHeaderRightSide, StyledMoreMenuItem, + IconButton, } from './styles'; import { Popover } from '@/components/popover'; import { useTheme } from '@/styles'; import { useEditor } from '@/components/editor-provider'; -import { AnimateRadio } from '@/components/animate-radio'; - -const PaperItem = ({ active }: { active?: boolean }) => { - const { - theme: { - colors: { highlight, disabled }, - }, - } = useTheme(); - - return ; -}; - -const EdgelessItem = ({ active }: { active?: boolean }) => { - const { - theme: { - colors: { highlight, disabled }, - }, - } = useTheme(); - - return ; -}; -const EditorModeSwitch = ({ isHover }: { isHover: boolean }) => { - const handleModeSwitch = (mode: 'page' | 'edgeless') => { - const event = new CustomEvent('affine.switch-mode', { detail: mode }); - window.dispatchEvent(event); - }; - return ( - } - labelRight="Edgeless" - iconRight={} - style={{ - marginRight: '12px', - }} - initialValue="left" - onChange={value => { - handleModeSwitch(value === 'left' ? 'page' : 'edgeless'); - }} - /> - ); -}; +import EditorModeSwitch from '@/components/editor-mode-switch'; const DarkModeSwitch = () => { const { changeMode, mode } = useTheme(); @@ -71,16 +27,14 @@ const DarkModeSwitch = () => { <> {mode === 'dark' ? ( { changeMode('light'); }} > ) : ( { changeMode('dark'); }} @@ -130,26 +84,36 @@ export const Header = () => { }, [editor]); return ( - { - setIsHover(true); - }} - onMouseLeave={() => { - setIsHover(false); - }} - > + - {}} /> + {}} /> - - + { + setIsHover(true); + }} + onMouseLeave={() => { + setIsHover(false); + }} + > + {title} - }> - + } + style={{ marginLeft: '20px' }} + > + + + diff --git a/packages/app/src/components/Header/styles.ts b/packages/app/src/components/Header/styles.ts index e2175e7aa7..e690dcbd09 100644 --- a/packages/app/src/components/Header/styles.ts +++ b/packages/app/src/components/Header/styles.ts @@ -71,3 +71,20 @@ export const StyledMoreMenuItem = styled('div')({ }, }, }); + +export const IconButton = styled('div')(({ theme }) => { + return { + width: '32px', + height: '32px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + color: theme.colors.disabled, + background: 'transparent', + borderRadius: '5px', + ':hover': { + color: theme.colors.highlight, + background: '#F1F3FF', + }, + }; +}); diff --git a/packages/app/src/components/animate-radio/icons.tsx b/packages/app/src/components/animate-radio/icons.tsx deleted file mode 100644 index cc93ced81f..0000000000 --- a/packages/app/src/components/animate-radio/icons.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { CSSProperties, DOMAttributes } from 'react'; - -type IconProps = { - color?: string; - style?: CSSProperties; -} & DOMAttributes; - -export const ArrowIcon = ({ - color, - style: propsStyle = {}, - direction = 'right', - ...props -}: IconProps & { direction?: 'left' | 'right' | 'middle' }) => { - const style = { - fill: color, - transform: `rotate(${direction === 'left' ? '0' : '180deg'})`, - opacity: direction === 'middle' ? 0 : 1, - ...propsStyle, - }; - return ( - - - - ); -}; diff --git a/packages/app/src/components/editor-mode-switch/icons.tsx b/packages/app/src/components/editor-mode-switch/icons.tsx new file mode 100644 index 0000000000..e59abf234f --- /dev/null +++ b/packages/app/src/components/editor-mode-switch/icons.tsx @@ -0,0 +1,85 @@ +import { CSSProperties, DOMAttributes } from 'react'; + +type IconProps = { + style?: CSSProperties; +} & DOMAttributes; + +export const ArrowIcon = ({ + style: propsStyle = {}, + direction = 'right', + ...props +}: IconProps & { direction?: 'left' | 'right' | 'middle' }) => { + const style = { + transform: `rotate(${direction === 'left' ? '0' : '180deg'})`, + opacity: direction === 'middle' ? 0 : 1, + ...propsStyle, + }; + return ( + + + + ); +}; + +export const PaperIcon = ({ style = {}, ...props }: IconProps) => { + return ( + + + + + + ); +}; + +export const EdgelessIcon = ({ style = {}, ...props }: IconProps) => { + return ( + + + + + ); +}; diff --git a/packages/app/src/components/animate-radio/index.tsx b/packages/app/src/components/editor-mode-switch/index.tsx similarity index 54% rename from packages/app/src/components/animate-radio/index.tsx rename to packages/app/src/components/editor-mode-switch/index.tsx index 4919581f80..9558abc756 100644 --- a/packages/app/src/components/animate-radio/index.tsx +++ b/packages/app/src/components/editor-mode-switch/index.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, cloneElement } from 'react'; +import React, { useState, useEffect, cloneElement } from 'react'; import { StyledAnimateRadioContainer, StyledRadioMiddle, @@ -7,12 +7,34 @@ import { StyledLabel, StyledIcon, } from './style'; -import { ArrowIcon } from './icons'; import type { RadioItemStatus, AnimateRadioProps, AnimateRadioItemProps, } from './type'; +import { useTheme } from '@/styles'; +import { EdgelessIcon, PaperIcon } from './icons'; +import { useEditor } from '@/components/editor-provider'; + +const PaperItem = ({ active }: { active?: boolean }) => { + const { + theme: { + colors: { highlight, disabled }, + }, + } = useTheme(); + + return ; +}; + +const EdgelessItem = ({ active }: { active?: boolean }) => { + const { + theme: { + colors: { highlight, disabled }, + }, + } = useTheme(); + + return ; +}; const AnimateRadioItem = ({ active, @@ -24,7 +46,7 @@ const AnimateRadioItem = ({ }: AnimateRadioItemProps) => { return ( - + {cloneElement(icon, { active, })} @@ -44,37 +66,32 @@ const RadioMiddle = ({ }) => { return ( ); }; -export const AnimateRadio = ({ - labelLeft, - labelRight, - iconLeft, - iconRight, +export const EditorModeSwitch = ({ isHover, style = {}, - onChange, - initialValue = 'left', }: AnimateRadioProps) => { - const [active, setActive] = useState(initialValue); + const { mode, setMode } = useEditor(); const modifyRadioItemStatus = (): RadioItemStatus => { return { - left: !isHover && active === 'right' ? 'shrink' : 'normal', - right: !isHover && active === 'left' ? 'shrink' : 'normal', + left: isHover + ? mode === 'page' + ? 'stretch' + : 'normal' + : mode === 'page' + ? 'shrink' + : 'hidden', + right: isHover + ? mode === 'edgeless' + ? 'stretch' + : 'normal' + : mode === 'edgeless' + ? 'shrink' + : 'hidden', }; }; const [radioItemStatus, setRadioItemStatus] = useState( @@ -84,19 +101,18 @@ export const AnimateRadio = ({ useEffect(() => { setRadioItemStatus(modifyRadioItemStatus()); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isHover, active]); + }, [isHover, mode]); return ( } + active={mode === 'page'} status={radioItemStatus.left} onClick={() => { - setActive('left'); - onChange?.('left'); + setMode('page'); }} onMouseEnter={() => { setRadioItemStatus({ @@ -105,31 +121,18 @@ export const AnimateRadio = ({ }); }} onMouseLeave={() => { - setRadioItemStatus({ - ...radioItemStatus, - left: 'normal', - }); + setRadioItemStatus(modifyRadioItemStatus()); }} /> - + } + active={mode === 'edgeless'} status={radioItemStatus.right} onClick={() => { - setActive('right'); - onChange?.('right'); + setMode('edgeless'); }} onMouseEnter={() => { setRadioItemStatus({ @@ -138,14 +141,11 @@ export const AnimateRadio = ({ }); }} onMouseLeave={() => { - setRadioItemStatus({ - ...radioItemStatus, - right: 'normal', - }); + setRadioItemStatus(modifyRadioItemStatus()); }} /> ); }; -export default AnimateRadio; +export default EditorModeSwitch; diff --git a/packages/app/src/components/animate-radio/style.ts b/packages/app/src/components/editor-mode-switch/style.ts similarity index 82% rename from packages/app/src/components/animate-radio/style.ts rename to packages/app/src/components/editor-mode-switch/style.ts index 7bcb2fb36e..aea2af5738 100644 --- a/packages/app/src/components/animate-radio/style.ts +++ b/packages/app/src/components/editor-mode-switch/style.ts @@ -8,11 +8,11 @@ const ANIMATE_DURATION = 300; export const StyledAnimateRadioContainer = styled('div')<{ shrink: boolean }>( ({ shrink }) => { const animateScaleStretch = keyframes`${toString( - spring({ width: '66px' }, { width: '132px' }, { preset: 'gentle' }) + spring({ width: '36px' }, { width: '160px' }, { preset: 'gentle' }) )}`; const animateScaleShrink = keyframes( `${toString( - spring({ width: '132px' }, { width: '66px' }, { preset: 'gentle' }) + spring({ width: '160px' }, { width: '36px' }, { preset: 'gentle' }) )}` ); const shrinkStyle = shrink @@ -40,7 +40,7 @@ export const StyledRadioMiddle = styled('div')<{ hidden: boolean; }>(({ hidden }) => { return { - width: '6px', + width: '1px', height: '100%', position: 'relative', opacity: hidden ? '0' : '1', @@ -53,9 +53,6 @@ export const StyledMiddleLine = styled('div')<{ hidden: boolean }>( width: '1px', height: '16px', background: '#D0D7E3', - position: 'absolute', - left: '0', - right: '0', top: '0', bottom: '0', margin: 'auto', @@ -69,13 +66,13 @@ export const StyledRadioItem = styled('div')<{ active: boolean; }>(({ status, active, theme }) => { const animateScaleStretch = keyframes`${toString( - spring({ width: '66px' }, { width: '116px' }) + spring({ width: '44px' }, { width: '112px' }) )}`; const animateScaleOrigin = keyframes( - `${toString(spring({ width: '116px' }, { width: '66px' }))}` + `${toString(spring({ width: '112px' }, { width: '44px' }))}` ); const animateScaleShrink = keyframes( - `${toString(spring({ width: '66px' }, { width: '0px' }))}` + `${toString(spring({ width: '0px' }, { width: '36px' }))}` ); const dynamicStyle = status === 'stretch' @@ -86,14 +83,16 @@ export const StyledRadioItem = styled('div')<{ : status === 'shrink' ? { animation: `${animateScaleShrink} ${ANIMATE_DURATION}ms forwards`, - opacity: '0', } - : { animation: `${animateScaleOrigin} ${ANIMATE_DURATION}ms forwards` }; + : status === 'normal' + ? { animation: `${animateScaleOrigin} ${ANIMATE_DURATION}ms forwards` } + : {}; const { colors: { highlight, disabled }, } = theme; return { + width: '0', height: '100%', display: 'flex', cursor: 'pointer', @@ -135,19 +134,14 @@ export const StyledIcon = styled('div')<{ shrink: boolean; isLeft: boolean; }>(({ shrink, isLeft }) => { - const shrinkStyle = shrink - ? { - width: '24px', - margin: isLeft ? '0 12px' : '0 5px', - } - : { - width: '66px', - }; + const dynamicStyle = shrink + ? { width: '36px' } + : { width: isLeft ? '44px' : '34px' }; return { display: 'flex', justifyContent: 'center', alignItems: 'center', flexShrink: '0', - ...shrinkStyle, + ...dynamicStyle, }; }); diff --git a/packages/app/src/components/animate-radio/type.ts b/packages/app/src/components/editor-mode-switch/type.ts similarity index 58% rename from packages/app/src/components/animate-radio/type.ts rename to packages/app/src/components/editor-mode-switch/type.ts index 1a2f1277d0..d9bf0b5245 100644 --- a/packages/app/src/components/animate-radio/type.ts +++ b/packages/app/src/components/editor-mode-switch/type.ts @@ -1,20 +1,14 @@ import { CSSProperties, DOMAttributes, ReactElement } from 'react'; -export type ItemStatus = 'normal' | 'stretch' | 'shrink'; +export type ItemStatus = 'normal' | 'stretch' | 'shrink' | 'hidden'; export type RadioItemStatus = { left: ItemStatus; right: ItemStatus; }; export type AnimateRadioProps = { - labelLeft: string; - labelRight: string; - iconLeft: ReactElement; - iconRight: ReactElement; isHover: boolean; - initialValue?: 'left' | 'right'; - style?: CSSProperties; - onChange?: (value: 'left' | 'right') => void; + style: CSSProperties; }; export type AnimateRadioItemProps = { active: boolean; diff --git a/packages/app/src/components/editor-provider.tsx b/packages/app/src/components/editor-provider.tsx index de6d95d845..030b912d11 100644 --- a/packages/app/src/components/editor-provider.tsx +++ b/packages/app/src/components/editor-provider.tsx @@ -5,13 +5,17 @@ import type { PropsWithChildren } from 'react'; type EditorContextValue = { editor: EditorContainer | null; + mode: EditorContainer['mode']; setEditor: (editor: EditorContainer) => void; + setMode: (mode: EditorContainer['mode']) => void; }; type EditorContextProps = PropsWithChildren<{}>; export const EditorContext = createContext({ editor: null, + mode: 'page', setEditor: () => {}, + setMode: () => {}, }); export const useEditor = () => useContext(EditorContext); @@ -20,9 +24,15 @@ export const EditorProvider = ({ children, }: PropsWithChildren) => { const [editor, setEditor] = useState(null); + const [mode, setMode] = useState('page'); + + useEffect(() => { + const event = new CustomEvent('affine.switch-mode', { detail: mode }); + window.dispatchEvent(event); + }, [mode]); return ( - + {children} ); diff --git a/packages/app/src/components/faq/icons.tsx b/packages/app/src/components/faq/icons.tsx new file mode 100644 index 0000000000..9a4eba69ae --- /dev/null +++ b/packages/app/src/components/faq/icons.tsx @@ -0,0 +1,45 @@ +export const HelpIcon = () => { + return ( + + + + ); +}; + +export const ContactIcon = () => { + return ( + + + + ); +}; + +export const KeyboardIcon = () => { + return ( + + + + ); +}; diff --git a/packages/app/src/components/faq/index.tsx b/packages/app/src/components/faq/index.tsx new file mode 100644 index 0000000000..2eec3dbf3d --- /dev/null +++ b/packages/app/src/components/faq/index.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { StyledFAQ, StyledIconWrapper, StyledFAQWrapper } from './style'; +import { ContactIcon, HelpIcon, KeyboardIcon } from './icons'; +import Grow from '@mui/material/Grow'; +import { Tooltip } from '../tooltip'; +import { Modal } from '@/components/modal'; +const Contact = () => { + const [openModal, setOpenModal] = React.useState(false); + return ( + <> + { + setOpenModal(false); + }} + > +
modal content
+ + + { + setOpenModal(true); + }} + > + + + + + ); +}; +export const FAQ = () => { + const [showContent, setShowContent] = React.useState(false); + return ( + { + setShowContent(true); + }} + onMouseLeave={() => { + setShowContent(false); + }} + > + + + + + + + + + + + + + + + + ); +}; diff --git a/packages/app/src/components/faq/style.ts b/packages/app/src/components/faq/style.ts new file mode 100644 index 0000000000..a5710e2fb9 --- /dev/null +++ b/packages/app/src/components/faq/style.ts @@ -0,0 +1,54 @@ +import { styled } from '@/styles'; + +export const StyledFAQ = styled('div')(({ theme }) => { + return { + width: '32px', + height: '32px', + backgroundColor: '#fff', + color: theme.colors.disabled, + position: 'fixed', + right: '30px', + bottom: '30px', + borderRadius: '50%', + zIndex: 1000, + ':hover': { + backgroundColor: '#F1F3FF', + color: theme.colors.highlight, + }, + }; +}); + +export const StyledIconWrapper = styled('div')(({ theme }) => { + return { + color: theme.colors.disabled, + marginBottom: '24px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + cursor: 'pointer', + backgroundColor: 'transparent', + borderRadius: '50%', + width: '32px', + height: '32px', + ':hover': { + color: theme.colors.highlight, + backgroundColor: '#F1F3FF', + }, + }; +}); + +export const StyledFAQWrapper = styled('div')(({ theme }) => { + return { + position: 'absolute', + bottom: '100%', + left: '0', + width: '100%', + color: theme.colors.disabled, + ':hover': { + '> svg': { + color: theme.colors.highlight, + }, + color: theme.colors.highlight, + }, + }; +}); diff --git a/packages/app/src/components/popover/index.tsx b/packages/app/src/components/popover/index.tsx index 7541a4b4f1..3c43eb0d5a 100644 --- a/packages/app/src/components/popover/index.tsx +++ b/packages/app/src/components/popover/index.tsx @@ -1,9 +1,10 @@ import { useState } from 'react'; -import type { PropsWithChildren } from 'react'; +import type { CSSProperties, PropsWithChildren } from 'react'; import { styled } from '@/styles'; type PopoverProps = { popoverContent?: React.ReactNode; + style?: CSSProperties; }; const StyledPopoverContainer = styled('div')({ @@ -34,6 +35,7 @@ const StyledPopover = styled('div')<{ show: boolean }>(({ show }) => { export const Popover = ({ children, popoverContent, + style = {}, }: PropsWithChildren) => { const [show, setShow] = useState(false); return ( @@ -47,6 +49,7 @@ export const Popover = ({ onMouseLeave={() => { setShow(false); }} + style={style} > {children} diff --git a/packages/app/src/components/popper/Popper.tsx b/packages/app/src/components/popper/Popper.tsx index 68ea64197b..c889f80971 100644 --- a/packages/app/src/components/popper/Popper.tsx +++ b/packages/app/src/components/popper/Popper.tsx @@ -95,7 +95,6 @@ export const Popper = ({ }; }); - // @ts-ignore // @ts-ignore return ( = { export const PopoverContainer = styled('div')< Pick ->(({ theme, direction, style }) => { - return ''; - // const shadow = theme.affine.shadows.shadow1; - // const white = theme.affine.palette.white; - // - // const borderRadius = - // border_radius_map[direction] || border_radius_map['left-top']; - // return { - // boxShadow: shadow, - // borderRadius: borderRadius, - // padding: '8px 4px', - // backgroundColor: white, - // ...style, - // }; +>(({ direction, style }) => { + const borderRadius = + border_radius_map[direction] || border_radius_map['left-top']; + return { + borderRadius: borderRadius, + ...style, + }; }); diff --git a/packages/app/src/components/tooltip/Tooltip.tsx b/packages/app/src/components/tooltip/Tooltip.tsx index 0bbe560f9c..bea79371c7 100644 --- a/packages/app/src/components/tooltip/Tooltip.tsx +++ b/packages/app/src/components/tooltip/Tooltip.tsx @@ -26,14 +26,14 @@ export const placementToContainerDirection: Record< }; const useTooltipStyle = (): CSSProperties => { - const theme = useTheme(); - return {}; - // return { - // backgroundColor: theme.affine.palette.icons, - // color: theme.affine.palette.white, - // ...theme.affine.typography.tooltip, - // padding: '4px 8px', - // }; + const { theme } = useTheme(); + return { + boxShadow: '1px 1px 4px rgba(0, 0, 0, 0.14)', + padding: '4px 12px', + backgroundColor: theme.colors.highlight, + color: '#fff', + fontSize: theme.font.xs, + }; }; export const Tooltip = ( diff --git a/packages/app/src/pages/affine.tsx b/packages/app/src/pages/affine.tsx index 10a8cc78e2..d202ac30a4 100644 --- a/packages/app/src/pages/affine.tsx +++ b/packages/app/src/pages/affine.tsx @@ -1,7 +1,7 @@ import type { ReactNode } from 'react'; import { useRef, useState, useEffect } from 'react'; import { styled } from '@/styles'; -import { PaperIcon, EdgelessIcon } from '../components/Header/icons'; +import { PaperIcon, EdgelessIcon } from '@/components/Header/icons'; export const StyledHeader = styled('div')({ height: '60px', width: '100vw', diff --git a/packages/app/src/pages/index.tsx b/packages/app/src/pages/index.tsx index 632b2ef9f5..aa8e9ae95b 100644 --- a/packages/app/src/pages/index.tsx +++ b/packages/app/src/pages/index.tsx @@ -2,7 +2,7 @@ import type { NextPage } from 'next'; import dynamic from 'next/dynamic'; import { styled, useTheme } from '@/styles'; import { Header } from '@/components/Header'; - +import { FAQ } from '@/components/faq'; import '@/components/simple-counter'; const StyledEditorContainer = styled('div')(({ theme }) => { @@ -56,6 +56,7 @@ const Home: NextPage = () => { {/*>*/} {/* auto*/} {/**/} + ); }; diff --git a/packages/app/src/styles/theme.ts b/packages/app/src/styles/theme.ts index f419b8d646..5b00dd2583 100644 --- a/packages/app/src/styles/theme.ts +++ b/packages/app/src/styles/theme.ts @@ -8,9 +8,15 @@ export const lightTheme: AffineTheme = { disabled: '#9096A5', background: '#fff', }, + font: { + xs: '12px', + sm: '16px', + base: '18px', + }, }; export const darkTheme: AffineTheme = { + ...lightTheme, colors: { primary: '#fff', highlight: '#7389FD', diff --git a/packages/app/src/styles/types.ts b/packages/app/src/styles/types.ts index 81b9fa4cb4..7c042957c5 100644 --- a/packages/app/src/styles/types.ts +++ b/packages/app/src/styles/types.ts @@ -18,6 +18,11 @@ export interface AffineTheme { disabled: string; background: string; }; + font: { + xs: string; // tiny + sm: string; // small + base: string; + }; } declare module '@emotion/react' {