mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-17 10:06:17 +08:00
init: the first public commit for AFFiNE
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot, type Root } from 'react-dom/client';
|
||||
|
||||
import { BasePlugin } from './../base-plugin';
|
||||
import { PlaceholderPanel } from './PlaceholderPanel';
|
||||
const PLUGIN_NAME = 'placeholder';
|
||||
|
||||
export class PlaceholderPlugin extends BasePlugin {
|
||||
private root?: Root;
|
||||
|
||||
public static override get pluginName(): string {
|
||||
return PLUGIN_NAME;
|
||||
}
|
||||
|
||||
protected override on_render(): void {
|
||||
const container = document.createElement('div');
|
||||
// TODO remove
|
||||
container.classList.add(`id-${PLUGIN_NAME}`);
|
||||
window.document.body.appendChild(container);
|
||||
this.root = createRoot(container);
|
||||
this._renderPlugin();
|
||||
}
|
||||
|
||||
private _renderPlugin(): void {
|
||||
this.root?.render(
|
||||
<StrictMode>
|
||||
<PlaceholderPanel
|
||||
editor={this.editor}
|
||||
onClickTips={() => this.dispose()}
|
||||
/>
|
||||
</StrictMode>
|
||||
);
|
||||
}
|
||||
|
||||
public override dispose(): void {
|
||||
this.root?.unmount();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router';
|
||||
import { Virgo, BlockEditor } from '@toeverything/framework/virgo';
|
||||
import { debounce } from '@toeverything/utils';
|
||||
|
||||
import { MuiBox as Box, styled, BaseButton } from '@toeverything/components/ui';
|
||||
import {
|
||||
services,
|
||||
type ReturnUnobserve,
|
||||
TemplateFactory,
|
||||
TemplateMeta,
|
||||
} from '@toeverything/datasource/db-service';
|
||||
|
||||
const PlaceholderPanelContainer = styled('div')({
|
||||
position: 'fixed',
|
||||
top: '0px',
|
||||
fontSize: '16px',
|
||||
lineHeight: '22px',
|
||||
opacity: '0.333',
|
||||
});
|
||||
|
||||
const TemplateItemContainer = styled('div')({
|
||||
'&:hover': {
|
||||
backgroundColor: '#eee',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
});
|
||||
|
||||
const EmptyPageTipContainer = styled('div')({
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
backgroundColor: '#eee',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
});
|
||||
|
||||
type PlaceholderPanelProps = {
|
||||
editor: Virgo;
|
||||
onClickTips?: () => void;
|
||||
};
|
||||
|
||||
export const PlaceholderPanel = (props: PlaceholderPanelProps) => {
|
||||
const editor = props.editor;
|
||||
const workspaceId = editor.workspace;
|
||||
|
||||
const [point, setPoint] = useState({
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
const [open, setOpen] = useState(false);
|
||||
// const navigate = useNavigate();
|
||||
|
||||
const ifPageChildrenExist = useCallback(async () => {
|
||||
const rootId = await editor.getRootBlockId();
|
||||
const dbPageBlock = await services.api.editorBlock.getBlock(
|
||||
workspaceId,
|
||||
rootId
|
||||
);
|
||||
if (!dbPageBlock) return;
|
||||
if (dbPageBlock.children && dbPageBlock.children.length > 0) {
|
||||
setOpen(false);
|
||||
} else {
|
||||
if (open) {
|
||||
return;
|
||||
}
|
||||
setOpen(true);
|
||||
adjustPosition();
|
||||
}
|
||||
}, [
|
||||
editor,
|
||||
workspaceId,
|
||||
editor.getRootBlockId,
|
||||
open,
|
||||
services.api.editorBlock.getBlock,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
let unobserve: ReturnUnobserve | undefined = undefined;
|
||||
const observe = async () => {
|
||||
const rootId = await editor.getRootBlockId();
|
||||
|
||||
unobserve = await services.api.editorBlock.observe(
|
||||
{
|
||||
workspace: workspaceId,
|
||||
id: rootId,
|
||||
},
|
||||
() => {
|
||||
ifPageChildrenExist();
|
||||
}
|
||||
);
|
||||
};
|
||||
observe();
|
||||
|
||||
return () => {
|
||||
unobserve?.();
|
||||
};
|
||||
}, [editor, workspaceId, editor.getRootBlockId, ifPageChildrenExist]);
|
||||
|
||||
const adjustPosition = useCallback(async () => {
|
||||
//@ts-ignore
|
||||
const rootBlockDom = await editor.getBlockDomById(
|
||||
editor.getRootBlockId()
|
||||
);
|
||||
const { x, y } = rootBlockDom.getBoundingClientRect();
|
||||
setPoint({
|
||||
x,
|
||||
y: y + 60,
|
||||
});
|
||||
//@ts-ignore
|
||||
}, [editor, editor.getRootBlockId, editor.getBlockDomById]);
|
||||
|
||||
useEffect(() => {
|
||||
let unobserve: () => void;
|
||||
const observerePageTreeChange = async () => {
|
||||
unobserve = await services.api.pageTree.observe(
|
||||
{ workspace: workspaceId, page: editor.getRootBlockId() },
|
||||
ifPageChildrenExist
|
||||
);
|
||||
};
|
||||
observerePageTreeChange();
|
||||
|
||||
return () => {
|
||||
unobserve?.();
|
||||
};
|
||||
}, [editor, workspaceId, editor.getRootBlockId, ifPageChildrenExist, open]);
|
||||
|
||||
useEffect(() => {
|
||||
ifPageChildrenExist();
|
||||
}, [editor, ifPageChildrenExist, open]);
|
||||
|
||||
const handleClickEmptyPage = async () => {
|
||||
const rootBlockId = await editor.getRootBlockId();
|
||||
await services.api.editorBlock.copyTemplateToPage(
|
||||
workspaceId,
|
||||
rootBlockId,
|
||||
TemplateFactory.generatePageTemplateByGroupKeys({
|
||||
name: 'Empty Page',
|
||||
groupKeys: ['empty'],
|
||||
})
|
||||
);
|
||||
setOpen(false);
|
||||
props.onClickTips();
|
||||
};
|
||||
const templateList: Array<TemplateMeta> =
|
||||
TemplateFactory.defaultTemplateList;
|
||||
const handleNewFromTemplate = async (template: TemplateMeta) => {
|
||||
const pageId = await editor.getRootBlockId();
|
||||
const newPage = await services.api.editorBlock.getBlock(
|
||||
workspaceId,
|
||||
pageId
|
||||
);
|
||||
|
||||
await services.api.editorBlock.copyTemplateToPage(
|
||||
workspaceId,
|
||||
newPage.id,
|
||||
TemplateFactory.generatePageTemplateByGroupKeys(template)
|
||||
);
|
||||
|
||||
// redirectToPage(workspaceId, newPage.id);
|
||||
|
||||
// handleClose();
|
||||
setOpen(false);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<PlaceholderPanelContainer
|
||||
style={{
|
||||
top: point.y + 'px',
|
||||
left: point.x + 'px',
|
||||
display: open ? 'block' : 'none',
|
||||
}}
|
||||
>
|
||||
<EmptyPageTipContainer onClick={handleClickEmptyPage}>
|
||||
Press Enter to continue with an empty page, or pick a
|
||||
template
|
||||
</EmptyPageTipContainer>
|
||||
<div style={{ marginTop: '4px', marginLeft: '-8px' }}>
|
||||
{templateList.map((template, index) => {
|
||||
return (
|
||||
<TemplateItemContainer
|
||||
key={index}
|
||||
onClick={() => {
|
||||
handleNewFromTemplate(template);
|
||||
}}
|
||||
>
|
||||
<BaseButton>{template.name}</BaseButton>
|
||||
</TemplateItemContainer>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</PlaceholderPanelContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './Placeholder';
|
||||
Reference in New Issue
Block a user