diff --git a/libs/components/common/src/lib/text/plugins/DoubleLink.tsx b/libs/components/common/src/lib/text/plugins/DoubleLink.tsx index 5b819e4212..42f50740f3 100644 --- a/libs/components/common/src/lib/text/plugins/DoubleLink.tsx +++ b/libs/components/common/src/lib/text/plugins/DoubleLink.tsx @@ -26,15 +26,20 @@ export const DoubleLinkComponent = (props: RenderElementProps) => { [doubleLinkElement, navigate] ); + const displayValue = doubleLinkElement.children + .map((item: any) => item.text) + .join(''); + return ( - - - - {children} + + + + + {children} + {displayValue} + ); diff --git a/libs/components/common/src/lib/text/plugins/link.tsx b/libs/components/common/src/lib/text/plugins/link.tsx index 619239c64d..971d8dc62b 100644 --- a/libs/components/common/src/lib/text/plugins/link.tsx +++ b/libs/components/common/src/lib/text/plugins/link.tsx @@ -1,44 +1,42 @@ -import React, { - useEffect, - useMemo, - useRef, - useState, - useCallback, - KeyboardEvent, - MouseEvent, - memo, -} from 'react'; -import { createPortal } from 'react-dom'; -import { useNavigate } from 'react-router-dom'; - -import isUrl from 'is-url'; -import style9 from 'style9'; -import { - Editor, - Transforms, - Element as SlateElement, - Descendant, - Range as SlateRange, - Node, -} from 'slate'; -import { ReactEditor } from 'slate-react'; -import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import EditIcon from '@mui/icons-material/Edit'; import LinkOffIcon from '@mui/icons-material/LinkOff'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import { LinkIcon } from '@toeverything/components/icons'; import { MuiTooltip as Tooltip, - styled, muiTooltipClasses, + styled, type MuiTooltipProps, } from '@toeverything/components/ui'; import { getRelativeUrlForInternalPageUrl, isInternalPageUrl, } from '@toeverything/utils'; - +import isUrl from 'is-url'; +import React, { + KeyboardEvent, + memo, + MouseEvent, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { createPortal } from 'react-dom'; +import { useNavigate } from 'react-router-dom'; +import { + Descendant, + Editor, + Element as SlateElement, + Node, + Range as SlateRange, + Transforms, +} from 'slate'; +import { ReactEditor } from 'slate-react'; +import style9 from 'style9'; import { getRandomString } from '../utils'; -import { colors } from '../../colors'; -import { LinkIcon } from '@toeverything/components/icons'; + export type LinkElement = { type: 'link'; url: string; @@ -47,13 +45,20 @@ export type LinkElement = { }; export const withLinks = (editor: ReactEditor) => { - const { isInline } = editor; + const { isInline, isVoid } = editor; editor.isInline = element => { // @ts-ignore return element.type === 'link' ? true : isInline(element); }; + editor.isVoid = element => { + // @ts-ignore + return element.type === 'link' && element.linkType === 'doubleLink' + ? true + : isVoid(element); + }; + return editor; }; diff --git a/libs/components/common/src/lib/text/slate-utils.ts b/libs/components/common/src/lib/text/slate-utils.ts index cbe9a24ed3..03701d0646 100644 --- a/libs/components/common/src/lib/text/slate-utils.ts +++ b/libs/components/common/src/lib/text/slate-utils.ts @@ -405,11 +405,11 @@ Editor.before = function ( if (element) { if (isInlineAndVoid(editor, element)) { // Inline entities need to be drilled out - // target = Editor.before(editor, target); - target = { - path: [0, path[1] - 1], - offset: 0, - }; + target = Editor.before(editor, target); + // target = { + // path: [0, path[1] - 1], + // offset: 0, + // }; } else if (editor.isInline(element) && !editor.isVoid(element)) { // Inline styles such as hyperlinks need to drill directly into it const inlineTextLength = element?.children?.[0]?.text?.length; diff --git a/libs/components/editor-blocks/src/components/text-manage/TextManage.tsx b/libs/components/editor-blocks/src/components/text-manage/TextManage.tsx index fa15639172..72faaef8a7 100644 --- a/libs/components/editor-blocks/src/components/text-manage/TextManage.tsx +++ b/libs/components/editor-blocks/src/components/text-manage/TextManage.tsx @@ -1,6 +1,5 @@ /* eslint-disable max-lines */ import { - CustomText, Text, type SlateUtils, type TextProps, @@ -109,7 +108,7 @@ const findLowestCommonAncestor = async ( export const TextManage = forwardRef( (props, ref) => { - const { block, editor, handleBackSpace, ...otherOptions } = props; + const { block, editor, handleEnter, ...otherOptions } = props; const defaultRef = useRef(null); // Maybe there is a better way const textRef = @@ -448,58 +447,35 @@ export const TextManage = forwardRef( } }; - const onBackspace: TextProps['handleBackSpace'] = async props => { - const { isCollAndStart, splitContents, selection } = props; - if (!isCollAndStart) { - const { - contentBeforeSelection, - contentAfterSelection, - contentSelection, - } = splitContents; - const { selectionStart, selectionEnd } = selection; - let hasDeleteDoubleLink = false; - - const beforeContent = [...contentBeforeSelection.content]; - const lastItem = beforeContent[beforeContent.length - 1]; - if ( - 'linkType' in lastItem && - lastItem.linkType === 'doubleLink' - ) { - if ( - selectionStart === selectionEnd || - selectionStart.offset - ) { - beforeContent.splice(beforeContent.length - 1, 1); - hasDeleteDoubleLink = true; + const onTextEnter: TextProps['handleEnter'] = async props => { + const { splitContents } = props; + if (splitContents) { + const { contentBeforeSelection, contentAfterSelection } = + splitContents; + // after[after.length - 1]; + if (!contentBeforeSelection.isEmpty) { + const beforeSelection = contentBeforeSelection.content; + const lastItem: any = + beforeSelection.length > 0 + ? beforeSelection[beforeSelection.length - 1] + : null; + if (lastItem?.linkType === 'doubleLink') { + contentBeforeSelection.content.push({ text: '' }); } } - - const afterContent = [...contentAfterSelection.content]; - const beginItem = afterContent[0]; - const lastSel = contentSelection.content[0]; - if ( - selectionEnd.offset && - 'linkType' in beginItem && - beginItem.linkType === 'doubleLink' && - 'linkType' in lastSel && - lastSel.linkType === 'doubleLink' - ) { - afterContent.splice(0, 1); - hasDeleteDoubleLink = true; - } - - if (hasDeleteDoubleLink) { - await block.setProperty('text', { - value: [ - ...beforeContent, - ...afterContent, - ] as CustomText[], - }); - return true; + if (!contentAfterSelection.isEmpty) { + const afterSelection = contentAfterSelection.content; + const firstItem: any = + afterSelection.length > 0 ? afterSelection[0] : null; + if (firstItem?.linkType === 'doubleLink') { + contentAfterSelection.content.splice(0, 0, { + text: '', + }); + } } } - return (handleBackSpace && handleBackSpace(props)) || false; + return handleEnter && handleEnter(props); }; if (!properties || !properties.text) { @@ -523,7 +499,7 @@ export const TextManage = forwardRef( handleUndo={onUndo} handleRedo={onRedo} handleEsc={onKeyboardEsc} - handleBackSpace={onBackspace} + handleEnter={onTextEnter} {...otherOptions} /> ); diff --git a/libs/components/editor-core/src/editor/views/base-view.ts b/libs/components/editor-core/src/editor/views/base-view.ts index 2b3002f500..df110cf87c 100644 --- a/libs/components/editor-core/src/editor/views/base-view.ts +++ b/libs/components/editor-core/src/editor/views/base-view.ts @@ -1,5 +1,3 @@ -import { ComponentType, ReactElement } from 'react'; - import type { Column, DefaultColumnsValue, @@ -10,6 +8,7 @@ import { MapOperation, } from '@toeverything/datasource/jwt'; import { cloneDeep } from '@toeverything/utils'; +import { ComponentType, ReactElement } from 'react'; import type { EventData } from '../block'; import { AsyncBlock } from '../block'; import type { Editor } from '../editor'; @@ -113,7 +112,11 @@ export abstract class BaseView { // Whether the component is empty isEmpty(block: AsyncBlock): boolean { const text = block.getProperty('text'); - const result = !text?.value?.[0]?.text; + // const result = !text?.value?.[0]?.text; + const result = + text?.value?.findIndex( + (item: any) => item.text || item.children?.length + ) === -1; // Assert that the text is really empty if (