diff --git a/libs/components/editor-blocks/src/blocks/text/TextView.tsx b/libs/components/editor-blocks/src/blocks/text/TextView.tsx index 633595c904..5edf231217 100644 --- a/libs/components/editor-blocks/src/blocks/text/TextView.tsx +++ b/libs/components/editor-blocks/src/blocks/text/TextView.tsx @@ -14,7 +14,7 @@ import { CreateView } from '@toeverything/framework/virgo'; import { BlockContainer } from '../../components/BlockContainer'; import { IndentWrapper } from '../../components/IndentWrapper'; import { TextManage } from '../../components/text-manage'; -import { tabBlock } from '../../utils/indent'; +import { dedentBlock, tabBlock } from '../../utils/indent'; interface CreateTextView extends CreateView { // TODO: need to optimize containerClassName?: string; @@ -115,7 +115,19 @@ export const TextView = ({ return false; } - const preParent = await parentBlock.previousSibling(); + // The parent block is group block or is the root block. + // Merge block to previous sibling. + // + // - group/root <- parent block + // - text1 <- preNode + // - text2 <- press backspace before target block + // - children + // + // --- + // + // - group/root + // - text1text2 <- merge block to previous sibling + // - children <- children should switch parent block if ( Protocol.Block.Type.group === parentBlock.type || editor.getRootBlockId() === parentBlock.id @@ -130,10 +142,7 @@ export const TextView = ({ block.id, 'end' ); - if ( - block.getProperty('text').value[0] && - block.getProperty('text').value[0]?.text !== '' - ) { + if (!block.blockProvider?.isEmpty()) { const value = [ ...preNode.getProperty('text').value, ...block.getProperty('text').value, @@ -145,12 +154,13 @@ export const TextView = ({ await preNode.append(...children); await block.remove(); } else { + // If not pre node, block should be merged to the parent block // TODO: point does not clear await editor.selectionManager.activePreviousNode( block.id, 'start' ); - if (block.blockProvider.isEmpty()) { + if (block.blockProvider?.isEmpty()) { await block.remove(); const parentChild = await parentBlock.children(); if ( @@ -158,6 +168,8 @@ export const TextView = ({ Protocol.Block.Type.group && !parentChild.length ) { + const preParent = + await parentBlock.previousSibling(); await editor.selectionManager.setSelectedNodesIds( [preParent?.id ?? editor.getRootBlockId()] ); @@ -198,17 +210,9 @@ export const TextView = ({ await parentBlock.remove(); } return true; - } else { - const nextNodes = await block.nextSiblings(); - for (const nextNode of nextNodes) { - await nextNode.remove(); - } - block.append(...nextNodes); - editor.commands.blockCommands.moveBlockAfter( - block.id, - parentBlock.id - ); } + + dedentBlock(editor, block); return true; } ); diff --git a/libs/components/editor-blocks/src/utils/indent.ts b/libs/components/editor-blocks/src/utils/indent.ts index 7f0a2bad08..2c3eee6719 100644 --- a/libs/components/editor-blocks/src/utils/indent.ts +++ b/libs/components/editor-blocks/src/utils/indent.ts @@ -1,6 +1,6 @@ import { - type BlockEditor, supportChildren, + type BlockEditor, } from '@toeverything/components/editor-core'; import { Protocol } from '@toeverything/datasource/db-service'; import { AsyncBlock } from '@toeverything/framework/virgo'; @@ -81,7 +81,7 @@ const indentBlock = async (block: TodoAsyncBlock) => { * └─ [ ] * ``` */ -const dedentBlock = async (editor: BlockEditor, block: AsyncBlock) => { +export const dedentBlock = async (editor: BlockEditor, block: AsyncBlock) => { if (editor.getRootBlockId() === block.id) { return false; } diff --git a/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts b/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts index 9da0ed55ee..a96de8eb93 100644 --- a/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts +++ b/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts @@ -1,4 +1,5 @@ /* eslint-disable max-lines */ +import { Protocol } from '@toeverything/datasource/db-service'; import { domToRect, Point } from '@toeverything/utils'; import { AsyncBlock } from '../..'; import { GridDropType } from '../commands/types'; @@ -6,7 +7,6 @@ import { Editor } from '../editor'; import { BlockDropPlacement, GroupDirection } from '../types'; // TODO: Evaluate implementing custom events with Rxjs import EventEmitter from 'eventemitter3'; -import { Protocol } from '@toeverything/datasource/db-service'; enum DragType { dragBlock = 'dragBlock', @@ -281,6 +281,10 @@ export class DragDropManager { this._editor.getRootBlockId() ); let direction = BlockDropPlacement.none; + if (!rootBlock || !rootBlock.dom) { + console.warn('Can not find dom bind with block', rootBlock); + return; + } const rootBlockRect = domToRect(rootBlock.dom); let targetBlock: AsyncBlock | undefined; let typesInfo = { @@ -303,6 +307,10 @@ export class DragDropManager { if (direction !== BlockDropPlacement.none) { const blockList = await this._editor.getBlockListByLevelOrder(); targetBlock = blockList.find(block => { + if (!block.dom) { + console.warn('Can not find dom bind with block', block); + return false; + } const domRect = domToRect(block.dom); const pointChecker = direction === BlockDropPlacement.outerLeft diff --git a/libs/components/editor-core/src/editor/selection/selection.ts b/libs/components/editor-core/src/editor/selection/selection.ts index 406f9adecb..e6c1f94c83 100644 --- a/libs/components/editor-core/src/editor/selection/selection.ts +++ b/libs/components/editor-core/src/editor/selection/selection.ts @@ -1,15 +1,16 @@ /* eslint-disable max-lines */ import { + debounce, domToRect, + getBlockIdByDom, + last, Point, Rect, - last, without, - debounce, - getBlockIdByDom, } from '@toeverything/utils'; import EventEmitter from 'eventemitter3'; +import { Protocol } from '@toeverything/datasource/db-service'; import { BlockEditor } from '../..'; import { AsyncBlock } from '../block'; import { VirgoSelection } from '../types'; @@ -18,19 +19,17 @@ import { changeEventName, CursorTypes, IdList, + SelectBlock, selectEndEventName, SelectEventCallbackTypes, SelectEventTypes, + SelectInfo, SelectionSettings, SelectionSettingsMap, SelectionTypes, SelectPosition, - SelectBlock, - SelectInfo, } from './types'; import { isLikeBlockListIds } from './utils'; -import { Protocol } from '@toeverything/datasource/db-service'; -import { Editor } from 'slate'; // IMP: maybe merge active and select into single function export type SelectionInfo = InstanceType< @@ -336,12 +335,12 @@ export class SelectionManager implements VirgoSelection { }); for await (const childBlock of selectableChildren) { const { dom } = childBlock; - if (dom && selectionRect.isIntersect(domToRect(dom))) { - selectedNodes.push(childBlock); - } if (!dom) { console.warn('can not find dom bind with block'); } + if (dom && selectionRect.isIntersect(domToRect(dom))) { + selectedNodes.push(childBlock); + } } // if just only has one selected maybe select the children if (selectedNodes.length === 1) { @@ -1063,10 +1062,10 @@ export class SelectionManager implements VirgoSelection { index: number, blockId: string ): Promise { - let preRang = document.createRange(); + const preRang = document.createRange(); preRang.setStart(nowRange.startContainer, index); preRang.setEnd(nowRange.endContainer, index); - let prePosition = preRang.getClientRects().item(0); + const prePosition = preRang.getClientRects().item(0); this.activeNodeByNodeId( blockId, new Point(prePosition.left, prePosition.bottom) 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 4fe50d3b90..b69cdd848a 100644 --- a/libs/components/editor-core/src/editor/views/base-view.ts +++ b/libs/components/editor-core/src/editor/views/base-view.ts @@ -9,10 +9,10 @@ import { BlockDecoration, MapOperation, } from '@toeverything/datasource/jwt'; +import { cloneDeep } from '@toeverything/utils'; import type { EventData } from '../block'; import { AsyncBlock } from '../block'; import type { Editor } from '../editor'; -import { cloneDeep } from '@toeverything/utils'; import { SelectBlock } from '../selection'; export interface CreateView { @@ -114,7 +114,21 @@ export abstract class BaseView { // Whether the component is empty isEmpty(block: AsyncBlock): boolean { const text = block.getProperty('text'); - return !text?.value?.[0]?.text; + const result = !text?.value?.[0]?.text; + + // Assert that the text is really empty + if ( + result && + block.getProperty('text')?.value.some(content => content.text) + ) { + console.warn( + 'Assertion isEmpty error! The block has an empty start fragment, but it is not empty', + block + ); + } + // Assert end + + return result; } getSelProperties(block: AsyncBlock, selectInfo: any): DefaultColumnsValue { diff --git a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuPlugin.tsx b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuPlugin.tsx index 3b432167ee..9c339e8630 100644 --- a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuPlugin.tsx +++ b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuPlugin.tsx @@ -1,15 +1,15 @@ -import { HookType, BlockDropPlacement } from '@toeverything/framework/virgo'; -import { StrictMode } from 'react'; -import { BasePlugin } from '../../base-plugin'; -import { ignoreBlockTypes } from './menu-config'; -import { - LineInfoSubject, - LeftMenuDraggable, - BlockDomInfo, -} from './LeftMenuDraggable'; -import { PluginRenderRoot } from '../../utils'; -import { Subject, throttleTime } from 'rxjs'; +import { BlockDropPlacement, HookType } from '@toeverything/framework/virgo'; import { domToRect, last, Point } from '@toeverything/utils'; +import { StrictMode } from 'react'; +import { Subject, throttleTime } from 'rxjs'; +import { BasePlugin } from '../../base-plugin'; +import { PluginRenderRoot } from '../../utils'; +import { + BlockDomInfo, + LeftMenuDraggable, + LineInfoSubject, +} from './LeftMenuDraggable'; +import { ignoreBlockTypes } from './menu-config'; const DRAG_THROTTLE_DELAY = 60; export class LeftMenuPlugin extends BasePlugin { private _mousedown?: boolean; @@ -111,6 +111,10 @@ export class LeftMenuPlugin extends BasePlugin { block.dom, block.id ); + if (!targetBlock.dom) { + console.warn('Can not find dom bind with block', targetBlock); + return; + } this._lineInfo.next({ direction, blockInfo: { diff --git a/libs/components/ui/src/button/IconButton.tsx b/libs/components/ui/src/button/IconButton.tsx index 0421ecdaf7..88114f7b2c 100644 --- a/libs/components/ui/src/button/IconButton.tsx +++ b/libs/components/ui/src/button/IconButton.tsx @@ -3,9 +3,7 @@ import type { MouseEventHandler, PropsWithChildren, } from 'react'; -import { cx } from '../clsx'; import { styled } from '../styled'; -import { buttonStatus } from './constants'; /* Temporary solution, needs to be adjusted */ const SIZE_SMALL = 'small' as const; @@ -29,13 +27,18 @@ const SIZE_CONFIG = { type SizeType = keyof typeof SIZE_CONFIG; -interface IconButtonProps { +type IconButtonContainerProps = { + size?: SizeType; + hoverColor?: string; // CSSProperties['backgroundColor']; + backgroundColor?: string; // CSSProperties['backgroundColor']; + disabled?: boolean; +}; + +interface IconButtonProps extends IconButtonContainerProps { onClick?: MouseEventHandler; disabled?: boolean; style?: CSSProperties; className?: string; - size?: SizeType; - hoverColor?: string; } export const IconButton = ({ @@ -50,55 +53,40 @@ export const IconButton = ({ {...props} onClick={disabled ? undefined : onClick} disabled={disabled} - className={cx({ [buttonStatus.disabled]: disabled }, className)} > {children} ); }; -const Container = styled('button')<{ - size?: SizeType; - hoverColor?: string; -}>(({ theme, size = SIZE_MIDDLE, hoverColor }) => { - const { iconSize, areaSize } = SIZE_CONFIG[size]; +const Container = styled('button')( + ({ theme, size = SIZE_MIDDLE, hoverColor, backgroundColor, disabled }) => { + const { iconSize, areaSize } = SIZE_CONFIG[size]; - return { - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - width: areaSize, - height: areaSize, - backgroundColor: 'transparent', - color: theme.affine.palette.icons, - padding: theme.affine.spacing.iconPadding, - borderRadius: '3px', - - '& svg': { - width: iconSize, - height: iconSize, + return { display: 'flex', justifyContent: 'center', alignItems: 'center', - }, + width: areaSize, + height: areaSize, + backgroundColor: backgroundColor ?? 'transparent', + color: theme.affine.palette.icons, + padding: theme.affine.spacing.iconPadding, + borderRadius: '3px', - '&:hover': { - backgroundColor: hoverColor || theme.affine.palette.hover, - }, + '& svg': { + width: iconSize, + height: iconSize, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, - [`&${buttonStatus.hover}`]: { - backgroundColor: theme.affine.palette.hover, - }, + '&:hover': { + backgroundColor: hoverColor || theme.affine.palette.hover, + }, - '&:focus': { - color: theme.affine.palette.primary, - }, - [`&.${buttonStatus.focus}`]: { - color: theme.affine.palette.primary, - }, - - [`&${buttonStatus.disabled}`]: { - cursor: 'not-allowed', - }, - }; -}); + ...(disabled ? { cursor: 'not-allowed' } : {}), + }; + } +); diff --git a/libs/components/ui/src/button/constants.ts b/libs/components/ui/src/button/constants.ts deleted file mode 100644 index 22ec35c5d1..0000000000 --- a/libs/components/ui/src/button/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const buttonStatus = { - hover: '.hover', - focus: '.focus', - active: '.focus', - disabled: '.disabled', -};