mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-10 05:29:08 +08:00
feat: Intra-line double link interaction
This commit is contained in:
@@ -57,7 +57,9 @@ export const CommonList = (props: MenuItemsProps) => {
|
|||||||
>
|
>
|
||||||
{usedItems?.length ? (
|
{usedItems?.length ? (
|
||||||
usedItems.map((item, idx) => {
|
usedItems.map((item, idx) => {
|
||||||
if (item.block) {
|
if (item.renderCustom) {
|
||||||
|
return item.renderCustom(item);
|
||||||
|
} else if (item.block) {
|
||||||
return (
|
return (
|
||||||
<BlockPreview
|
<BlockPreview
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|||||||
@@ -1,21 +1,17 @@
|
|||||||
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
|
||||||
|
|
||||||
import { Virgo, PluginHooks, HookType } from '@toeverything/framework/virgo';
|
|
||||||
import {
|
import {
|
||||||
CommonList,
|
CommonList,
|
||||||
CommonListItem,
|
|
||||||
commonListContainer,
|
commonListContainer,
|
||||||
|
CommonListItem,
|
||||||
} from '@toeverything/components/common';
|
} from '@toeverything/components/common';
|
||||||
import { domToRect } from '@toeverything/utils';
|
|
||||||
import { styled } from '@toeverything/components/ui';
|
import { styled } from '@toeverything/components/ui';
|
||||||
|
import { HookType, PluginHooks, Virgo } from '@toeverything/framework/virgo';
|
||||||
import { QueryResult } from '../../search';
|
import { domToRect } from '@toeverything/utils';
|
||||||
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
export type DoubleLinkMenuContainerProps = {
|
export type DoubleLinkMenuContainerProps = {
|
||||||
editor: Virgo;
|
editor: Virgo;
|
||||||
hooks: PluginHooks;
|
hooks: PluginHooks;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
isShow?: boolean;
|
|
||||||
blockId: string;
|
blockId: string;
|
||||||
onSelected?: (item: string) => void;
|
onSelected?: (item: string) => void;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
@@ -25,7 +21,6 @@ export type DoubleLinkMenuContainerProps = {
|
|||||||
|
|
||||||
export const DoubleLinkMenuContainer = ({
|
export const DoubleLinkMenuContainer = ({
|
||||||
hooks,
|
hooks,
|
||||||
isShow = false,
|
|
||||||
onSelected,
|
onSelected,
|
||||||
onClose,
|
onClose,
|
||||||
types,
|
types,
|
||||||
@@ -66,12 +61,13 @@ export const DoubleLinkMenuContainer = ({
|
|||||||
}, [needCheckIntoView, currentItem]);
|
}, [needCheckIntoView, currentItem]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isShow && types && !currentItem) setCurrentItem(types[0]);
|
if (types && !currentItem) {
|
||||||
if (!isShow) onClose?.();
|
setCurrentItem(types[0]);
|
||||||
}, [currentItem, isShow, onClose, types]);
|
}
|
||||||
|
}, [currentItem, onClose, types]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isShow && types) {
|
if (types) {
|
||||||
if (!types.includes(currentItem)) {
|
if (!types.includes(currentItem)) {
|
||||||
setNeedCheckIntoView(true);
|
setNeedCheckIntoView(true);
|
||||||
if (types.length) {
|
if (types.length) {
|
||||||
@@ -81,11 +77,11 @@ export const DoubleLinkMenuContainer = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [isShow, types, currentItem]);
|
}, [types, currentItem]);
|
||||||
|
|
||||||
const handleClickUp = useCallback(
|
const handleClickUp = useCallback(
|
||||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
if (isShow && types && event.code === 'ArrowUp') {
|
if (types && event.code === 'ArrowUp') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!currentItem && types.length) {
|
if (!currentItem && types.length) {
|
||||||
setCurrentItem(types[types.length - 1]);
|
setCurrentItem(types[types.length - 1]);
|
||||||
@@ -99,12 +95,12 @@ export const DoubleLinkMenuContainer = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isShow, types, currentItem]
|
[types, currentItem]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleClickDown = useCallback(
|
const handleClickDown = useCallback(
|
||||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
if (isShow && types && event.code === 'ArrowDown') {
|
if (types && event.code === 'ArrowDown') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!currentItem && types.length) {
|
if (!currentItem && types.length) {
|
||||||
setCurrentItem(types[0]);
|
setCurrentItem(types[0]);
|
||||||
@@ -118,17 +114,17 @@ export const DoubleLinkMenuContainer = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isShow, types, currentItem]
|
[types, currentItem]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleClickEnter = useCallback(
|
const handleClickEnter = useCallback(
|
||||||
async (event: React.KeyboardEvent<HTMLDivElement>) => {
|
async (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
if (isShow && event.code === 'Enter' && currentItem) {
|
if (event.code === 'Enter' && currentItem) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
onSelected && onSelected(currentItem);
|
onSelected && onSelected(currentItem);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isShow, currentItem, onSelected]
|
[currentItem, onSelected]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
@@ -150,7 +146,7 @@ export const DoubleLinkMenuContainer = ({
|
|||||||
};
|
};
|
||||||
}, [hooks, handleKeyDown]);
|
}, [hooks, handleKeyDown]);
|
||||||
|
|
||||||
return isShow ? (
|
return (
|
||||||
<RootContainer
|
<RootContainer
|
||||||
ref={menuRef}
|
ref={menuRef}
|
||||||
onKeyDownCapture={handleKeyDown}
|
onKeyDownCapture={handleKeyDown}
|
||||||
@@ -165,11 +161,10 @@ export const DoubleLinkMenuContainer = ({
|
|||||||
/>
|
/>
|
||||||
</ContentContainer>
|
</ContentContainer>
|
||||||
</RootContainer>
|
</RootContainer>
|
||||||
) : null;
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const RootContainer = styled('div')(({ theme }) => ({
|
const RootContainer = styled('div')(({ theme }) => ({
|
||||||
// position: 'fixed',
|
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
maxHeight: '525px',
|
maxHeight: '525px',
|
||||||
borderRadius: '10px',
|
borderRadius: '10px',
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { CommonListItem } from '@toeverything/components/common';
|
import { CommonListItem } from '@toeverything/components/common';
|
||||||
import { AddIcon } from '@toeverything/components/icons';
|
import { AddIcon } from '@toeverything/components/icons';
|
||||||
import {
|
import {
|
||||||
|
Input,
|
||||||
ListButton,
|
ListButton,
|
||||||
MuiClickAwayListener,
|
MuiClickAwayListener,
|
||||||
MuiGrow as Grow,
|
MuiGrow as Grow,
|
||||||
MuiOutlinedInput as OutlinedInput,
|
|
||||||
MuiPaper as Paper,
|
MuiPaper as Paper,
|
||||||
MuiPopper as Popper,
|
MuiPopper as Popper,
|
||||||
styled,
|
styled,
|
||||||
@@ -13,6 +13,7 @@ import { services } from '@toeverything/datasource/db-service';
|
|||||||
import { HookType, PluginHooks, Virgo } from '@toeverything/framework/virgo';
|
import { HookType, PluginHooks, Virgo } from '@toeverything/framework/virgo';
|
||||||
import { getPageId } from '@toeverything/utils';
|
import { getPageId } from '@toeverything/utils';
|
||||||
import React, {
|
import React, {
|
||||||
|
ChangeEvent,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
@@ -24,6 +25,7 @@ import { DoubleLinkMenuContainer } from './Container';
|
|||||||
|
|
||||||
const ADD_NEW_SUB_PAGE = 'AddNewSubPage';
|
const ADD_NEW_SUB_PAGE = 'AddNewSubPage';
|
||||||
const ADD_NEW_PAGE = 'AddNewPage';
|
const ADD_NEW_PAGE = 'AddNewPage';
|
||||||
|
const ARRAY_KEYS = ['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'];
|
||||||
|
|
||||||
export type DoubleLinkMenuProps = {
|
export type DoubleLinkMenuProps = {
|
||||||
editor: Virgo;
|
editor: Virgo;
|
||||||
@@ -31,7 +33,7 @@ export type DoubleLinkMenuProps = {
|
|||||||
style?: { left: number; top: number };
|
style?: { left: number; top: number };
|
||||||
};
|
};
|
||||||
|
|
||||||
type DoubleMenuStyle = {
|
type DoubleLinkMenuStyle = {
|
||||||
left: number;
|
left: number;
|
||||||
top: number;
|
top: number;
|
||||||
height: number;
|
height: number;
|
||||||
@@ -42,58 +44,63 @@ export const DoubleLinkMenu = ({
|
|||||||
hooks,
|
hooks,
|
||||||
style,
|
style,
|
||||||
}: DoubleLinkMenuProps) => {
|
}: DoubleLinkMenuProps) => {
|
||||||
const [isShow, setIsShow] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [blockId, setBlockId] = useState<string>();
|
|
||||||
const [searchText, setSearchText] = useState<string>('');
|
|
||||||
const [searchBlocks, setSearchBlocks] = useState<QueryResult>([]);
|
|
||||||
const [items, setItems] = useState<CommonListItem[]>([]);
|
|
||||||
const [isNewPage, setIsNewPage] = useState(false);
|
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const ref = useRef();
|
const dialogRef = useRef<HTMLDivElement>();
|
||||||
const ref1 = useRef<HTMLInputElement>();
|
const newPageSearchRef = useRef<HTMLInputElement>();
|
||||||
const [referenceMenuStyle, setReferenceMenuStyle] =
|
const [doubleLinkMenuStyle, setDoubleLinkMenuStyle] =
|
||||||
useState<DoubleMenuStyle>({
|
useState<DoubleLinkMenuStyle>({
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
const [curBlockId, setCurBlockId] = useState<string>();
|
||||||
QueryBlocks(editor, searchText, result => {
|
const [searchText, setSearchText] = useState<string>('');
|
||||||
setSearchBlocks(result);
|
const [inAddNewPage, setInAddNewPage] = useState(false);
|
||||||
ref1?.current?.focus();
|
const [filterText, setFilterText] = useState<string>('');
|
||||||
const items: CommonListItem[] = [];
|
const [searchResultBlocks, setSearchResultBlocks] = useState<QueryResult>(
|
||||||
if (searchBlocks?.length > 0) {
|
[]
|
||||||
if (isNewPage) {
|
);
|
||||||
items.push({
|
|
||||||
renderCustom: () => {
|
|
||||||
return <ListButton content={'SUGGESTED'} />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
items.push({
|
|
||||||
renderCustom: () => {
|
|
||||||
return <ListButton content={'LINK TO PAGE'} />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
items.push(
|
|
||||||
...(searchBlocks?.map(
|
|
||||||
block => ({ block } as CommonListItem)
|
|
||||||
) || [])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items.length > 0) {
|
const menuTypes = useMemo(() => {
|
||||||
items.push({ divider: 'newPage' });
|
return Object.values(searchResultBlocks)
|
||||||
}
|
.map(({ id }) => id)
|
||||||
|
.concat([ADD_NEW_SUB_PAGE, ADD_NEW_PAGE]);
|
||||||
|
}, [searchResultBlocks]);
|
||||||
|
|
||||||
|
const menuItems: CommonListItem[] = useMemo(() => {
|
||||||
|
const items: CommonListItem[] = [];
|
||||||
|
if (searchResultBlocks?.length > 0) {
|
||||||
items.push({
|
items.push({
|
||||||
content: {
|
renderCustom: () => {
|
||||||
id: ADD_NEW_SUB_PAGE,
|
return (
|
||||||
content: 'Add new sub-page',
|
<ListButton
|
||||||
icon: AddIcon,
|
content={
|
||||||
|
inAddNewPage ? 'SUGGESTED' : 'LINK TO PAGE'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
items.push(
|
||||||
|
...(searchResultBlocks?.map(
|
||||||
|
block => ({ block } as CommonListItem)
|
||||||
|
) || [])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.length > 0) {
|
||||||
|
items.push({ divider: 'newPage' });
|
||||||
|
}
|
||||||
|
items.push({
|
||||||
|
content: {
|
||||||
|
id: ADD_NEW_SUB_PAGE,
|
||||||
|
content: 'Add new sub-page',
|
||||||
|
icon: AddIcon,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
!inAddNewPage &&
|
||||||
items.push({
|
items.push({
|
||||||
content: {
|
content: {
|
||||||
id: ADD_NEW_PAGE,
|
id: ADD_NEW_PAGE,
|
||||||
@@ -101,26 +108,28 @@ export const DoubleLinkMenu = ({
|
|||||||
icon: AddIcon,
|
icon: AddIcon,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
return items;
|
||||||
|
}, [searchResultBlocks, inAddNewPage]);
|
||||||
|
|
||||||
setItems(items);
|
useEffect(() => {
|
||||||
|
const text = inAddNewPage ? filterText : searchText;
|
||||||
|
QueryBlocks(editor, text, result => {
|
||||||
|
setSearchResultBlocks(result);
|
||||||
});
|
});
|
||||||
}, [editor, searchText, isNewPage]);
|
}, [editor, searchText, filterText, inAddNewPage]);
|
||||||
|
|
||||||
const types = useMemo(() => {
|
|
||||||
return Object.values(searchBlocks)
|
|
||||||
.map(({ id }) => id)
|
|
||||||
.concat([ADD_NEW_SUB_PAGE, ADD_NEW_PAGE]);
|
|
||||||
}, [searchBlocks]);
|
|
||||||
|
|
||||||
const hideMenu = useCallback(() => {
|
const hideMenu = useCallback(() => {
|
||||||
setIsShow(false);
|
setIsOpen(false);
|
||||||
setIsNewPage(false);
|
setInAddNewPage(false);
|
||||||
editor.blockHelper.removeDoubleLinkSearchSlash(blockId);
|
editor.blockHelper.removeDoubleLinkSearchSlash(curBlockId);
|
||||||
editor.scrollManager.unLock();
|
editor.scrollManager.unLock();
|
||||||
}, [blockId, editor.blockHelper, editor.scrollManager]);
|
}, [curBlockId, editor]);
|
||||||
|
|
||||||
const handleSearch = useCallback(
|
const searchChange = useCallback(
|
||||||
async (event: React.KeyboardEvent<HTMLDivElement>) => {
|
async (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if (ARRAY_KEYS.includes(event.key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const { type, anchorNode } = editor.selection.currentSelectInfo;
|
const { type, anchorNode } = editor.selection.currentSelectInfo;
|
||||||
if (
|
if (
|
||||||
type === 'Range' &&
|
type === 'Range' &&
|
||||||
@@ -130,24 +139,31 @@ export const DoubleLinkMenu = ({
|
|||||||
const text = editor.blockHelper.getBlockTextBeforeSelection(
|
const text = editor.blockHelper.getBlockTextBeforeSelection(
|
||||||
anchorNode.id
|
anchorNode.id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (text.endsWith('[[')) {
|
if (text.endsWith('[[')) {
|
||||||
if (
|
|
||||||
[
|
|
||||||
'ArrowRight',
|
|
||||||
'ArrowLeft',
|
|
||||||
'ArrowUp',
|
|
||||||
'ArrowDown',
|
|
||||||
].includes(event.key)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.key === 'Backspace') {
|
if (event.key === 'Backspace') {
|
||||||
hideMenu();
|
hideMenu();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setBlockId(anchorNode.id);
|
editor.blockHelper.removeDoubleLinkSearchSlash(curBlockId);
|
||||||
editor.blockHelper.removeDoubleLinkSearchSlash(blockId);
|
setCurBlockId(anchorNode.id);
|
||||||
|
setSearchText('');
|
||||||
|
setIsOpen(true);
|
||||||
|
editor.scrollManager.lock();
|
||||||
|
const clientRect =
|
||||||
|
editor.selection.currentSelectInfo?.browserSelection
|
||||||
|
?.getRangeAt(0)
|
||||||
|
?.getBoundingClientRect();
|
||||||
|
if (clientRect) {
|
||||||
|
const rectTop = clientRect.top;
|
||||||
|
const { top, left } =
|
||||||
|
editor.container.getBoundingClientRect();
|
||||||
|
setDoubleLinkMenuStyle({
|
||||||
|
top: rectTop - top,
|
||||||
|
left: clientRect.left - left,
|
||||||
|
height: clientRect.height,
|
||||||
|
});
|
||||||
|
setAnchorEl(dialogRef.current);
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const textSelection =
|
const textSelection =
|
||||||
editor.blockHelper.selectionToSlateRange(
|
editor.blockHelper.selectionToSlateRange(
|
||||||
@@ -163,40 +179,22 @@ export const DoubleLinkMenu = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setSearchText('');
|
|
||||||
setIsShow(true);
|
|
||||||
editor.scrollManager.lock();
|
|
||||||
const rect =
|
|
||||||
editor.selection.currentSelectInfo?.browserSelection
|
|
||||||
?.getRangeAt(0)
|
|
||||||
?.getBoundingClientRect();
|
|
||||||
if (rect) {
|
|
||||||
const rectTop = rect.top;
|
|
||||||
const { top, left } =
|
|
||||||
editor.container.getBoundingClientRect();
|
|
||||||
setReferenceMenuStyle({
|
|
||||||
top: rectTop - top,
|
|
||||||
left: rect.left - left,
|
|
||||||
height: rect.height,
|
|
||||||
});
|
|
||||||
setAnchorEl(ref.current);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isShow) {
|
if (isOpen) {
|
||||||
const searchText =
|
const searchText =
|
||||||
editor.blockHelper.getDoubleLinkSearchSlashText(blockId);
|
editor.blockHelper.getDoubleLinkSearchSlashText(curBlockId);
|
||||||
if (searchText && searchText.startsWith('[[')) {
|
if (searchText && searchText.startsWith('[[')) {
|
||||||
setSearchText(searchText.slice(2).trim());
|
setSearchText(searchText.slice(2).trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[editor, isShow, blockId, hideMenu]
|
[editor, isOpen, curBlockId, hideMenu]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleKeyup = useCallback(
|
const handleKeyup = useCallback(
|
||||||
(event: React.KeyboardEvent<HTMLDivElement>) => handleSearch(event),
|
(event: React.KeyboardEvent<HTMLDivElement>) => searchChange(event),
|
||||||
[handleSearch]
|
[searchChange]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
@@ -223,75 +221,86 @@ export const DoubleLinkMenu = ({
|
|||||||
};
|
};
|
||||||
}, [handleKeyup, handleKeyDown, hooks]);
|
}, [handleKeyup, handleKeyDown, hooks]);
|
||||||
|
|
||||||
const handleSelected = async (linkBlockId: string) => {
|
const insertDoubleLink = useCallback(
|
||||||
if (blockId) {
|
async (pageId: string) => {
|
||||||
if (linkBlockId === ADD_NEW_SUB_PAGE) {
|
editor.blockHelper.setSelectDoubleLinkSearchSlash(curBlockId);
|
||||||
handleAddSubPage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (linkBlockId === ADD_NEW_PAGE) {
|
|
||||||
setIsNewPage(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isNewPage) {
|
|
||||||
const newPage = await services.api.editorBlock.create({
|
|
||||||
workspace: editor.workspace,
|
|
||||||
type: 'page' as const,
|
|
||||||
});
|
|
||||||
await services.api.pageTree.addChildPageToWorkspace(
|
|
||||||
editor.workspace,
|
|
||||||
linkBlockId,
|
|
||||||
newPage.id
|
|
||||||
);
|
|
||||||
linkBlockId = newPage.id;
|
|
||||||
}
|
|
||||||
editor.blockHelper.setSelectDoubleLinkSearchSlash(blockId);
|
|
||||||
await editor.blockHelper.insertDoubleLink(
|
await editor.blockHelper.insertDoubleLink(
|
||||||
editor.workspace,
|
editor.workspace,
|
||||||
linkBlockId,
|
pageId,
|
||||||
blockId
|
curBlockId
|
||||||
);
|
);
|
||||||
hideMenu();
|
hideMenu();
|
||||||
|
},
|
||||||
|
[editor, curBlockId, hideMenu]
|
||||||
|
);
|
||||||
|
|
||||||
|
const addSubPage = useCallback(
|
||||||
|
async (parentPageId: string) => {
|
||||||
|
const newPage = await services.api.editorBlock.create({
|
||||||
|
workspace: editor.workspace,
|
||||||
|
type: 'page' as const,
|
||||||
|
});
|
||||||
|
services.api.editorBlock.update({
|
||||||
|
id: newPage.id,
|
||||||
|
workspace: editor.workspace,
|
||||||
|
properties: {
|
||||||
|
text: { value: [{ text: searchText }] },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await services.api.pageTree.addChildPageToWorkspace(
|
||||||
|
editor.workspace,
|
||||||
|
parentPageId,
|
||||||
|
newPage.id
|
||||||
|
);
|
||||||
|
return newPage.id;
|
||||||
|
},
|
||||||
|
[searchText, editor]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSelected = async (id: string) => {
|
||||||
|
if (curBlockId) {
|
||||||
|
if (id === ADD_NEW_PAGE) {
|
||||||
|
setInAddNewPage(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
newPageSearchRef.current?.focus();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (id === ADD_NEW_SUB_PAGE) {
|
||||||
|
const pageId = await addSubPage(getPageId());
|
||||||
|
insertDoubleLink(pageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (inAddNewPage) {
|
||||||
|
const pageId = await addSubPage(id);
|
||||||
|
insertDoubleLink(pageId);
|
||||||
|
} else {
|
||||||
|
insertDoubleLink(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleFilterChange = useCallback(
|
||||||
blockId && editor.blockHelper.removeDoubleLinkSearchSlash(blockId);
|
async (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
};
|
const text = e.target.value;
|
||||||
|
|
||||||
const handleAddSubPage = async () => {
|
await setFilterText(text);
|
||||||
const newPage = await services.api.editorBlock.create({
|
},
|
||||||
workspace: editor.workspace,
|
[]
|
||||||
type: 'page' as const,
|
);
|
||||||
});
|
|
||||||
setIsNewPage(false);
|
|
||||||
services.api.editorBlock.update({
|
|
||||||
id: newPage.id,
|
|
||||||
workspace: editor.workspace,
|
|
||||||
properties: {
|
|
||||||
text: { value: [{ text: searchText }] },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await services.api.pageTree.addChildPageToWorkspace(
|
|
||||||
editor.workspace,
|
|
||||||
getPageId(),
|
|
||||||
newPage.id
|
|
||||||
);
|
|
||||||
handleSelected(newPage.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={dialogRef}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
width: '10px',
|
width: '10px',
|
||||||
...referenceMenuStyle,
|
...doubleLinkMenuStyle,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MuiClickAwayListener onClickAway={() => hideMenu()}>
|
<MuiClickAwayListener onClickAway={() => hideMenu()}>
|
||||||
<Popper
|
<Popper
|
||||||
open={isShow}
|
open={isOpen}
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
transition
|
transition
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
@@ -304,25 +313,26 @@ export const DoubleLinkMenu = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Paper>
|
<Paper>
|
||||||
<DoubleLinkMenuWrapper>
|
{inAddNewPage && (
|
||||||
<SearchContainer ref={ref1}>
|
<NewPageSearchContainer>
|
||||||
<OutlinedInput
|
<Input
|
||||||
placeholder="Filter actions..."
|
ref={newPageSearchRef}
|
||||||
onChange={() => {}}
|
value={filterText}
|
||||||
|
onChange={handleFilterChange}
|
||||||
|
placeholder="Search page to add to..."
|
||||||
/>
|
/>
|
||||||
</SearchContainer>
|
</NewPageSearchContainer>
|
||||||
<DoubleLinkMenuContainer
|
)}
|
||||||
editor={editor}
|
<DoubleLinkMenuContainer
|
||||||
hooks={hooks}
|
editor={editor}
|
||||||
style={style}
|
hooks={hooks}
|
||||||
isShow={true}
|
style={style}
|
||||||
blockId={blockId}
|
blockId={curBlockId}
|
||||||
onSelected={handleSelected}
|
onSelected={handleSelected}
|
||||||
onClose={handleClose}
|
onClose={hideMenu}
|
||||||
items={items}
|
items={menuItems}
|
||||||
types={types}
|
types={menuTypes}
|
||||||
/>
|
/>
|
||||||
</DoubleLinkMenuWrapper>
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grow>
|
</Grow>
|
||||||
)}
|
)}
|
||||||
@@ -332,15 +342,6 @@ export const DoubleLinkMenu = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DoubleLinkMenuWrapper = styled('div')({
|
const NewPageSearchContainer = styled('div')({
|
||||||
zIndex: 1,
|
padding: '8px 8px 0px 8px',
|
||||||
});
|
|
||||||
|
|
||||||
const SearchContainer = styled('div')({
|
|
||||||
padding: '8px 8px',
|
|
||||||
input: {
|
|
||||||
height: '28px',
|
|
||||||
padding: '5px 10px',
|
|
||||||
with: '300px',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user