mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 17:46:18 +08:00
feat: add quick search UI
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
const Result = () => {
|
||||
return (
|
||||
<div>
|
||||
<div>Jump to</div>
|
||||
<div>All pages</div>
|
||||
<div>Favorites</div>
|
||||
<div>Trash</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Result;
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { StyledModalFooterContent } from './style';
|
||||
|
||||
const addIcon = (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<mask
|
||||
id="mask0_4831_8057"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="20"
|
||||
height="20"
|
||||
>
|
||||
<rect width="20" height="20" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_4831_8057)">
|
||||
<path
|
||||
d="M10 15.625C9.81933 15.625 9.663 15.559 9.531 15.427C9.399 15.295 9.333 15.1387 9.333 14.958V10.667H5.042C4.86133 10.667 4.705 10.601 4.573 10.469C4.441 10.337 4.375 10.1807 4.375 10C4.375 9.81933 4.441 9.663 4.573 9.531C4.705 9.399 4.86133 9.333 5.042 9.333H9.333V5.042C9.333 4.86133 9.399 4.705 9.531 4.573C9.663 4.441 9.81933 4.375 10 4.375C10.1807 4.375 10.337 4.441 10.469 4.573C10.601 4.705 10.667 4.86133 10.667 5.042V9.333H14.958C15.1387 9.333 15.295 9.399 15.427 9.531C15.559 9.663 15.625 9.81933 15.625 10C15.625 10.1807 15.559 10.337 15.427 10.469C15.295 10.601 15.1387 10.667 14.958 10.667H10.667V14.958C10.667 15.1387 10.601 15.295 10.469 15.427C10.337 15.559 10.1807 15.625 10 15.625Z"
|
||||
fill="#9096A5"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const QuickSearchFooter = () => {
|
||||
return (
|
||||
<StyledModalFooterContent>
|
||||
{addIcon} <span>New page</span>
|
||||
</StyledModalFooterContent>
|
||||
);
|
||||
};
|
||||
|
||||
export default QuickSearchFooter;
|
||||
@@ -1,11 +1,41 @@
|
||||
import React from 'react';
|
||||
import { Popper } from '@/ui/popper';
|
||||
import { Modal } from '@/ui/modal';
|
||||
|
||||
const QuickSearch = () => {
|
||||
import {
|
||||
StyledModalWrapper,
|
||||
StyledContent,
|
||||
StyledModalHeader,
|
||||
StyledModalFooter,
|
||||
StyledModalDivider,
|
||||
StyledShortcut,
|
||||
} from './style';
|
||||
import Input from './input';
|
||||
import Result from './content';
|
||||
import QuickSearchFooter from './footer';
|
||||
type TransitionsModalProps = {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
};
|
||||
const isMac = () => {
|
||||
return /macintosh|mac os x/i.test(navigator.userAgent);
|
||||
};
|
||||
export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
|
||||
return (
|
||||
<Popper>
|
||||
<div>QuickSearch</div>
|
||||
</Popper>
|
||||
<Modal open={open} onClose={onClose}>
|
||||
<StyledModalWrapper data-testid="quick-search-modal-content">
|
||||
<StyledModalHeader>
|
||||
<Input />
|
||||
<StyledShortcut>{isMac() ? '⌘+K' : 'Ctrl+K'}</StyledShortcut>
|
||||
</StyledModalHeader>
|
||||
<StyledModalDivider />
|
||||
<StyledContent>
|
||||
<Result />
|
||||
</StyledContent>
|
||||
|
||||
<StyledModalFooter>
|
||||
<QuickSearchFooter />
|
||||
</StyledModalFooter>
|
||||
</StyledModalWrapper>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import { StyledInput, StyledInputContent, StyledLabel } from './style';
|
||||
|
||||
const searchIcon = (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M14.4 9.5C14.4 12.2062 12.2062 14.4 9.5 14.4C6.7938 14.4 4.6 12.2062 4.6 9.5C4.6 6.7938 6.7938 4.6 9.5 4.6C12.2062 4.6 14.4 6.7938 14.4 9.5ZM13.4215 14.6842C12.3315 15.5101 10.973 16 9.5 16C5.91015 16 3 13.0899 3 9.5C3 5.91015 5.91015 3 9.5 3C13.0899 3 16 5.91015 16 9.5C16 11.0402 15.4643 12.4553 14.5691 13.5691L21 20L19.8687 21.1313L13.4215 14.6842Z"
|
||||
fill="#9096A5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const Input = () => {
|
||||
return (
|
||||
<StyledInputContent>
|
||||
<StyledLabel htmlFor="quickSearchInput">{searchIcon}</StyledLabel>
|
||||
<StyledInput
|
||||
id="quickSearchInput"
|
||||
type="text"
|
||||
placeholder="Quick search..."
|
||||
/>
|
||||
</StyledInputContent>
|
||||
);
|
||||
};
|
||||
|
||||
export default Input;
|
||||
@@ -0,0 +1,94 @@
|
||||
import { displayFlex, styled } from '@/styles';
|
||||
import { relative } from 'path';
|
||||
|
||||
export const StyledModalWrapper = styled('div')(({ theme }) => {
|
||||
return {
|
||||
width: '620px',
|
||||
height: 'auto',
|
||||
maxHeight: '720px',
|
||||
minHeight: '350px',
|
||||
backgroundColor: theme.colors.popoverBackground,
|
||||
borderRadius: '20px',
|
||||
position: 'absolute',
|
||||
top: '138px',
|
||||
margin: 'auto',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledContent = styled('div')(({ theme }) => {
|
||||
return {
|
||||
minHeight: '215px',
|
||||
maxHeight: '585px',
|
||||
width: '100%',
|
||||
padding: '5px 24px',
|
||||
overflow: 'auto',
|
||||
color: theme.colors.textColor,
|
||||
marginTop: '16px',
|
||||
letterSpacing: '0.06em',
|
||||
};
|
||||
});
|
||||
export const StyledInputContent = styled('div')({
|
||||
margin: '13px 0',
|
||||
...displayFlex('space-between', 'center'),
|
||||
});
|
||||
export const StyledShortcut = styled('div')(({ theme }) => {
|
||||
return { color: theme.colors.placeHolderColor, fontSize: theme.font.sm };
|
||||
});
|
||||
export const StyledInput = styled('input')(({ theme }) => {
|
||||
return {
|
||||
width: '492px',
|
||||
height: '22px',
|
||||
padding: '0 12px',
|
||||
fontSize: theme.font.sm,
|
||||
...displayFlex('space-between', 'center'),
|
||||
letterSpacing: '0.06em',
|
||||
color: theme.colors.textColor,
|
||||
'::placeholder': {
|
||||
color: theme.colors.placeHolderColor,
|
||||
},
|
||||
};
|
||||
});
|
||||
export const StyledLabel = styled('label')(({ theme }) => {
|
||||
return { width: '24px', height: '24px' };
|
||||
});
|
||||
|
||||
export const StyledModalHeader = styled('div')(({ theme }) => {
|
||||
return {
|
||||
height: '48px',
|
||||
margin: '12px 24px 0px 24px',
|
||||
...displayFlex('space-between', 'center'),
|
||||
color: theme.colors.textColor,
|
||||
};
|
||||
});
|
||||
export const StyledModalDivider = styled('div')(({ theme }) => {
|
||||
return {
|
||||
width: 'auto',
|
||||
height: '0',
|
||||
margin: '6px 12px 6.5px 12px',
|
||||
position: 'relative',
|
||||
borderTop: `0.5px solid ${theme.colors.placeHolderColor}`,
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledModalFooter = styled('div')(({ theme }) => {
|
||||
return {
|
||||
fontSize: theme.font.sm,
|
||||
lineHeight: '20px',
|
||||
textAlign: 'center',
|
||||
color: theme.colors.textColor,
|
||||
margin: '16px 0',
|
||||
};
|
||||
});
|
||||
export const StyledModalFooterContent = styled('div')(({ theme }) => {
|
||||
return {
|
||||
fontSize: theme.font.sm,
|
||||
lineHeight: '20px',
|
||||
textAlign: 'center',
|
||||
...displayFlex('center', 'center'),
|
||||
color: theme.colors.textColor,
|
||||
margin: '16px 0',
|
||||
span: {
|
||||
marginLeft: '12px',
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
windowsKeyboardShortcuts,
|
||||
winMarkdownShortcuts,
|
||||
} from '@/components/shortcuts-modal/config';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import Slide from '@mui/material/Slide';
|
||||
import { ModalCloseButton } from '@/ui/modal';
|
||||
type ModalProps = {
|
||||
|
||||
@@ -11,14 +11,14 @@ import { Arrow } from './icons';
|
||||
import { useModal } from '@/providers/global-modal-provider';
|
||||
|
||||
export const WorkSpaceSliderBar = () => {
|
||||
const { shortcutsModalHandler } = useModal();
|
||||
const { triggerQuickSearchModal } = useModal();
|
||||
const [show, setShow] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<StyledSliderBar show={show}>
|
||||
<StyledListItem
|
||||
onClick={() => {
|
||||
shortcutsModalHandler(true);
|
||||
triggerQuickSearchModal(true);
|
||||
}}
|
||||
>
|
||||
Quick search
|
||||
|
||||
@@ -2,16 +2,18 @@ import { createContext, useContext, useState } from 'react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import ShortcutsModal from '@/components/shortcuts-modal';
|
||||
import ContactModal from '@/components/contact-modal';
|
||||
|
||||
import QuickSearch from '@/components/quick-search';
|
||||
type ModalContextValue = {
|
||||
shortcutsModalHandler: (visible: boolean) => void;
|
||||
triggerContactModal: (visible: boolean) => void;
|
||||
triggerQuickSearchModal: (visible: boolean) => void;
|
||||
};
|
||||
type ModalContextProps = PropsWithChildren<{}>;
|
||||
|
||||
export const ModalContext = createContext<ModalContextValue>({
|
||||
shortcutsModalHandler: () => {},
|
||||
triggerContactModal: () => {},
|
||||
triggerQuickSearchModal: () => {},
|
||||
});
|
||||
|
||||
export const useModal = () => useContext(ModalContext);
|
||||
@@ -21,6 +23,7 @@ export const ModalProvider = ({
|
||||
}: PropsWithChildren<ModalContextProps>) => {
|
||||
const [openContactModal, setOpenContactModal] = useState(false);
|
||||
const [openShortcutsModal, setOpenShortcutsModal] = useState(false);
|
||||
const [openQuickSearchModal, setOpenQuickSearchModal] = useState(false);
|
||||
|
||||
return (
|
||||
<ModalContext.Provider
|
||||
@@ -31,6 +34,9 @@ export const ModalProvider = ({
|
||||
triggerContactModal: visible => {
|
||||
setOpenContactModal(visible);
|
||||
},
|
||||
triggerQuickSearchModal: visible => {
|
||||
setOpenQuickSearchModal(visible);
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ContactModal
|
||||
@@ -45,6 +51,12 @@ export const ModalProvider = ({
|
||||
setOpenShortcutsModal(false);
|
||||
}}
|
||||
></ShortcutsModal>
|
||||
<QuickSearch
|
||||
open={openQuickSearchModal}
|
||||
onClose={() => {
|
||||
setOpenQuickSearchModal(false);
|
||||
}}
|
||||
></QuickSearch>
|
||||
{children}
|
||||
</ModalContext.Provider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user