mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-09 21:25:45 +08:00
feat: add import modal
This commit is contained in:
@@ -16,7 +16,7 @@ export const FAQ = () => {
|
|||||||
const [showContent, setShowContent] = useState(false);
|
const [showContent, setShowContent] = useState(false);
|
||||||
const { mode } = useTheme();
|
const { mode } = useTheme();
|
||||||
const { mode: editorMode } = useEditor();
|
const { mode: editorMode } = useEditor();
|
||||||
const { shortcutsModalHandler, triggerContactModal } = useModal();
|
const { triggerShortcutsModal, triggerContactModal } = useModal();
|
||||||
const isEdgelessDark = mode === 'dark' && editorMode === 'edgeless';
|
const isEdgelessDark = mode === 'dark' && editorMode === 'edgeless';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -38,7 +38,7 @@ export const FAQ = () => {
|
|||||||
isEdgelessDark={isEdgelessDark}
|
isEdgelessDark={isEdgelessDark}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowContent(false);
|
setShowContent(false);
|
||||||
triggerContactModal(true);
|
triggerContactModal();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ContactIcon />
|
<ContactIcon />
|
||||||
@@ -50,7 +50,7 @@ export const FAQ = () => {
|
|||||||
isEdgelessDark={isEdgelessDark}
|
isEdgelessDark={isEdgelessDark}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowContent(false);
|
setShowContent(false);
|
||||||
shortcutsModalHandler(true);
|
triggerShortcutsModal();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<KeyboardIcon />
|
<KeyboardIcon />
|
||||||
@@ -74,14 +74,3 @@ export const FAQ = () => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const routesLIst: any = [
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
element: <HelpIcon />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
|
||||||
|
import { StyledButtonWrapper, StyledTitle } from './styles';
|
||||||
|
import { Button } from '@/ui/button';
|
||||||
|
import { Wrapper } from '@/ui/layout';
|
||||||
|
import { Loading } from '@/components/loading';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
type ImportModalProps = {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
export const ImportModal = ({ open, onClose }: ImportModalProps) => {
|
||||||
|
const [status, setStatus] = useState<'unImported' | 'importing'>(
|
||||||
|
'unImported'
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (status === 'importing') {
|
||||||
|
setTimeout(() => {
|
||||||
|
setStatus('unImported');
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}, [status]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={open} onClose={onClose}>
|
||||||
|
<ModalWrapper width={460} height={240}>
|
||||||
|
<ModalCloseButton onClick={onClose} />
|
||||||
|
<StyledTitle>Import</StyledTitle>
|
||||||
|
|
||||||
|
{status === 'unImported' && (
|
||||||
|
<StyledButtonWrapper>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setStatus('importing');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Markdown
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setStatus('importing');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
HTML
|
||||||
|
</Button>
|
||||||
|
</StyledButtonWrapper>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{status === 'importing' && (
|
||||||
|
<Wrapper justifyContent="center" style={{ marginTop: 22 }}>
|
||||||
|
<Loading size={25}></Loading>
|
||||||
|
</Wrapper>
|
||||||
|
)}
|
||||||
|
</ModalWrapper>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImportModal;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { styled } from '@/styles';
|
||||||
|
|
||||||
|
export const StyledTitle = styled.div(({ theme }) => {
|
||||||
|
return {
|
||||||
|
fontSize: theme.font.h6,
|
||||||
|
fontWeight: 600,
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: '45px',
|
||||||
|
color: theme.colors.popoverColor,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const StyledButtonWrapper = styled.div(() => {
|
||||||
|
return {
|
||||||
|
width: '280px',
|
||||||
|
margin: '24px auto 0',
|
||||||
|
button: {
|
||||||
|
display: 'block',
|
||||||
|
width: '100%',
|
||||||
|
':not(:last-child)': {
|
||||||
|
marginBottom: '16px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const StyledLoadingWrapper = styled.div(() => {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,13 +1,19 @@
|
|||||||
import { StyledLoading, StyledLoadingItem } from './styled';
|
import {
|
||||||
|
StyledLoadingWrapper,
|
||||||
|
StyledLoading,
|
||||||
|
StyledLoadingItem,
|
||||||
|
} from './styled';
|
||||||
|
|
||||||
export const Loading = () => {
|
export const Loading = ({ size = 40 }: { size?: number }) => {
|
||||||
return (
|
return (
|
||||||
<StyledLoading>
|
<StyledLoadingWrapper size={size}>
|
||||||
<StyledLoadingItem />
|
<StyledLoading>
|
||||||
<StyledLoadingItem />
|
<StyledLoadingItem size={size} />
|
||||||
<StyledLoadingItem />
|
<StyledLoadingItem size={size} />
|
||||||
<StyledLoadingItem />
|
<StyledLoadingItem size={size} />
|
||||||
</StyledLoading>
|
<StyledLoadingItem size={size} />
|
||||||
|
</StyledLoading>
|
||||||
|
</StyledLoadingWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
import { styled } from '@/styles';
|
import { styled } from '@/styles';
|
||||||
|
|
||||||
// Inspired by https://codepen.io/graphilla/pen/rNvBMYY
|
// Inspired by https://codepen.io/graphilla/pen/rNvBMYY
|
||||||
const loadingItemSize = '40px';
|
export const StyledLoadingWrapper = styled.div<{ size?: number }>(
|
||||||
|
({ size = 40 }) => {
|
||||||
|
return {
|
||||||
|
width: size * 4,
|
||||||
|
height: size * 4,
|
||||||
|
position: 'relative',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
export const StyledLoading = styled.div`
|
export const StyledLoading = styled.div`
|
||||||
position: relative;
|
position: absolute;
|
||||||
left: 50%;
|
left: 25%;
|
||||||
transform: rotateX(55deg) rotateZ(-45deg)
|
top: 25%;
|
||||||
translate(calc(${loadingItemSize} * -2), 0);
|
|
||||||
margin-bottom: calc(3 * ${loadingItemSize});
|
|
||||||
|
|
||||||
@keyframes slide {
|
@keyframes slide {
|
||||||
0% {
|
0% {
|
||||||
transform: translate(var(--sx), var(--sy));
|
transform: translate(var(--sx), var(--sy));
|
||||||
@@ -21,24 +26,14 @@ export const StyledLoading = styled.div`
|
|||||||
transform: translate(var(--ex), var(--ey));
|
transform: translate(var(--ex), var(--ey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@keyframes load {
|
|
||||||
20% {
|
|
||||||
content: '.';
|
|
||||||
}
|
|
||||||
40% {
|
|
||||||
content: '..';
|
|
||||||
}
|
|
||||||
80%,
|
|
||||||
100% {
|
|
||||||
content: '...';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledLoadingItem = styled.div`
|
export const StyledLoadingItem = styled.div<{ size: number }>(
|
||||||
|
({ size = 40 }) => {
|
||||||
|
return `
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: ${loadingItemSize};
|
width: ${size}px;
|
||||||
height: ${loadingItemSize};
|
height: ${size}px;
|
||||||
background: #9dacf9;
|
background: #9dacf9;
|
||||||
animation: slide 0.9s cubic-bezier(0.65, 0.53, 0.59, 0.93) infinite;
|
animation: slide 0.9s cubic-bezier(0.65, 0.53, 0.59, 0.93) infinite;
|
||||||
|
|
||||||
@@ -92,3 +87,5 @@ export const StyledLoadingItem = styled.div`
|
|||||||
--ey: -50%;
|
--ey: -50%;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const FavoriteList = ({ showList }: { showList: boolean }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
export const WorkSpaceSliderBar = () => {
|
export const WorkSpaceSliderBar = () => {
|
||||||
const { triggerQuickSearchModal } = useModal();
|
const { triggerQuickSearchModal, triggerImportModal } = useModal();
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
const [showSubFavorite, setShowSubFavorite] = useState(false);
|
const [showSubFavorite, setShowSubFavorite] = useState(false);
|
||||||
const { createPage } = useEditor();
|
const { createPage } = useEditor();
|
||||||
@@ -58,7 +58,7 @@ export const WorkSpaceSliderBar = () => {
|
|||||||
<StyledSliderBar show={show}>
|
<StyledSliderBar show={show}>
|
||||||
<StyledListItem
|
<StyledListItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
triggerQuickSearchModal(true);
|
triggerQuickSearchModal();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Quick search
|
Quick search
|
||||||
@@ -102,7 +102,13 @@ export const WorkSpaceSliderBar = () => {
|
|||||||
</StyledListItem>
|
</StyledListItem>
|
||||||
<FavoriteList showList={showSubFavorite} />
|
<FavoriteList showList={showSubFavorite} />
|
||||||
|
|
||||||
<StyledListItem>Import</StyledListItem>
|
<StyledListItem
|
||||||
|
onClick={() => {
|
||||||
|
triggerImportModal();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Import
|
||||||
|
</StyledListItem>
|
||||||
|
|
||||||
<Link href={{ pathname: '/page-list/trash' }}>
|
<Link href={{ pathname: '/page-list/trash' }}>
|
||||||
<StyledListItem active={router.pathname === '/page-list/trash'}>
|
<StyledListItem active={router.pathname === '/page-list/trash'}>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const StyledLoadingContainer = styled('div')(({ theme }) => {
|
|||||||
color: '#6880FF',
|
color: '#6880FF',
|
||||||
h1: {
|
h1: {
|
||||||
fontSize: '2em',
|
fontSize: '2em',
|
||||||
marginTop: '150px',
|
marginTop: '15px',
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,17 +3,27 @@ import type { PropsWithChildren } from 'react';
|
|||||||
import ShortcutsModal from '@/components/shortcuts-modal';
|
import ShortcutsModal from '@/components/shortcuts-modal';
|
||||||
import ContactModal from '@/components/contact-modal';
|
import ContactModal from '@/components/contact-modal';
|
||||||
import QuickSearch from '@/components/quick-search';
|
import QuickSearch from '@/components/quick-search';
|
||||||
|
import { ImportModal } from '@/components/import';
|
||||||
|
|
||||||
type ModalContextValue = {
|
type ModalContextValue = {
|
||||||
shortcutsModalHandler: (visible: boolean) => void;
|
triggerShortcutsModal: () => void;
|
||||||
triggerContactModal: (visible: boolean) => void;
|
triggerContactModal: () => void;
|
||||||
triggerQuickSearchModal: (visible: boolean) => void;
|
triggerQuickSearchModal: () => void;
|
||||||
|
triggerImportModal: () => void;
|
||||||
};
|
};
|
||||||
type ModalContextProps = PropsWithChildren<{}>;
|
type ModalContextProps = PropsWithChildren<{}>;
|
||||||
|
type ModalMap = {
|
||||||
|
contact: boolean;
|
||||||
|
shortcuts: boolean;
|
||||||
|
quickSearch: boolean;
|
||||||
|
import: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export const ModalContext = createContext<ModalContextValue>({
|
export const ModalContext = createContext<ModalContextValue>({
|
||||||
shortcutsModalHandler: () => {},
|
triggerShortcutsModal: () => {},
|
||||||
triggerContactModal: () => {},
|
triggerContactModal: () => {},
|
||||||
triggerQuickSearchModal: () => {},
|
triggerQuickSearchModal: () => {},
|
||||||
|
triggerImportModal: () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useModal = () => useContext(ModalContext);
|
export const useModal = () => useContext(ModalContext);
|
||||||
@@ -21,59 +31,78 @@ export const useModal = () => useContext(ModalContext);
|
|||||||
export const ModalProvider = ({
|
export const ModalProvider = ({
|
||||||
children,
|
children,
|
||||||
}: PropsWithChildren<ModalContextProps>) => {
|
}: PropsWithChildren<ModalContextProps>) => {
|
||||||
const [openContactModal, setOpenContactModal] = useState(false);
|
const [modalMap, setModalMap] = useState<ModalMap>({
|
||||||
const [openShortcutsModal, setOpenShortcutsModal] = useState(false);
|
contact: false,
|
||||||
const [openQuickSearchModal, setOpenQuickSearchModal] = useState(false);
|
shortcuts: false,
|
||||||
|
quickSearch: false,
|
||||||
|
import: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const triggerHandler = (key: keyof ModalMap, visible?: boolean) => {
|
||||||
|
setModalMap({
|
||||||
|
...modalMap,
|
||||||
|
[key]: visible ?? !modalMap[key],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const down = (e: KeyboardEvent) => {
|
const down = (e: KeyboardEvent) => {
|
||||||
if (e.key === 'k' && e.metaKey) {
|
if (e.key === 'k' && e.metaKey) {
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
if (selection?.toString()) {
|
if (selection?.toString()) {
|
||||||
setOpenQuickSearchModal(false);
|
triggerHandler('quickSearch', false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selection?.isCollapsed) {
|
if (selection?.isCollapsed) {
|
||||||
setOpenQuickSearchModal(
|
triggerHandler('quickSearch');
|
||||||
openQuickSearchModal => !openQuickSearchModal
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener('keydown', down);
|
document.addEventListener('keydown', down);
|
||||||
return () => document.removeEventListener('keydown', down);
|
return () => document.removeEventListener('keydown', down);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContext.Provider
|
<ModalContext.Provider
|
||||||
value={{
|
value={{
|
||||||
shortcutsModalHandler: visible => {
|
triggerShortcutsModal: () => {
|
||||||
setOpenShortcutsModal(visible);
|
triggerHandler('shortcuts');
|
||||||
},
|
},
|
||||||
triggerContactModal: visible => {
|
triggerContactModal: () => {
|
||||||
setOpenContactModal(visible);
|
triggerHandler('contact');
|
||||||
},
|
},
|
||||||
triggerQuickSearchModal: visible => {
|
triggerQuickSearchModal: () => {
|
||||||
setOpenQuickSearchModal(visible);
|
triggerHandler('quickSearch');
|
||||||
|
},
|
||||||
|
triggerImportModal: () => {
|
||||||
|
triggerHandler('import');
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ContactModal
|
<ContactModal
|
||||||
open={openContactModal}
|
open={modalMap.contact}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setOpenContactModal(false);
|
triggerHandler('contact', false);
|
||||||
}}
|
}}
|
||||||
></ContactModal>
|
></ContactModal>
|
||||||
<ShortcutsModal
|
<ShortcutsModal
|
||||||
open={openShortcutsModal}
|
open={modalMap.shortcuts}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setOpenShortcutsModal(false);
|
triggerHandler('shortcuts', false);
|
||||||
}}
|
}}
|
||||||
></ShortcutsModal>
|
></ShortcutsModal>
|
||||||
<QuickSearch
|
<QuickSearch
|
||||||
open={openQuickSearchModal}
|
open={modalMap.quickSearch}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setOpenQuickSearchModal(false);
|
triggerHandler('quickSearch', false);
|
||||||
}}
|
}}
|
||||||
></QuickSearch>
|
></QuickSearch>
|
||||||
|
<ImportModal
|
||||||
|
open={modalMap.import}
|
||||||
|
onClose={() => {
|
||||||
|
triggerHandler('import', false);
|
||||||
|
}}
|
||||||
|
></ImportModal>
|
||||||
{children}
|
{children}
|
||||||
</ModalContext.Provider>
|
</ModalContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { displayFlex, styled, AffineTheme } from '@/styles';
|
import { displayFlex, styled } from '@/styles';
|
||||||
import { ConfirmProps } from '@/ui/confirm/confirm';
|
|
||||||
import { ModalWrapper } from '@/ui/modal';
|
import { ModalWrapper } from '@/ui/modal';
|
||||||
|
|
||||||
export const StyledModalWrapper = styled(ModalWrapper)(({ theme }) => {
|
export const StyledModalWrapper = styled(ModalWrapper)(({ theme }) => {
|
||||||
@@ -7,8 +6,6 @@ export const StyledModalWrapper = styled(ModalWrapper)(({ theme }) => {
|
|||||||
width: '460px',
|
width: '460px',
|
||||||
height: '240px',
|
height: '240px',
|
||||||
padding: '0 60px',
|
padding: '0 60px',
|
||||||
background: theme.colors.popoverBackground,
|
|
||||||
position: 'relative',
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,44 +34,3 @@ export const StyledButtonWrapper = styled.div(() => {
|
|||||||
marginTop: '32px',
|
marginTop: '32px',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const getButtonColors = (
|
|
||||||
theme: AffineTheme,
|
|
||||||
confirmType: ConfirmProps['confirmType']
|
|
||||||
) => {
|
|
||||||
switch (confirmType) {
|
|
||||||
case 'primary':
|
|
||||||
return {
|
|
||||||
background: theme.colors.primaryColor,
|
|
||||||
color: '#fff',
|
|
||||||
borderColor: theme.colors.primaryColor,
|
|
||||||
};
|
|
||||||
case 'warning':
|
|
||||||
return {
|
|
||||||
background: theme.colors.warningBackground,
|
|
||||||
color: theme.colors.warningColor,
|
|
||||||
borderColor: theme.colors.warningBackground,
|
|
||||||
':hover': {
|
|
||||||
borderColor: theme.colors.warningColor,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
case 'danger':
|
|
||||||
return {
|
|
||||||
background: theme.colors.errorBackground,
|
|
||||||
color: theme.colors.errorColor,
|
|
||||||
borderColor: theme.colors.errorBackground,
|
|
||||||
':hover': {
|
|
||||||
borderColor: theme.colors.errorColor,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
color: theme.colors.popoverColor,
|
|
||||||
borderColor: theme.colors.borderColor,
|
|
||||||
':hover': {
|
|
||||||
borderColor: theme.colors.primaryColor,
|
|
||||||
color: theme.colors.primaryColor,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
Reference in New Issue
Block a user