mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 17:46:18 +08:00
feat: add import modal
This commit is contained in:
@@ -16,7 +16,7 @@ export const FAQ = () => {
|
||||
const [showContent, setShowContent] = useState(false);
|
||||
const { mode } = useTheme();
|
||||
const { mode: editorMode } = useEditor();
|
||||
const { shortcutsModalHandler, triggerContactModal } = useModal();
|
||||
const { triggerShortcutsModal, triggerContactModal } = useModal();
|
||||
const isEdgelessDark = mode === 'dark' && editorMode === 'edgeless';
|
||||
|
||||
return (
|
||||
@@ -38,7 +38,7 @@ export const FAQ = () => {
|
||||
isEdgelessDark={isEdgelessDark}
|
||||
onClick={() => {
|
||||
setShowContent(false);
|
||||
triggerContactModal(true);
|
||||
triggerContactModal();
|
||||
}}
|
||||
>
|
||||
<ContactIcon />
|
||||
@@ -50,7 +50,7 @@ export const FAQ = () => {
|
||||
isEdgelessDark={isEdgelessDark}
|
||||
onClick={() => {
|
||||
setShowContent(false);
|
||||
shortcutsModalHandler(true);
|
||||
triggerShortcutsModal();
|
||||
}}
|
||||
>
|
||||
<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 (
|
||||
<StyledLoading>
|
||||
<StyledLoadingItem />
|
||||
<StyledLoadingItem />
|
||||
<StyledLoadingItem />
|
||||
<StyledLoadingItem />
|
||||
</StyledLoading>
|
||||
<StyledLoadingWrapper size={size}>
|
||||
<StyledLoading>
|
||||
<StyledLoadingItem size={size} />
|
||||
<StyledLoadingItem size={size} />
|
||||
<StyledLoadingItem size={size} />
|
||||
<StyledLoadingItem size={size} />
|
||||
</StyledLoading>
|
||||
</StyledLoadingWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { styled } from '@/styles';
|
||||
|
||||
// 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`
|
||||
position: relative;
|
||||
left: 50%;
|
||||
transform: rotateX(55deg) rotateZ(-45deg)
|
||||
translate(calc(${loadingItemSize} * -2), 0);
|
||||
margin-bottom: calc(3 * ${loadingItemSize});
|
||||
|
||||
position: absolute;
|
||||
left: 25%;
|
||||
top: 25%;
|
||||
@keyframes slide {
|
||||
0% {
|
||||
transform: translate(var(--sx), var(--sy));
|
||||
@@ -21,24 +26,14 @@ export const StyledLoading = styled.div`
|
||||
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;
|
||||
width: ${loadingItemSize};
|
||||
height: ${loadingItemSize};
|
||||
width: ${size}px;
|
||||
height: ${size}px;
|
||||
background: #9dacf9;
|
||||
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%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -47,7 +47,7 @@ const FavoriteList = ({ showList }: { showList: boolean }) => {
|
||||
);
|
||||
};
|
||||
export const WorkSpaceSliderBar = () => {
|
||||
const { triggerQuickSearchModal } = useModal();
|
||||
const { triggerQuickSearchModal, triggerImportModal } = useModal();
|
||||
const [show, setShow] = useState(false);
|
||||
const [showSubFavorite, setShowSubFavorite] = useState(false);
|
||||
const { createPage } = useEditor();
|
||||
@@ -58,7 +58,7 @@ export const WorkSpaceSliderBar = () => {
|
||||
<StyledSliderBar show={show}>
|
||||
<StyledListItem
|
||||
onClick={() => {
|
||||
triggerQuickSearchModal(true);
|
||||
triggerQuickSearchModal();
|
||||
}}
|
||||
>
|
||||
Quick search
|
||||
@@ -102,7 +102,13 @@ export const WorkSpaceSliderBar = () => {
|
||||
</StyledListItem>
|
||||
<FavoriteList showList={showSubFavorite} />
|
||||
|
||||
<StyledListItem>Import</StyledListItem>
|
||||
<StyledListItem
|
||||
onClick={() => {
|
||||
triggerImportModal();
|
||||
}}
|
||||
>
|
||||
Import
|
||||
</StyledListItem>
|
||||
|
||||
<Link href={{ pathname: '/page-list/trash' }}>
|
||||
<StyledListItem active={router.pathname === '/page-list/trash'}>
|
||||
|
||||
@@ -10,7 +10,7 @@ const StyledLoadingContainer = styled('div')(({ theme }) => {
|
||||
color: '#6880FF',
|
||||
h1: {
|
||||
fontSize: '2em',
|
||||
marginTop: '150px',
|
||||
marginTop: '15px',
|
||||
fontWeight: '600',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,17 +3,27 @@ import type { PropsWithChildren } from 'react';
|
||||
import ShortcutsModal from '@/components/shortcuts-modal';
|
||||
import ContactModal from '@/components/contact-modal';
|
||||
import QuickSearch from '@/components/quick-search';
|
||||
import { ImportModal } from '@/components/import';
|
||||
|
||||
type ModalContextValue = {
|
||||
shortcutsModalHandler: (visible: boolean) => void;
|
||||
triggerContactModal: (visible: boolean) => void;
|
||||
triggerQuickSearchModal: (visible: boolean) => void;
|
||||
triggerShortcutsModal: () => void;
|
||||
triggerContactModal: () => void;
|
||||
triggerQuickSearchModal: () => void;
|
||||
triggerImportModal: () => void;
|
||||
};
|
||||
type ModalContextProps = PropsWithChildren<{}>;
|
||||
type ModalMap = {
|
||||
contact: boolean;
|
||||
shortcuts: boolean;
|
||||
quickSearch: boolean;
|
||||
import: boolean;
|
||||
};
|
||||
|
||||
export const ModalContext = createContext<ModalContextValue>({
|
||||
shortcutsModalHandler: () => {},
|
||||
triggerShortcutsModal: () => {},
|
||||
triggerContactModal: () => {},
|
||||
triggerQuickSearchModal: () => {},
|
||||
triggerImportModal: () => {},
|
||||
});
|
||||
|
||||
export const useModal = () => useContext(ModalContext);
|
||||
@@ -21,59 +31,78 @@ export const useModal = () => useContext(ModalContext);
|
||||
export const ModalProvider = ({
|
||||
children,
|
||||
}: PropsWithChildren<ModalContextProps>) => {
|
||||
const [openContactModal, setOpenContactModal] = useState(false);
|
||||
const [openShortcutsModal, setOpenShortcutsModal] = useState(false);
|
||||
const [openQuickSearchModal, setOpenQuickSearchModal] = useState(false);
|
||||
const [modalMap, setModalMap] = useState<ModalMap>({
|
||||
contact: false,
|
||||
shortcuts: false,
|
||||
quickSearch: false,
|
||||
import: false,
|
||||
});
|
||||
|
||||
const triggerHandler = (key: keyof ModalMap, visible?: boolean) => {
|
||||
setModalMap({
|
||||
...modalMap,
|
||||
[key]: visible ?? !modalMap[key],
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
if (e.key === 'k' && e.metaKey) {
|
||||
const selection = window.getSelection();
|
||||
if (selection?.toString()) {
|
||||
setOpenQuickSearchModal(false);
|
||||
triggerHandler('quickSearch', false);
|
||||
return;
|
||||
}
|
||||
if (selection?.isCollapsed) {
|
||||
setOpenQuickSearchModal(
|
||||
openQuickSearchModal => !openQuickSearchModal
|
||||
);
|
||||
triggerHandler('quickSearch');
|
||||
}
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', down);
|
||||
return () => document.removeEventListener('keydown', down);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ModalContext.Provider
|
||||
value={{
|
||||
shortcutsModalHandler: visible => {
|
||||
setOpenShortcutsModal(visible);
|
||||
triggerShortcutsModal: () => {
|
||||
triggerHandler('shortcuts');
|
||||
},
|
||||
triggerContactModal: visible => {
|
||||
setOpenContactModal(visible);
|
||||
triggerContactModal: () => {
|
||||
triggerHandler('contact');
|
||||
},
|
||||
triggerQuickSearchModal: visible => {
|
||||
setOpenQuickSearchModal(visible);
|
||||
triggerQuickSearchModal: () => {
|
||||
triggerHandler('quickSearch');
|
||||
},
|
||||
triggerImportModal: () => {
|
||||
triggerHandler('import');
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ContactModal
|
||||
open={openContactModal}
|
||||
open={modalMap.contact}
|
||||
onClose={() => {
|
||||
setOpenContactModal(false);
|
||||
triggerHandler('contact', false);
|
||||
}}
|
||||
></ContactModal>
|
||||
<ShortcutsModal
|
||||
open={openShortcutsModal}
|
||||
open={modalMap.shortcuts}
|
||||
onClose={() => {
|
||||
setOpenShortcutsModal(false);
|
||||
triggerHandler('shortcuts', false);
|
||||
}}
|
||||
></ShortcutsModal>
|
||||
<QuickSearch
|
||||
open={openQuickSearchModal}
|
||||
open={modalMap.quickSearch}
|
||||
onClose={() => {
|
||||
setOpenQuickSearchModal(false);
|
||||
triggerHandler('quickSearch', false);
|
||||
}}
|
||||
></QuickSearch>
|
||||
<ImportModal
|
||||
open={modalMap.import}
|
||||
onClose={() => {
|
||||
triggerHandler('import', false);
|
||||
}}
|
||||
></ImportModal>
|
||||
{children}
|
||||
</ModalContext.Provider>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { displayFlex, styled, AffineTheme } from '@/styles';
|
||||
import { ConfirmProps } from '@/ui/confirm/confirm';
|
||||
import { displayFlex, styled } from '@/styles';
|
||||
import { ModalWrapper } from '@/ui/modal';
|
||||
|
||||
export const StyledModalWrapper = styled(ModalWrapper)(({ theme }) => {
|
||||
@@ -7,8 +6,6 @@ export const StyledModalWrapper = styled(ModalWrapper)(({ theme }) => {
|
||||
width: '460px',
|
||||
height: '240px',
|
||||
padding: '0 60px',
|
||||
background: theme.colors.popoverBackground,
|
||||
position: 'relative',
|
||||
};
|
||||
});
|
||||
|
||||
@@ -37,44 +34,3 @@ export const StyledButtonWrapper = styled.div(() => {
|
||||
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