mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-15 05:38:00 +08:00
feat: Intra-line double link interaction
This commit is contained in:
@@ -2,6 +2,7 @@ import { PagesIcon } from '@toeverything/components/icons';
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Descendant } from 'slate';
|
import { Descendant } from 'slate';
|
||||||
|
import { RenderElementProps } from 'slate-react';
|
||||||
|
|
||||||
export type DoubleLinkElement = {
|
export type DoubleLinkElement = {
|
||||||
type: 'link';
|
type: 'link';
|
||||||
@@ -11,17 +12,17 @@ export type DoubleLinkElement = {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DoubleLinkComponent = ({ attributes, children, element }: any) => {
|
export const DoubleLinkComponent = (props: RenderElementProps) => {
|
||||||
|
const { attributes, children, element } = props;
|
||||||
|
const doubleLinkElement = element as DoubleLinkElement;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleClickLinkText = useCallback(
|
const handleClickLinkText = useCallback(
|
||||||
(event: React.MouseEvent<HTMLAnchorElement>) => {
|
(event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
event.preventDefault();
|
const { workspaceId, blockId } = doubleLinkElement;
|
||||||
event.stopPropagation();
|
|
||||||
const { workspaceId, blockId } = element;
|
|
||||||
navigate(`/${workspaceId}/${blockId}`);
|
navigate(`/${workspaceId}/${blockId}`);
|
||||||
},
|
},
|
||||||
[element, navigate]
|
[doubleLinkElement, navigate]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -30,7 +31,7 @@ export const DoubleLinkComponent = ({ attributes, children, element }: any) => {
|
|||||||
<a
|
<a
|
||||||
{...attributes}
|
{...attributes}
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
href={`/${element.workspaceId}/${element.blockId}`}
|
href={`/${doubleLinkElement.workspaceId}/${doubleLinkElement.blockId}`}
|
||||||
>
|
>
|
||||||
<span onClick={handleClickLinkText}>{children}</span>
|
<span onClick={handleClickLinkText}>{children}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ export class AsyncBlock {
|
|||||||
const oldData = this.raw_data;
|
const oldData = this.raw_data;
|
||||||
this.raw_data = blockData;
|
this.raw_data = blockData;
|
||||||
this.raw_data = await this.filterPageInvalidChildren(blockData);
|
this.raw_data = await this.filterPageInvalidChildren(blockData);
|
||||||
this.raw_data = await this.updateDoubleLinkBlock(this.raw_data);
|
|
||||||
this.emit('update', { block: this, oldData });
|
this.emit('update', { block: this, oldData });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import {
|
import { CommonList, CommonListItem } from '@toeverything/components/common';
|
||||||
CommonList,
|
|
||||||
commonListContainer,
|
|
||||||
CommonListItem,
|
|
||||||
} from '@toeverything/components/common';
|
|
||||||
import { styled } from '@toeverything/components/ui';
|
import { styled } from '@toeverything/components/ui';
|
||||||
import { HookType, PluginHooks, Virgo } from '@toeverything/framework/virgo';
|
import { HookType, PluginHooks, Virgo } from '@toeverything/framework/virgo';
|
||||||
import { domToRect } from '@toeverything/utils';
|
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
export type DoubleLinkMenuContainerProps = {
|
export type DoubleLinkMenuContainerProps = {
|
||||||
@@ -25,35 +20,6 @@ export const DoubleLinkMenuContainer = (
|
|||||||
const { hooks, onSelected, onClose, types, style, items } = props;
|
const { hooks, onSelected, onClose, types, style, items } = props;
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
const [currentItem, setCurrentItem] = useState<string | undefined>();
|
const [currentItem, setCurrentItem] = useState<string | undefined>();
|
||||||
const [needCheckIntoView, setNeedCheckIntoView] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (needCheckIntoView) {
|
|
||||||
if (currentItem && menuRef.current) {
|
|
||||||
const itemElement =
|
|
||||||
menuRef.current.querySelector<HTMLButtonElement>(
|
|
||||||
`.item-${currentItem}`
|
|
||||||
);
|
|
||||||
const scrollElement =
|
|
||||||
menuRef.current.querySelector<HTMLButtonElement>(
|
|
||||||
`.${commonListContainer}`
|
|
||||||
);
|
|
||||||
if (itemElement) {
|
|
||||||
const itemRect = domToRect(itemElement);
|
|
||||||
const scrollRect = domToRect(scrollElement);
|
|
||||||
if (
|
|
||||||
itemRect.top < scrollRect.top ||
|
|
||||||
itemRect.bottom > scrollRect.bottom
|
|
||||||
) {
|
|
||||||
itemElement.scrollIntoView({
|
|
||||||
block: 'nearest',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setNeedCheckIntoView(false);
|
|
||||||
}
|
|
||||||
}, [needCheckIntoView, currentItem]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (types && !currentItem) {
|
if (types && !currentItem) {
|
||||||
@@ -61,19 +27,6 @@ export const DoubleLinkMenuContainer = (
|
|||||||
}
|
}
|
||||||
}, [currentItem, onClose, types]);
|
}, [currentItem, onClose, types]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (types) {
|
|
||||||
if (!types.includes(currentItem)) {
|
|
||||||
setNeedCheckIntoView(true);
|
|
||||||
if (types.length) {
|
|
||||||
setCurrentItem(types[0]);
|
|
||||||
} else {
|
|
||||||
setCurrentItem(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [types, currentItem]);
|
|
||||||
|
|
||||||
const handleUpDownKey = useCallback(
|
const handleUpDownKey = useCallback(
|
||||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
if (types && ['ArrowUp', 'ArrowDown'].includes(event.code)) {
|
if (types && ['ArrowUp', 'ArrowDown'].includes(event.code)) {
|
||||||
@@ -85,7 +38,6 @@ export const DoubleLinkMenuContainer = (
|
|||||||
if (currentItem) {
|
if (currentItem) {
|
||||||
const idx = types.indexOf(currentItem);
|
const idx = types.indexOf(currentItem);
|
||||||
if (isUpkey ? idx > 0 : idx < types.length - 1) {
|
if (isUpkey ? idx > 0 : idx < types.length - 1) {
|
||||||
setNeedCheckIntoView(true);
|
|
||||||
setCurrentItem(types[isUpkey ? idx - 1 : idx + 1]);
|
setCurrentItem(types[isUpkey ? idx - 1 : idx + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,19 +156,14 @@ export const DoubleLinkMenu = ({
|
|||||||
});
|
});
|
||||||
setAnchorEl(dialogRef.current);
|
setAnchorEl(dialogRef.current);
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
const textSelection = editor.blockHelper.selectionToSlateRange(
|
||||||
const textSelection = editor.blockHelper.selectionToSlateRange(
|
nextNodeId,
|
||||||
nextNodeId,
|
editor.selection.currentSelectInfo?.browserSelection
|
||||||
editor.selection.currentSelectInfo.browserSelection
|
);
|
||||||
);
|
if (textSelection) {
|
||||||
if (textSelection) {
|
const { anchor } = textSelection;
|
||||||
const { anchor } = textSelection;
|
editor.blockHelper.setDoubleLinkSearchSlash(nextNodeId, anchor);
|
||||||
editor.blockHelper.setDoubleLinkSearchSlash(
|
}
|
||||||
nextNodeId,
|
|
||||||
anchor
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[editor]
|
[editor]
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user