feat: Intra-line double link interaction
This commit is contained in:
@@ -20,6 +20,7 @@ export const DoubleLinkComponent = ({ attributes, children, element }: any) => {
|
|||||||
<PagesIcon style={{ verticalAlign: 'middle', height: '20px' }} />
|
<PagesIcon style={{ verticalAlign: 'middle', height: '20px' }} />
|
||||||
<a
|
<a
|
||||||
{...attributes}
|
{...attributes}
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
href={`/${element.workspaceId}/${element.blockId}`}
|
href={`/${element.workspaceId}/${element.blockId}`}
|
||||||
>
|
>
|
||||||
<span onClick={handleClickLinkText}>{children}</span>
|
<span onClick={handleClickLinkText}>{children}</span>
|
||||||
|
|||||||
@@ -599,6 +599,10 @@ class SlateUtils {
|
|||||||
|
|
||||||
const textChildren: Text[] = [];
|
const textChildren: Text[] = [];
|
||||||
for (const child of fragmentChildren) {
|
for (const child of fragmentChildren) {
|
||||||
|
if ((child as any).type === 'link') {
|
||||||
|
textChildren.push(child as Text);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!('text' in child)) {
|
if (!('text' in child)) {
|
||||||
console.error('Debug information:', point1, point2, fragment);
|
console.error('Debug information:', point1, point2, fragment);
|
||||||
throw new Error('Fragment exists nested!');
|
throw new Error('Fragment exists nested!');
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
} from '@toeverything/components/ui';
|
} from '@toeverything/components/ui';
|
||||||
import { services } from '@toeverything/datasource/db-service';
|
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 React, {
|
import React, {
|
||||||
ChangeEvent,
|
ChangeEvent,
|
||||||
useCallback,
|
useCallback,
|
||||||
@@ -20,12 +19,13 @@ import React, {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
import { QueryBlocks, QueryResult } from '../../search';
|
import { QueryBlocks, QueryResult } from '../../search';
|
||||||
import { DoubleLinkMenuContainer } from './Container';
|
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'];
|
const ARRAY_CODES = ['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'];
|
||||||
|
|
||||||
export type DoubleLinkMenuProps = {
|
export type DoubleLinkMenuProps = {
|
||||||
editor: Virgo;
|
editor: Virgo;
|
||||||
@@ -44,6 +44,7 @@ export const DoubleLinkMenu = ({
|
|||||||
hooks,
|
hooks,
|
||||||
style,
|
style,
|
||||||
}: DoubleLinkMenuProps) => {
|
}: DoubleLinkMenuProps) => {
|
||||||
|
const { page_id: curPageId } = useParams();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const dialogRef = useRef<HTMLDivElement>();
|
const dialogRef = useRef<HTMLDivElement>();
|
||||||
@@ -120,9 +121,12 @@ export const DoubleLinkMenu = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const text = inAddNewPage ? filterText : searchText;
|
const text = inAddNewPage ? filterText : searchText;
|
||||||
QueryBlocks(editor, text, result => {
|
QueryBlocks(editor, text, result => {
|
||||||
|
if (!inAddNewPage) {
|
||||||
|
result = result.filter(item => item.id !== curPageId);
|
||||||
|
}
|
||||||
setSearchResultBlocks(result);
|
setSearchResultBlocks(result);
|
||||||
});
|
});
|
||||||
}, [editor, searchText, filterText, inAddNewPage]);
|
}, [editor, searchText, filterText, inAddNewPage, curPageId]);
|
||||||
|
|
||||||
const hideMenu = useCallback(() => {
|
const hideMenu = useCallback(() => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
@@ -133,23 +137,31 @@ export const DoubleLinkMenu = ({
|
|||||||
|
|
||||||
const searchChange = useCallback(
|
const searchChange = useCallback(
|
||||||
async (event: React.KeyboardEvent<HTMLDivElement>) => {
|
async (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
if (ARRAY_KEYS.includes(event.key)) {
|
if (ARRAY_CODES.includes(event.code)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (event.code === 'Backspace') {
|
||||||
|
const searchText =
|
||||||
|
editor.blockHelper.getDoubleLinkSearchSlashText(curBlockId);
|
||||||
|
if (!searchText || searchText === '[[') {
|
||||||
|
hideMenu();
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
const { type, anchorNode } = editor.selection.currentSelectInfo;
|
const { type, anchorNode } = editor.selection.currentSelectInfo;
|
||||||
if (
|
if (
|
||||||
type === 'Range' &&
|
!isOpen ||
|
||||||
|
(type === 'Range' &&
|
||||||
anchorNode &&
|
anchorNode &&
|
||||||
editor.blockHelper.isSelectionCollapsed(anchorNode.id)
|
anchorNode.id !== curBlockId &&
|
||||||
|
editor.blockHelper.isSelectionCollapsed(anchorNode.id))
|
||||||
) {
|
) {
|
||||||
const text = editor.blockHelper.getBlockTextBeforeSelection(
|
const text = editor.blockHelper.getBlockTextBeforeSelection(
|
||||||
anchorNode.id
|
anchorNode.id
|
||||||
);
|
);
|
||||||
if (text.endsWith('[[')) {
|
if (text.endsWith('[[')) {
|
||||||
if (event.key === 'Backspace') {
|
|
||||||
hideMenu();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
editor.blockHelper.removeDoubleLinkSearchSlash(curBlockId);
|
editor.blockHelper.removeDoubleLinkSearchSlash(curBlockId);
|
||||||
setCurBlockId(anchorNode.id);
|
setCurBlockId(anchorNode.id);
|
||||||
setSearchText('');
|
setSearchText('');
|
||||||
@@ -211,26 +223,18 @@ export const DoubleLinkMenu = ({
|
|||||||
if (event.code === 'Escape') {
|
if (event.code === 'Escape') {
|
||||||
hideMenu();
|
hideMenu();
|
||||||
}
|
}
|
||||||
const { type, anchorNode } = editor.selection.currentSelectInfo;
|
|
||||||
if (
|
if (event.code === 'Backspace') {
|
||||||
type === 'Range' &&
|
const searchText =
|
||||||
anchorNode &&
|
editor.blockHelper.getDoubleLinkSearchSlashText(curBlockId);
|
||||||
editor.blockHelper.isSelectionCollapsed(anchorNode.id)
|
if (!searchText || searchText === '[[') {
|
||||||
) {
|
|
||||||
const text = editor.blockHelper.getBlockTextBeforeSelection(
|
|
||||||
anchorNode.id
|
|
||||||
);
|
|
||||||
if (text.endsWith('[[')) {
|
|
||||||
if (event.key === 'Backspace') {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
hideMenu();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[hideMenu, editor, isOpen]
|
[hideMenu, editor, isOpen, curBlockId]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -294,7 +298,7 @@ export const DoubleLinkMenu = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (id === ADD_NEW_SUB_PAGE) {
|
if (id === ADD_NEW_SUB_PAGE) {
|
||||||
const pageId = await addSubPage(getPageId());
|
const pageId = await addSubPage(curPageId);
|
||||||
insertDoubleLink(pageId);
|
insertDoubleLink(pageId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user