mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-25 06:18:45 +08:00
chore: merge blocksuite source code (#9213)
This commit is contained in:
@@ -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';
|
||||
Reference in New Issue
Block a user