chore: merge blocksuite source code (#9213)

This commit is contained in:
Mirone
2024-12-20 15:38:06 +08:00
committed by GitHub
parent 2c9ef916f4
commit 30200ff86d
2031 changed files with 238888 additions and 229 deletions
@@ -0,0 +1,23 @@
import type { BlockSelection, Command } from '@blocksuite/block-std';
export const getBlockSelectionsCommand: Command<
never,
'currentBlockSelections'
> = (ctx, next) => {
const currentBlockSelections = ctx.std.selection.filter('block');
if (currentBlockSelections.length === 0) return;
next({ currentBlockSelections });
};
declare global {
namespace BlockSuite {
interface CommandContext {
currentBlockSelections?: BlockSelection[];
}
interface Commands {
getBlockSelections: typeof getBlockSelectionsCommand;
}
}
}
@@ -0,0 +1,25 @@
import type { Command } from '@blocksuite/block-std';
import type { ImageSelection } from '../../selection/index.js';
export const getImageSelectionsCommand: Command<
never,
'currentImageSelections'
> = (ctx, next) => {
const currentImageSelections = ctx.std.selection.filter('image');
if (currentImageSelections.length === 0) return;
next({ currentImageSelections });
};
declare global {
namespace BlockSuite {
interface CommandContext {
currentImageSelections?: ImageSelection[];
}
interface Commands {
getImageSelections: typeof getImageSelectionsCommand;
}
}
}
@@ -0,0 +1,200 @@
import type {
BlockSelection,
Command,
TextSelection,
} from '@blocksuite/block-std';
import { getViewportElement } from '../../utils/index.js';
export interface SelectionRect {
width: number;
height: number;
top: number;
left: number;
/**
* The block id that the rect is in. Only available for block selections.
*/
blockId?: string;
}
export const getSelectionRectsCommand: Command<
'currentTextSelection' | 'currentBlockSelections',
'selectionRects',
{
textSelection?: TextSelection;
blockSelections?: BlockSelection[];
}
> = (ctx, next) => {
let textSelection;
let blockSelections;
// priority parameters
if (ctx.textSelection) {
textSelection = ctx.textSelection;
} else if (ctx.blockSelections) {
blockSelections = ctx.blockSelections;
} else if (ctx.currentTextSelection) {
textSelection = ctx.currentTextSelection;
} else if (ctx.currentBlockSelections) {
blockSelections = ctx.currentBlockSelections;
} else {
console.error(
'No selection provided, may forgot to call getTextSelection or getBlockSelections or provide the selection directly.'
);
return;
}
const { std } = ctx;
const container = getViewportElement(std.host);
const containerRect = container?.getBoundingClientRect();
if (textSelection) {
const range = std.range.textSelectionToRange(textSelection);
if (range) {
const nativeRects = Array.from(range.getClientRects());
const rectsWithoutFiltered = nativeRects
.map(rect => ({
width: rect.right - rect.left,
height: rect.bottom - rect.top,
top:
rect.top - (containerRect?.top ?? 0) + (container?.scrollTop ?? 0),
left:
rect.left -
(containerRect?.left ?? 0) +
(container?.scrollLeft ?? 0),
}))
.filter(rect => rect.width > 0 && rect.height > 0);
return next({
selectionRects: filterCoveringRects(rectsWithoutFiltered),
});
}
} else if (blockSelections && blockSelections.length > 0) {
const result = blockSelections
.map(blockSelection => {
const block = std.view.getBlock(blockSelection.blockId);
if (!block) return null;
const rect = block.getBoundingClientRect();
return {
width: rect.width,
height: rect.height,
top:
rect.top - (containerRect?.top ?? 0) + (container?.scrollTop ?? 0),
left:
rect.left -
(containerRect?.left ?? 0) +
(container?.scrollLeft ?? 0),
blockId: blockSelection.blockId,
};
})
.filter(rect => !!rect);
return next({ selectionRects: result });
}
return;
};
declare global {
namespace BlockSuite {
interface CommandContext {
selectionRects?: SelectionRect[];
}
interface Commands {
/**
* Get the selection rects of the current selection or given selections.
*
* @chain may be `getTextSelection`, `getBlockSelections`, or nothing.
* @param textSelection The provided text selection.
* @param blockSelections The provided block selections. If `textSelection` is provided, this will be ignored.
* @returns The selection rects.
*/
getSelectionRects: typeof getSelectionRectsCommand;
}
}
}
function covers(rect1: SelectionRect, rect2: SelectionRect): boolean {
return (
rect1.left <= rect2.left &&
rect1.top <= rect2.top &&
rect1.left + rect1.width >= rect2.left + rect2.width &&
rect1.top + rect1.height >= rect2.top + rect2.height
);
}
function intersects(rect1: SelectionRect, rect2: SelectionRect): boolean {
return (
rect1.left <= rect2.left + rect2.width &&
rect1.left + rect1.width >= rect2.left &&
rect1.top <= rect2.top + rect2.height &&
rect1.top + rect1.height >= rect2.top
);
}
function merge(rect1: SelectionRect, rect2: SelectionRect): SelectionRect {
const left = Math.min(rect1.left, rect2.left);
const top = Math.min(rect1.top, rect2.top);
const right = Math.max(rect1.left + rect1.width, rect2.left + rect2.width);
const bottom = Math.max(rect1.top + rect1.height, rect2.top + rect2.height);
return {
width: right - left,
height: bottom - top,
top,
left,
};
}
export function filterCoveringRects(rects: SelectionRect[]): SelectionRect[] {
let mergedRects: SelectionRect[] = [];
let hasChanges: boolean;
do {
hasChanges = false;
const newMergedRects: SelectionRect[] = [...mergedRects];
for (const rect of rects) {
let merged = false;
for (let i = 0; i < newMergedRects.length; i++) {
if (covers(newMergedRects[i], rect)) {
merged = true;
break;
} else if (intersects(newMergedRects[i], rect)) {
newMergedRects[i] = merge(newMergedRects[i], rect);
merged = true;
hasChanges = true;
break;
}
}
if (!merged) {
newMergedRects.push(rect);
}
}
if (!hasChanges) {
for (let i = 0; i < newMergedRects.length; i++) {
for (let j = i + 1; j < newMergedRects.length; j++) {
if (intersects(newMergedRects[i], newMergedRects[j])) {
newMergedRects[i] = merge(newMergedRects[i], newMergedRects[j]);
newMergedRects.splice(j, 1);
hasChanges = true;
break;
}
}
}
}
mergedRects = newMergedRects;
} while (hasChanges);
return mergedRects;
}
@@ -0,0 +1,23 @@
import type { Command, TextSelection } from '@blocksuite/block-std';
export const getTextSelectionCommand: Command<never, 'currentTextSelection'> = (
ctx,
next
) => {
const currentTextSelection = ctx.std.selection.find('text');
if (!currentTextSelection) return;
next({ currentTextSelection });
};
declare global {
namespace BlockSuite {
interface CommandContext {
currentTextSelection?: TextSelection;
}
interface Commands {
getTextSelection: typeof getTextSelectionCommand;
}
}
}
@@ -0,0 +1,7 @@
export { getBlockSelectionsCommand } from './get-block-selections.js';
export { getImageSelectionsCommand } from './get-image-selections.js';
export {
getSelectionRectsCommand,
type SelectionRect,
} from './get-selection-rects.js';
export { getTextSelectionCommand } from './get-text-selection.js';