mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-15 21:41:52 +08:00
feat: add outline plugin (#3624)
Co-authored-by: codert <codert.sn@gmail.com>
This commit is contained in:
96
plugins/outline/src/app.tsx
Normal file
96
plugins/outline/src/app.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import { Tooltip } from '@affine/component';
|
||||
import { deleteLayoutAtom, pushLayoutAtom } from '@affine/sdk/entry';
|
||||
import { TOCNotesPanel } from '@blocksuite/blocks';
|
||||
import { RightSidebarIcon } from '@blocksuite/icons';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { IconButton } from '@toeverything/components/button';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
import type { ComponentType, PropsWithChildren } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import { blocksuiteRootAtom } from './atom';
|
||||
|
||||
const Outline = () => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const tocPanelRef = useRef<TOCNotesPanel | null>(null);
|
||||
const [blocksuite] = useAtom(blocksuiteRootAtom);
|
||||
|
||||
if (!tocPanelRef.current) {
|
||||
tocPanelRef.current = new TOCNotesPanel();
|
||||
}
|
||||
|
||||
if (blocksuite?.page !== tocPanelRef.current?.page) {
|
||||
(tocPanelRef.current as TOCNotesPanel).page = blocksuite?.page as Page;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current || !tocPanelRef.current) return;
|
||||
|
||||
const container = ref.current;
|
||||
const tocPanel = tocPanelRef.current as TOCNotesPanel;
|
||||
|
||||
container.appendChild(tocPanel);
|
||||
|
||||
return () => {
|
||||
container.removeChild(tocPanel);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`outline-wrapper`}
|
||||
style={{
|
||||
height: '100%',
|
||||
borderLeft: `1px solid var(--affine-border-color)`,
|
||||
}}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const HeaderItem = ({
|
||||
Provider,
|
||||
}: {
|
||||
Provider: ComponentType<PropsWithChildren>;
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const pushLayout = useSetAtom(pushLayoutAtom);
|
||||
const deleteLayout = useSetAtom(deleteLayoutAtom);
|
||||
return (
|
||||
<Tooltip content="Plugin Enabled">
|
||||
<IconButton
|
||||
onClick={useCallback(() => {
|
||||
if (!open) {
|
||||
setOpen(true);
|
||||
pushLayout(
|
||||
'@affine/outline-plugin',
|
||||
div => {
|
||||
const root = createRoot(div);
|
||||
|
||||
div.style.height = '100%';
|
||||
|
||||
root.render(
|
||||
<Provider>
|
||||
<Outline />
|
||||
</Provider>
|
||||
);
|
||||
return () => {
|
||||
root.unmount();
|
||||
};
|
||||
},
|
||||
{
|
||||
maxWidth: [undefined, 300],
|
||||
}
|
||||
);
|
||||
} else {
|
||||
setOpen(false);
|
||||
deleteLayout('@affine/outline-plugin');
|
||||
}
|
||||
}, [Provider, deleteLayout, open, pushLayout])}
|
||||
>
|
||||
<RightSidebarIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
5
plugins/outline/src/atom.ts
Normal file
5
plugins/outline/src/atom.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { atom } from 'jotai';
|
||||
|
||||
export const blocksuiteRootAtom = atom(() =>
|
||||
document.querySelector('block-suite-root')
|
||||
);
|
||||
41
plugins/outline/src/index.ts
Normal file
41
plugins/outline/src/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { PluginContext } from '@affine/sdk/entry';
|
||||
import { registerTOCComponents } from '@blocksuite/blocks';
|
||||
import { createElement } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import { HeaderItem } from './app';
|
||||
|
||||
export const entry = (context: PluginContext) => {
|
||||
console.log('register outline');
|
||||
|
||||
context.register('headerItem', div => {
|
||||
registerTOCComponents(components => {
|
||||
for (const compName in components) {
|
||||
if (window.customElements.get(compName)) continue;
|
||||
|
||||
window.customElements.define(
|
||||
compName,
|
||||
components[compName as keyof typeof components]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
div.style.height = '100%';
|
||||
|
||||
const root = createRoot(div);
|
||||
root.render(
|
||||
createElement(
|
||||
context.utils.PluginProvider,
|
||||
{},
|
||||
createElement(HeaderItem, {
|
||||
Provider: context.utils.PluginProvider,
|
||||
})
|
||||
)
|
||||
);
|
||||
return () => {
|
||||
root.unmount();
|
||||
};
|
||||
});
|
||||
|
||||
return () => {};
|
||||
};
|
||||
Reference in New Issue
Block a user