diff --git a/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx b/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx index e6464860c5..3ac519f3c5 100644 --- a/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx +++ b/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx @@ -12,6 +12,8 @@ type GridHandleProps = { blockId: string; enabledAddItem: boolean; draggable: boolean; + alertHandleId: string; + onMouseEnter?: React.MouseEventHandler; }; export const GridHandle: FC = function ({ @@ -21,6 +23,8 @@ export const GridHandle: FC = function ({ onDrag, onMouseDown, draggable, + alertHandleId, + onMouseEnter, }) { const [isMouseDown, setIsMouseDown] = useState(false); const handleMouseDown: React.MouseEventHandler = e => { @@ -44,16 +48,17 @@ export const GridHandle: FC = function ({ editor.selectionManager.setActivatedNodeId(textBlock.id); } }; + + const handleMouseEnter: React.MouseEventHandler = e => { + onMouseEnter && onMouseEnter(e); + }; + return ( {enabledAddItem ? ( = function ({ ); }; -const GridHandleContainer = styled('div')(({ theme }) => ({ +const GridHandleContainer = styled('div')<{ + isMouseDown: boolean; + isAlert: boolean; +}>(({ theme, isMouseDown, isAlert }) => ({ position: 'relative', width: '10px', flexGrow: '0', @@ -78,11 +86,17 @@ const GridHandleContainer = styled('div')(({ theme }) => ({ borderRadius: '1px', backgroundClip: 'content-box', ' &:hover': { - backgroundColor: theme.affine.palette.primary, + backgroundColor: isAlert + ? 'red !important' + : theme.affine.palette.primary, [`.${GRID_ADD_HANDLE_NAME}`]: { display: 'block', }, }, + ...(isMouseDown && + (isAlert + ? { backgroundColor: 'red' } + : { backgroundColor: theme.affine.palette.primary })), })); const AddGridHandle = styled('div')(({ theme }) => ({ diff --git a/libs/components/editor-blocks/src/blocks/grid/Grid.tsx b/libs/components/editor-blocks/src/blocks/grid/Grid.tsx index 77a83ff599..bc27ec4ac6 100644 --- a/libs/components/editor-blocks/src/blocks/grid/Grid.tsx +++ b/libs/components/editor-blocks/src/blocks/grid/Grid.tsx @@ -31,6 +31,7 @@ export const Grid: FC = function (props) { const gridItemCountRef = useRef(); const originalLeftWidth = useRef(GRID_ITEM_MIN_WIDTH); const originalRightWidth = useRef(GRID_ITEM_MIN_WIDTH); + const [alertHandleId, setAlertHandleId] = useState(null); const getLeftRightGridItemDomByIndex = (index: number) => { const gridItems = Array.from(gridContainerRef.current?.children).filter( @@ -117,7 +118,7 @@ export const Grid: FC = function (props) { itemDom.style.width = width; }; - const handleDragGrid = (e: MouseEvent, index: number) => { + const handleDragGrid = async (e: MouseEvent, index: number) => { setIsOnDrag(true); window.getSelection().removeAllRanges(); if (!isSetMouseUp.current) { @@ -165,39 +166,47 @@ export const Grid: FC = function (props) { setItemWidth(leftGrid, newLeft); setItemWidth(rightGrid, newRight); updateDbWidth(leftBlockId, newLeft, rightBlockId, newRight); + [leftBlockId, rightBlockId].forEach(async blockId => { + if (await checkGridItemHasOverflow(blockId)) { + setAlertHandleId(leftBlockId); + } else { + setAlertHandleId(null); + } + }); } } }; - const children = ( - <> - {block.childrenIds.map((id, i) => { - return ( - - - handleDragGrid(event, i)} - editor={editor} - onMouseDown={event => handleMouseDown(event, i)} - blockId={id} - enabledAddItem={ - block.childrenIds.length < MAX_ITEM_COUNT - } - draggable={i !== block.childrenIds.length - 1} - /> - - ); - })} - - ); + const checkGridItemHasOverflow = async (blockId: string) => { + let isOverflow = false; + const block = await editor.getBlockById(blockId); + if (block) { + const blockDom = block.dom; + if (blockDom) { + block.dom.style.overflow = 'scroll'; + if (block.dom.clientWidth !== block.dom.scrollWidth) { + isOverflow = true; + } + blockDom.style.overflow = 'visible'; + } + } + return isOverflow; + }; + + const handleHandleMouseEnter = ( + e: React.MouseEvent, + index: number + ) => { + const leftBlockId = block.childrenIds[index]; + const rightBlockId = block.childrenIds[index + 1]; + [leftBlockId, rightBlockId].forEach(async blockId => { + if (await checkGridItemHasOverflow(blockId)) { + setAlertHandleId(leftBlockId); + } else { + setAlertHandleId(null); + } + }); + }; return ( <> @@ -206,7 +215,35 @@ export const Grid: FC = function (props) { ref={gridContainerRef} isOnDrag={isOnDrag} > - {children} + {block.childrenIds.map((id, i) => { + return ( + + + handleDragGrid(event, i)} + editor={editor} + onMouseDown={event => handleMouseDown(event, i)} + blockId={id} + enabledAddItem={ + block.childrenIds.length < MAX_ITEM_COUNT + } + onMouseEnter={event => + handleHandleMouseEnter(event, i) + } + alertHandleId={alertHandleId} + draggable={i !== block.childrenIds.length - 1} + /> + + ); + })} {isOnDrag ? ReactDOM.createPortal(, window.document.body) 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 d3160d56f5..35426c73de 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 @@ -12,6 +12,7 @@ enum DragType { } const DRAG_STATE_CHANGE_EVENT_KEY = 'dragStateChange'; +const MAX_GRID_BLOCK_FLOOR = 3; export class DragDropManager { private _editor: Editor; private _enabled: boolean; @@ -231,6 +232,17 @@ export class DragDropManager { if (!(await this._canBeDrop(event))) { direction = BlockDropPlacement.none; } + if ( + direction === BlockDropPlacement.left || + direction === BlockDropPlacement.right + ) { + const path = await this._editor.getBlockPath(blockId); + const gridBlocks = path.filter(block => block.type === 'grid'); + // limit grid block floor counts + if (gridBlocks.length >= MAX_GRID_BLOCK_FLOOR) { + direction = BlockDropPlacement.none; + } + } this._setBlockDragDirection(direction); return direction; } diff --git a/libs/components/editor-core/src/editor/editor.ts b/libs/components/editor-core/src/editor/editor.ts index 73e7f6f7d5..8750a566c7 100644 --- a/libs/components/editor-core/src/editor/editor.ts +++ b/libs/components/editor-core/src/editor/editor.ts @@ -340,7 +340,20 @@ export class Editor implements Virgo { const rootBlockId = this.getRootBlockId(); const rootBlock = await this.getBlockById(rootBlockId); const blockList: Array = rootBlock ? [rootBlock] : []; - const children = (await rootBlock?.children()) || []; + return [...blockList, ...(await this.getOffspring(rootBlockId))]; + } + + /** + * + * get all offspring of block + * @param {string} id + * @return {*} + * @memberof Editor + */ + async getOffspring(id: string) { + const block = await this.getBlockById(id); + const blockList: Array = []; + const children = (await block?.children()) || []; for (const block of children) { if (!block) { continue; @@ -379,6 +392,20 @@ export class Editor implements Virgo { return lastBlock; } + async getBlockPath(id: string) { + const block = await this.getBlockById(id); + if (!block) { + return []; + } + const path = [block]; + let parent = await block.parent(); + while (parent) { + path.unshift(parent); + parent = await parent.parent(); + } + return path; + } + async getBlockByPoint(point: Point) { const blockList = await this.getBlockList();