mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-11 15:16:28 +08:00
141 lines
3.9 KiB
TypeScript
141 lines
3.9 KiB
TypeScript
import { BlockPreview } from '@toeverything/components/common';
|
|
import {
|
|
MuiBox as Box,
|
|
MuiBox,
|
|
styled,
|
|
TransitionsModal,
|
|
} from '@toeverything/components/ui';
|
|
import { BlockEditor, PluginHooks, Virgo } from '@toeverything/framework/virgo';
|
|
import { throttle } from '@toeverything/utils';
|
|
import { useCallback, useEffect, useState } from 'react';
|
|
import { useNavigate, useParams } from 'react-router';
|
|
import style9 from 'style9';
|
|
|
|
const styles = style9.create({
|
|
wrapper: {
|
|
position: 'absolute',
|
|
top: '20%',
|
|
left: '50%',
|
|
transform: 'translate(-50%, -50%)',
|
|
width: '50vw',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
},
|
|
resultItem: {
|
|
width: '100%',
|
|
},
|
|
resultHide: {
|
|
opacity: 0,
|
|
},
|
|
});
|
|
|
|
export type QueryResult = Awaited<ReturnType<BlockEditor['search']>>;
|
|
|
|
const queryBlocksExec = (
|
|
editor: Virgo,
|
|
search: string,
|
|
callback: (result: QueryResult) => void
|
|
) => {
|
|
(editor as BlockEditor).search(search).then(pages => callback(pages));
|
|
};
|
|
|
|
export const QueryBlocks = throttle(queryBlocksExec, 500);
|
|
|
|
type SearchProps = {
|
|
editor: Virgo;
|
|
hooks: PluginHooks;
|
|
};
|
|
|
|
export const Search = (props: SearchProps) => {
|
|
const { workspace_id: workspaceId } = useParams();
|
|
const navigate = useNavigate();
|
|
|
|
const [open, setOpen] = useState(false);
|
|
const [search, setSearch] = useState<string>();
|
|
const [result, setResult] = useState<QueryResult>([]);
|
|
|
|
useEffect(() => {
|
|
search !== undefined &&
|
|
QueryBlocks(props.editor, search, result => {
|
|
setResult(result);
|
|
});
|
|
}, [props.editor, search]);
|
|
|
|
const handleNavigate = useCallback(
|
|
(id: string) => navigate(`/${workspaceId}/${id}`),
|
|
[navigate, workspaceId]
|
|
);
|
|
|
|
const handleSearch = useCallback(() => {
|
|
setOpen(true);
|
|
setSearch('');
|
|
}, []);
|
|
|
|
// useEffect(() => {
|
|
// const sub = props.hooks.get(HookType.ON_SEARCH).subscribe(handleSearch);
|
|
|
|
// return () => {
|
|
// sub.unsubscribe();
|
|
// };
|
|
// }, [props, handleSearch]);
|
|
|
|
return (
|
|
<TransitionsModal
|
|
open={open}
|
|
onClose={() => {
|
|
setOpen(false);
|
|
}}
|
|
>
|
|
<Box className={styles('wrapper')}>
|
|
<SearchInput
|
|
autoFocus
|
|
value={search}
|
|
onChange={e => setSearch(e.target.value)}
|
|
/>
|
|
<ResultContainer
|
|
sx={{ maxHeight: `${result.length * 28 + 32 + 20}px` }}
|
|
className={styles({
|
|
resultHide: !result.length,
|
|
})}
|
|
>
|
|
{result.map(block => (
|
|
<BlockPreview
|
|
className={styles('resultItem')}
|
|
key={block.id}
|
|
block={{
|
|
...block,
|
|
content: block.content || 'Untitled',
|
|
}}
|
|
onClick={() => {
|
|
handleNavigate(block.id);
|
|
}}
|
|
/>
|
|
))}
|
|
</ResultContainer>
|
|
</Box>
|
|
</TransitionsModal>
|
|
);
|
|
};
|
|
|
|
const SearchInput = styled('input')(({ theme }) => ({
|
|
margin: '0.5em',
|
|
backgroundColor: 'white',
|
|
boxShadow: theme.affine.shadows.shadow1,
|
|
padding: '16px 32px',
|
|
borderRadius: '10px',
|
|
}));
|
|
|
|
const ResultContainer = styled(MuiBox)(({ theme }) => ({
|
|
margin: '0.5em',
|
|
backgroundColor: 'white',
|
|
boxShadow: theme.affine.shadows.shadow1,
|
|
padding: '16px 32px',
|
|
borderRadius: '10px',
|
|
transitionProperty: 'max-height',
|
|
transitionDuration: '300ms',
|
|
transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
|
transitionDelay: '0ms',
|
|
overflowX: 'hidden',
|
|
overflowY: 'hidden',
|
|
}));
|