feat: grid optimise
This commit is contained in:
@@ -12,6 +12,8 @@ type GridHandleProps = {
|
|||||||
blockId: string;
|
blockId: string;
|
||||||
enabledAddItem: boolean;
|
enabledAddItem: boolean;
|
||||||
draggable: boolean;
|
draggable: boolean;
|
||||||
|
alertHandleId: string;
|
||||||
|
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GridHandle: FC<GridHandleProps> = function ({
|
export const GridHandle: FC<GridHandleProps> = function ({
|
||||||
@@ -21,6 +23,8 @@ export const GridHandle: FC<GridHandleProps> = function ({
|
|||||||
onDrag,
|
onDrag,
|
||||||
onMouseDown,
|
onMouseDown,
|
||||||
draggable,
|
draggable,
|
||||||
|
alertHandleId,
|
||||||
|
onMouseEnter,
|
||||||
}) {
|
}) {
|
||||||
const [isMouseDown, setIsMouseDown] = useState<boolean>(false);
|
const [isMouseDown, setIsMouseDown] = useState<boolean>(false);
|
||||||
const handleMouseDown: React.MouseEventHandler<HTMLDivElement> = e => {
|
const handleMouseDown: React.MouseEventHandler<HTMLDivElement> = e => {
|
||||||
@@ -44,16 +48,17 @@ export const GridHandle: FC<GridHandleProps> = function ({
|
|||||||
editor.selectionManager.setActivatedNodeId(textBlock.id);
|
editor.selectionManager.setActivatedNodeId(textBlock.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMouseEnter: React.MouseEventHandler<HTMLDivElement> = e => {
|
||||||
|
onMouseEnter && onMouseEnter(e);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridHandleContainer
|
<GridHandleContainer
|
||||||
style={
|
isMouseDown={isMouseDown}
|
||||||
isMouseDown
|
|
||||||
? {
|
|
||||||
backgroundColor: '#3E6FDB',
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
|
onMouseEnter={handleMouseEnter}
|
||||||
|
isAlert={alertHandleId === blockId}
|
||||||
>
|
>
|
||||||
{enabledAddItem ? (
|
{enabledAddItem ? (
|
||||||
<AddGridHandle
|
<AddGridHandle
|
||||||
@@ -67,7 +72,10 @@ export const GridHandle: FC<GridHandleProps> = function ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const GridHandleContainer = styled('div')(({ theme }) => ({
|
const GridHandleContainer = styled('div')<{
|
||||||
|
isMouseDown: boolean;
|
||||||
|
isAlert: boolean;
|
||||||
|
}>(({ theme, isMouseDown, isAlert }) => ({
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
width: '10px',
|
width: '10px',
|
||||||
flexGrow: '0',
|
flexGrow: '0',
|
||||||
@@ -78,11 +86,17 @@ const GridHandleContainer = styled('div')(({ theme }) => ({
|
|||||||
borderRadius: '1px',
|
borderRadius: '1px',
|
||||||
backgroundClip: 'content-box',
|
backgroundClip: 'content-box',
|
||||||
' &:hover': {
|
' &:hover': {
|
||||||
backgroundColor: theme.affine.palette.primary,
|
backgroundColor: isAlert
|
||||||
|
? 'red !important'
|
||||||
|
: theme.affine.palette.primary,
|
||||||
[`.${GRID_ADD_HANDLE_NAME}`]: {
|
[`.${GRID_ADD_HANDLE_NAME}`]: {
|
||||||
display: 'block',
|
display: 'block',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...(isMouseDown &&
|
||||||
|
(isAlert
|
||||||
|
? { backgroundColor: 'red' }
|
||||||
|
: { backgroundColor: theme.affine.palette.primary })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const AddGridHandle = styled('div')(({ theme }) => ({
|
const AddGridHandle = styled('div')(({ theme }) => ({
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export const Grid: FC<CreateView> = function (props) {
|
|||||||
const gridItemCountRef = useRef<number>();
|
const gridItemCountRef = useRef<number>();
|
||||||
const originalLeftWidth = useRef<number>(GRID_ITEM_MIN_WIDTH);
|
const originalLeftWidth = useRef<number>(GRID_ITEM_MIN_WIDTH);
|
||||||
const originalRightWidth = useRef<number>(GRID_ITEM_MIN_WIDTH);
|
const originalRightWidth = useRef<number>(GRID_ITEM_MIN_WIDTH);
|
||||||
|
const [alertHandleId, setAlertHandleId] = useState<string>(null);
|
||||||
|
|
||||||
const getLeftRightGridItemDomByIndex = (index: number) => {
|
const getLeftRightGridItemDomByIndex = (index: number) => {
|
||||||
const gridItems = Array.from(gridContainerRef.current?.children).filter(
|
const gridItems = Array.from(gridContainerRef.current?.children).filter(
|
||||||
@@ -117,7 +118,7 @@ export const Grid: FC<CreateView> = function (props) {
|
|||||||
itemDom.style.width = width;
|
itemDom.style.width = width;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDragGrid = (e: MouseEvent, index: number) => {
|
const handleDragGrid = async (e: MouseEvent, index: number) => {
|
||||||
setIsOnDrag(true);
|
setIsOnDrag(true);
|
||||||
window.getSelection().removeAllRanges();
|
window.getSelection().removeAllRanges();
|
||||||
if (!isSetMouseUp.current) {
|
if (!isSetMouseUp.current) {
|
||||||
@@ -165,39 +166,47 @@ export const Grid: FC<CreateView> = function (props) {
|
|||||||
setItemWidth(leftGrid, newLeft);
|
setItemWidth(leftGrid, newLeft);
|
||||||
setItemWidth(rightGrid, newRight);
|
setItemWidth(rightGrid, newRight);
|
||||||
updateDbWidth(leftBlockId, newLeft, rightBlockId, newRight);
|
updateDbWidth(leftBlockId, newLeft, rightBlockId, newRight);
|
||||||
|
[leftBlockId, rightBlockId].forEach(async blockId => {
|
||||||
|
if (await checkGridItemHasOverflow(blockId)) {
|
||||||
|
setAlertHandleId(leftBlockId);
|
||||||
|
} else {
|
||||||
|
setAlertHandleId(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const children = (
|
const checkGridItemHasOverflow = async (blockId: string) => {
|
||||||
<>
|
let isOverflow = false;
|
||||||
{block.childrenIds.map((id, i) => {
|
const block = await editor.getBlockById(blockId);
|
||||||
return (
|
if (block) {
|
||||||
<GridItem
|
const blockDom = block.dom;
|
||||||
style={{
|
if (blockDom) {
|
||||||
transition: isOnDrag
|
block.dom.style.overflow = 'scroll';
|
||||||
? 'none'
|
if (block.dom.clientWidth !== block.dom.scrollWidth) {
|
||||||
: 'all 0.2s ease-in-out',
|
isOverflow = true;
|
||||||
}}
|
}
|
||||||
key={id}
|
blockDom.style.overflow = 'visible';
|
||||||
className={GRID_ITEM_CLASS_NAME}
|
}
|
||||||
>
|
}
|
||||||
<RenderBlock hasContainer={false} blockId={id} />
|
return isOverflow;
|
||||||
<GridHandle
|
};
|
||||||
onDrag={event => handleDragGrid(event, i)}
|
|
||||||
editor={editor}
|
const handleHandleMouseEnter = (
|
||||||
onMouseDown={event => handleMouseDown(event, i)}
|
e: React.MouseEvent<HTMLDivElement>,
|
||||||
blockId={id}
|
index: number
|
||||||
enabledAddItem={
|
) => {
|
||||||
block.childrenIds.length < MAX_ITEM_COUNT
|
const leftBlockId = block.childrenIds[index];
|
||||||
}
|
const rightBlockId = block.childrenIds[index + 1];
|
||||||
draggable={i !== block.childrenIds.length - 1}
|
[leftBlockId, rightBlockId].forEach(async blockId => {
|
||||||
/>
|
if (await checkGridItemHasOverflow(blockId)) {
|
||||||
</GridItem>
|
setAlertHandleId(leftBlockId);
|
||||||
);
|
} else {
|
||||||
})}
|
setAlertHandleId(null);
|
||||||
</>
|
}
|
||||||
);
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -206,7 +215,35 @@ export const Grid: FC<CreateView> = function (props) {
|
|||||||
ref={gridContainerRef}
|
ref={gridContainerRef}
|
||||||
isOnDrag={isOnDrag}
|
isOnDrag={isOnDrag}
|
||||||
>
|
>
|
||||||
{children}
|
{block.childrenIds.map((id, i) => {
|
||||||
|
return (
|
||||||
|
<GridItem
|
||||||
|
style={{
|
||||||
|
transition: isOnDrag
|
||||||
|
? 'none'
|
||||||
|
: 'all 0.2s ease-in-out',
|
||||||
|
}}
|
||||||
|
key={id}
|
||||||
|
className={GRID_ITEM_CLASS_NAME}
|
||||||
|
>
|
||||||
|
<RenderBlock hasContainer={false} blockId={id} />
|
||||||
|
<GridHandle
|
||||||
|
onDrag={event => 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}
|
||||||
|
/>
|
||||||
|
</GridItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
{isOnDrag
|
{isOnDrag
|
||||||
? ReactDOM.createPortal(<GridMask />, window.document.body)
|
? ReactDOM.createPortal(<GridMask />, window.document.body)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ enum DragType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DRAG_STATE_CHANGE_EVENT_KEY = 'dragStateChange';
|
const DRAG_STATE_CHANGE_EVENT_KEY = 'dragStateChange';
|
||||||
|
const MAX_GRID_BLOCK_FLOOR = 3;
|
||||||
export class DragDropManager {
|
export class DragDropManager {
|
||||||
private _editor: Editor;
|
private _editor: Editor;
|
||||||
private _enabled: boolean;
|
private _enabled: boolean;
|
||||||
@@ -231,6 +232,17 @@ export class DragDropManager {
|
|||||||
if (!(await this._canBeDrop(event))) {
|
if (!(await this._canBeDrop(event))) {
|
||||||
direction = BlockDropPlacement.none;
|
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);
|
this._setBlockDragDirection(direction);
|
||||||
return direction;
|
return direction;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -340,7 +340,20 @@ export class Editor implements Virgo {
|
|||||||
const rootBlockId = this.getRootBlockId();
|
const rootBlockId = this.getRootBlockId();
|
||||||
const rootBlock = await this.getBlockById(rootBlockId);
|
const rootBlock = await this.getBlockById(rootBlockId);
|
||||||
const blockList: Array<AsyncBlock> = rootBlock ? [rootBlock] : [];
|
const blockList: Array<AsyncBlock> = 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<AsyncBlock> = [];
|
||||||
|
const children = (await block?.children()) || [];
|
||||||
for (const block of children) {
|
for (const block of children) {
|
||||||
if (!block) {
|
if (!block) {
|
||||||
continue;
|
continue;
|
||||||
@@ -379,6 +392,20 @@ export class Editor implements Virgo {
|
|||||||
return lastBlock;
|
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) {
|
async getBlockByPoint(point: Point) {
|
||||||
const blockList = await this.getBlockList();
|
const blockList = await this.getBlockList();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user