Some checks failed
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 4s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Analyze (javascript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
191 lines
5.3 KiB
TypeScript
191 lines
5.3 KiB
TypeScript
import {
|
|
LayoutType,
|
|
type MindmapElementModel,
|
|
type MindmapNode,
|
|
type MindmapRoot,
|
|
} from '@blocksuite/affine-model';
|
|
import { Bound } from '@blocksuite/global/gfx';
|
|
import last from 'lodash-es/last';
|
|
|
|
import { NODE_HORIZONTAL_SPACING, NODE_VERTICAL_SPACING } from '../view/layout';
|
|
|
|
const isOnEdge = (node: MindmapNode, direction: 'tail' | 'head') => {
|
|
let current = node;
|
|
|
|
while (current) {
|
|
if (!current.parent) return true;
|
|
|
|
if (direction === 'tail' && last(current.parent.children) === current) {
|
|
current = current.parent;
|
|
} else if (direction === 'head' && current.parent.children[0] === current) {
|
|
current = current.parent;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const TAIL_RESPONSE_AREA = NODE_HORIZONTAL_SPACING;
|
|
|
|
const fillResponseArea = (
|
|
node: MindmapNode,
|
|
layoutType: LayoutType,
|
|
parent: MindmapNode | null
|
|
) => {
|
|
// root node
|
|
if (!parent) {
|
|
const rootElmBound = node.element.elementBound;
|
|
const width =
|
|
layoutType === LayoutType.BALANCE
|
|
? rootElmBound.w + TAIL_RESPONSE_AREA * 2
|
|
: rootElmBound.w + TAIL_RESPONSE_AREA;
|
|
|
|
node.responseArea = new Bound(
|
|
layoutType === LayoutType.BALANCE || layoutType === LayoutType.LEFT
|
|
? rootElmBound.x - TAIL_RESPONSE_AREA
|
|
: rootElmBound.x,
|
|
rootElmBound.y,
|
|
width,
|
|
rootElmBound.h
|
|
);
|
|
|
|
if (node.detail.collapsed) {
|
|
return;
|
|
}
|
|
|
|
if (layoutType === LayoutType.BALANCE) {
|
|
(node as MindmapRoot).right.forEach(child => {
|
|
fillResponseArea(child, LayoutType.RIGHT, node);
|
|
});
|
|
(node as MindmapRoot).left.forEach(child => {
|
|
fillResponseArea(child, LayoutType.LEFT, node);
|
|
});
|
|
} else {
|
|
node.children.forEach(child => {
|
|
fillResponseArea(child, layoutType, node);
|
|
});
|
|
}
|
|
return;
|
|
} else {
|
|
const nodeBound = node.element.elementBound;
|
|
const idx = parent.children.indexOf(node) ?? -1;
|
|
const isLast =
|
|
idx === (parent.children.length || -1) - 1 && isOnEdge(node, 'tail');
|
|
const isFirst = idx === 0 && isOnEdge(node, 'head');
|
|
const upperSpacing = isFirst
|
|
? NODE_VERTICAL_SPACING * 2
|
|
: NODE_VERTICAL_SPACING / 2;
|
|
const lowerSpacing = isLast
|
|
? NODE_VERTICAL_SPACING * 2
|
|
: NODE_VERTICAL_SPACING / 2;
|
|
|
|
const h = nodeBound.h + upperSpacing + lowerSpacing;
|
|
const w =
|
|
(layoutType === LayoutType.RIGHT
|
|
? node.element.x +
|
|
node.element.w -
|
|
(parent.element.x + parent.element.w)
|
|
: parent.element.x - node.element.x) + TAIL_RESPONSE_AREA;
|
|
|
|
node.responseArea = new Bound(
|
|
layoutType === LayoutType.RIGHT
|
|
? parent.element.x + parent.element.w
|
|
: parent.element.x - w,
|
|
node.element.y - upperSpacing,
|
|
w,
|
|
h
|
|
);
|
|
|
|
if (node.children.length > 0 && !node.detail.collapsed) {
|
|
let responseArea: Bound;
|
|
|
|
node.children.forEach(child => {
|
|
fillResponseArea(child, layoutType, node);
|
|
|
|
if (responseArea) {
|
|
responseArea = responseArea.unite(child.responseArea!);
|
|
} else {
|
|
responseArea = child.responseArea!;
|
|
}
|
|
});
|
|
|
|
node.responseArea.h = responseArea!.h;
|
|
node.responseArea.y = responseArea!.y;
|
|
}
|
|
}
|
|
};
|
|
|
|
export const balanceLeftRightResponseArea = (tree: MindmapRoot) => {
|
|
const leftTreeArea = tree.left.reduce((pre: Bound | null, node) => {
|
|
if (pre) {
|
|
return pre.unite(node.responseArea!);
|
|
}
|
|
return node.responseArea!;
|
|
}, null);
|
|
const rightTreeArea = tree.right.reduce((pre: Bound | null, node) => {
|
|
if (pre) {
|
|
return pre.unite(node.responseArea!);
|
|
}
|
|
return node.responseArea!;
|
|
}, null);
|
|
|
|
if (!leftTreeArea || !rightTreeArea) {
|
|
return;
|
|
}
|
|
|
|
// if the height of the left tree and right tree are not equal
|
|
// expand the response area of lower tree to match the height of the higher tree
|
|
if (leftTreeArea.h !== rightTreeArea.h) {
|
|
const isLeftHigher = leftTreeArea.h > rightTreeArea.h;
|
|
const upperBoundary = isLeftHigher ? leftTreeArea.y : rightTreeArea.y;
|
|
const bottomBoundary = isLeftHigher
|
|
? leftTreeArea.y + leftTreeArea.h
|
|
: rightTreeArea.y + rightTreeArea.h;
|
|
const targetChildren = isLeftHigher ? tree.right : tree.left;
|
|
|
|
const expandEdge = (children: MindmapNode[]) => {
|
|
const expand = (direction: 'up' | 'down') => {
|
|
const expandUpperEdge = direction === 'up';
|
|
const node = direction === 'up' ? children[0] : last(children)!;
|
|
|
|
if (!node) return;
|
|
|
|
if (node.responseArea) {
|
|
node.responseArea.h = expandUpperEdge
|
|
? node.responseArea.h + (node.responseArea.y - upperBoundary)
|
|
: node.responseArea.h +
|
|
(bottomBoundary - node.responseArea.y - node.responseArea.h);
|
|
expandUpperEdge && (node.responseArea.y = upperBoundary);
|
|
}
|
|
};
|
|
|
|
expand('up');
|
|
expand('down');
|
|
};
|
|
|
|
expandEdge(targetChildren);
|
|
}
|
|
};
|
|
|
|
export const calculateResponseArea = (mindmap: MindmapElementModel) => {
|
|
const layoutDir = mindmap.layoutType;
|
|
const tree = mindmap.tree;
|
|
|
|
switch (layoutDir) {
|
|
case LayoutType.RIGHT:
|
|
case LayoutType.LEFT:
|
|
{
|
|
fillResponseArea(tree, layoutDir, null);
|
|
}
|
|
break;
|
|
case LayoutType.BALANCE:
|
|
{
|
|
fillResponseArea(tree, LayoutType.BALANCE, null);
|
|
balanceLeftRightResponseArea(tree);
|
|
}
|
|
break;
|
|
}
|
|
};
|