mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 12:55:00 +00:00
init: the first public commit for AFFiNE
This commit is contained in:
50
libs/components/editor-plugins/src/search/index.tsx
Normal file
50
libs/components/editor-plugins/src/search/index.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { StrictMode } from 'react';
|
||||
|
||||
import { HookType } from '@toeverything/framework/virgo';
|
||||
|
||||
import { BasePlugin } from '../base-plugin';
|
||||
import { Search } from './search';
|
||||
import { PluginRenderRoot } from '../utils';
|
||||
|
||||
export class FullTextSearchPlugin extends BasePlugin {
|
||||
#root?: PluginRenderRoot;
|
||||
|
||||
public static override get pluginName(): string {
|
||||
return 'search';
|
||||
}
|
||||
|
||||
public override init(): void {
|
||||
this.hooks.addHook(HookType.ON_SEARCH, this.handle_search, this);
|
||||
}
|
||||
|
||||
private unmount() {
|
||||
if (this.#root) {
|
||||
this.editor.setHotKeysScope();
|
||||
this.#root.unmount();
|
||||
this.#root = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private handle_search() {
|
||||
this.editor.setHotKeysScope('search');
|
||||
this.render_search();
|
||||
}
|
||||
private render_search() {
|
||||
this.#root = new PluginRenderRoot({
|
||||
name: FullTextSearchPlugin.pluginName,
|
||||
render: this.editor.reactRenderRoot.render,
|
||||
});
|
||||
this.#root.mount();
|
||||
this.#root.render(
|
||||
<StrictMode>
|
||||
<Search onClose={() => this.unmount()} editor={this.editor} />
|
||||
</StrictMode>
|
||||
);
|
||||
}
|
||||
public renderSearch() {
|
||||
this.render_search();
|
||||
}
|
||||
}
|
||||
|
||||
export type { QueryResult } from './search';
|
||||
export { QueryBlocks } from './search';
|
||||
117
libs/components/editor-plugins/src/search/search.tsx
Normal file
117
libs/components/editor-plugins/src/search/search.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router';
|
||||
import style9 from 'style9';
|
||||
|
||||
import { BlockPreview } from '@toeverything/components/common';
|
||||
import {
|
||||
TransitionsModal,
|
||||
MuiBox as Box,
|
||||
MuiBox,
|
||||
} from '@toeverything/components/ui';
|
||||
import { Virgo, BlockEditor } from '@toeverything/framework/virgo';
|
||||
import { throttle } from '@toeverything/utils';
|
||||
|
||||
const styles = style9.create({
|
||||
wrapper: {
|
||||
position: 'absolute',
|
||||
top: '20%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: '50vw',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
search: {
|
||||
margin: '0.5em',
|
||||
backgroundColor: 'white',
|
||||
boxShadow: '0px 1px 10px rgb(152 172 189 / 60%)',
|
||||
padding: '16px 32px',
|
||||
borderRadius: '10px',
|
||||
},
|
||||
result: {
|
||||
margin: '0.5em',
|
||||
backgroundColor: 'white',
|
||||
boxShadow: '0px 1px 10px rgb(152 172 189 / 60%)',
|
||||
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',
|
||||
},
|
||||
result_hide: {
|
||||
opacity: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export type QueryResult = Awaited<ReturnType<BlockEditor['search']>>;
|
||||
|
||||
const query_blocks = (
|
||||
editor: Virgo,
|
||||
search: string,
|
||||
callback: (result: QueryResult) => void
|
||||
) => {
|
||||
(editor as BlockEditor).search(search).then(pages => callback(pages));
|
||||
};
|
||||
|
||||
export const QueryBlocks = throttle(query_blocks, 500);
|
||||
|
||||
type SearchProps = {
|
||||
onClose: () => void;
|
||||
editor: Virgo;
|
||||
};
|
||||
|
||||
export const Search = (props: SearchProps) => {
|
||||
const { workspace_id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [open, set_open] = useState(true);
|
||||
const [search, set_search] = useState('');
|
||||
const [result, set_result] = useState<QueryResult>([]);
|
||||
|
||||
useEffect(() => {
|
||||
QueryBlocks(props.editor, search, result => {
|
||||
set_result(result);
|
||||
});
|
||||
}, [props.editor, search]);
|
||||
|
||||
const handle_navigate = useCallback(
|
||||
(id: string) => navigate(`/${workspace_id}/${id}`),
|
||||
[navigate, workspace_id]
|
||||
);
|
||||
|
||||
return (
|
||||
<TransitionsModal
|
||||
open={open}
|
||||
onClose={() => {
|
||||
set_open(false);
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
<Box className={styles('wrapper')}>
|
||||
<input
|
||||
className={styles('search')}
|
||||
autoFocus
|
||||
value={search}
|
||||
onChange={e => set_search(e.target.value)}
|
||||
/>
|
||||
<MuiBox
|
||||
sx={{ maxHeight: `${result.length * 28 + 32 + 20}px` }}
|
||||
className={styles('result', {
|
||||
result_hide: !result.length,
|
||||
})}
|
||||
>
|
||||
{result.map(block => (
|
||||
<BlockPreview
|
||||
key={block.id}
|
||||
block={block}
|
||||
onClick={() => handle_navigate(block.id)}
|
||||
/>
|
||||
))}
|
||||
</MuiBox>
|
||||
</Box>
|
||||
</TransitionsModal>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user