mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-02 06:39:46 +08:00
refactor(editor): extract root block (#10356)
Closes: [BS-2207](https://linear.app/affine-design/issue/BS-2207/move-root-block-to-affineblock-root)
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import type { BlockComponent, EditorHost } from '@blocksuite/block-std';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
|
||||
// Run the callback until a model's element updated.
|
||||
// Please notice that the callback will be called **once the element itself is ready**.
|
||||
// The children may be not updated.
|
||||
// If you want to wait for the text elements,
|
||||
// please use `onModelTextUpdated`.
|
||||
export async function onModelElementUpdated(
|
||||
editorHost: EditorHost,
|
||||
model: BlockModel,
|
||||
callback: (block: BlockComponent) => void
|
||||
) {
|
||||
const page = model.doc;
|
||||
if (!page.root) return;
|
||||
|
||||
const rootComponent = editorHost.view.getBlock(page.root.id);
|
||||
if (!rootComponent) return;
|
||||
await rootComponent.updateComplete;
|
||||
|
||||
const element = editorHost.view.getBlock(model.id);
|
||||
if (element) callback(element);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './callback';
|
||||
export * from './types';
|
||||
@@ -0,0 +1,18 @@
|
||||
export function formatDate(date: Date) {
|
||||
// yyyy-mm-dd
|
||||
const year = date.getFullYear();
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const strTime = `${year}-${month}-${day}`;
|
||||
return strTime;
|
||||
}
|
||||
|
||||
export function formatTime(date: Date) {
|
||||
// mm-dd hh:mm
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
const strTime = `${month}-${day} ${hours}:${minutes}`;
|
||||
return strTime;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { clamp } from '@blocksuite/affine-shared/utils';
|
||||
|
||||
type CollisionBox = {
|
||||
/**
|
||||
* The point that the objRect is positioned to.
|
||||
*/
|
||||
positioningPoint: { x: number; y: number };
|
||||
/**
|
||||
* The boundary rect of the obj that is being positioned.
|
||||
*/
|
||||
objRect?: { height: number; width: number };
|
||||
/**
|
||||
* The boundary rect of the container that the obj is in.
|
||||
*/
|
||||
boundaryRect?: DOMRect;
|
||||
offsetX?: number;
|
||||
offsetY?: number;
|
||||
edgeGap?: number;
|
||||
};
|
||||
|
||||
export function calcSafeCoordinate({
|
||||
positioningPoint,
|
||||
objRect = { width: 0, height: 0 },
|
||||
boundaryRect = document.body.getBoundingClientRect(),
|
||||
offsetX = 0,
|
||||
offsetY = 0,
|
||||
edgeGap = 20,
|
||||
}: CollisionBox) {
|
||||
const safeX = clamp(
|
||||
positioningPoint.x + offsetX,
|
||||
edgeGap,
|
||||
boundaryRect.width - objRect.width - edgeGap
|
||||
);
|
||||
const y = positioningPoint.y + offsetY;
|
||||
// Not use clamp for y coordinate to avoid the quick bar always showing after scrolling
|
||||
// const safeY = clamp(
|
||||
// positioningPoint.y + offsetY,
|
||||
// edgeGap,
|
||||
// boundaryRect.height - objRect.height - edgeGap
|
||||
// );
|
||||
return {
|
||||
x: safeX,
|
||||
y,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to compare the space available
|
||||
* at the top and bottom of an element within a container.
|
||||
*
|
||||
* Please give preference to {@link getPopperPosition}
|
||||
*/
|
||||
export function compareTopAndBottomSpace(
|
||||
obj: { getBoundingClientRect: () => DOMRect },
|
||||
container = document.body,
|
||||
gap = 20
|
||||
) {
|
||||
const objRect = obj.getBoundingClientRect();
|
||||
const spaceRect = container.getBoundingClientRect();
|
||||
const topSpace = objRect.top - spaceRect.top;
|
||||
const bottomSpace = spaceRect.bottom - objRect.bottom;
|
||||
const topOrBottom: 'top' | 'bottom' =
|
||||
topSpace > bottomSpace ? 'top' : 'bottom';
|
||||
return {
|
||||
placement: topOrBottom,
|
||||
// the height is the available space.
|
||||
height: (topOrBottom === 'top' ? topSpace : bottomSpace) - gap,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position of the popper element with flip.
|
||||
* return null if the reference rect is all zero.
|
||||
*/
|
||||
export function getPopperPosition(
|
||||
popper: {
|
||||
getBoundingClientRect: () => DOMRect;
|
||||
},
|
||||
reference: {
|
||||
getBoundingClientRect: () => DOMRect;
|
||||
},
|
||||
{ gap = 12, offsetY = 5 }: { gap?: number; offsetY?: number } = {}
|
||||
) {
|
||||
if (!popper) {
|
||||
// foolproof, someone may use element with non-null assertion
|
||||
console.warn(
|
||||
'The popper element is not exist. Popper position maybe incorrect'
|
||||
);
|
||||
}
|
||||
const { placement, height } = compareTopAndBottomSpace(
|
||||
reference,
|
||||
document.body,
|
||||
gap + offsetY
|
||||
);
|
||||
|
||||
const referenceRect = reference.getBoundingClientRect();
|
||||
if (
|
||||
referenceRect.x === 0 &&
|
||||
referenceRect.y === 0 &&
|
||||
referenceRect.width === 0 &&
|
||||
referenceRect.height === 0
|
||||
)
|
||||
return null;
|
||||
|
||||
const positioningPoint = {
|
||||
x: referenceRect.x,
|
||||
y: referenceRect.y + (placement === 'bottom' ? referenceRect.height : 0),
|
||||
};
|
||||
|
||||
// TODO maybe use the editor container as the boundary rect to avoid the format bar being covered by other elements
|
||||
const boundaryRect = document.body.getBoundingClientRect();
|
||||
// Note: the popperRect.height maybe incorrect
|
||||
// because we are calculated its correct height
|
||||
const popperRect = popper?.getBoundingClientRect();
|
||||
|
||||
const safeCoordinate = calcSafeCoordinate({
|
||||
positioningPoint,
|
||||
objRect: popperRect,
|
||||
boundaryRect,
|
||||
offsetY: placement === 'bottom' ? offsetY : -offsetY,
|
||||
});
|
||||
|
||||
return {
|
||||
placement,
|
||||
/**
|
||||
* The height is the available space height.
|
||||
*
|
||||
* Note: it's a max height, not the real height,
|
||||
* because sometimes the popper's height is smaller than the available space.
|
||||
*/
|
||||
height,
|
||||
x: `${safeCoordinate.x}px`,
|
||||
y:
|
||||
placement === 'bottom'
|
||||
? `${safeCoordinate.y}px`
|
||||
: // We need to use `calc(-100%)` since the height of popper maybe incorrect
|
||||
`calc(${safeCoordinate.y}px - 100%)`,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { RootBlockComponent } from '../types.js';
|
||||
|
||||
export function getClosestRootBlockComponent(
|
||||
el: HTMLElement
|
||||
): RootBlockComponent | null {
|
||||
return el.closest('affine-edgeless-root, affine-page-root');
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { BookmarkBlockComponent } from '@blocksuite/affine-block-bookmark';
|
||||
import {
|
||||
EmbedFigmaBlockComponent,
|
||||
EmbedGithubBlockComponent,
|
||||
EmbedHtmlBlockComponent,
|
||||
EmbedLinkedDocBlockComponent,
|
||||
EmbedLoomBlockComponent,
|
||||
EmbedSyncedDocBlockComponent,
|
||||
EmbedYoutubeBlockComponent,
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import type { BlockComponent } from '@blocksuite/block-std';
|
||||
|
||||
export type ExternalEmbedBlockComponent =
|
||||
| BookmarkBlockComponent
|
||||
| EmbedFigmaBlockComponent
|
||||
| EmbedGithubBlockComponent
|
||||
| EmbedLoomBlockComponent
|
||||
| EmbedYoutubeBlockComponent;
|
||||
|
||||
export type InternalEmbedBlockComponent =
|
||||
| EmbedLinkedDocBlockComponent
|
||||
| EmbedSyncedDocBlockComponent;
|
||||
|
||||
export type LinkableEmbedBlockComponent =
|
||||
| ExternalEmbedBlockComponent
|
||||
| InternalEmbedBlockComponent;
|
||||
|
||||
export type BuiltInEmbedBlockComponent =
|
||||
| LinkableEmbedBlockComponent
|
||||
| EmbedHtmlBlockComponent;
|
||||
|
||||
export function isEmbedCardBlockComponent(
|
||||
block: BlockComponent
|
||||
): block is BuiltInEmbedBlockComponent {
|
||||
return (
|
||||
block instanceof BookmarkBlockComponent ||
|
||||
block instanceof EmbedFigmaBlockComponent ||
|
||||
block instanceof EmbedGithubBlockComponent ||
|
||||
block instanceof EmbedHtmlBlockComponent ||
|
||||
block instanceof EmbedLoomBlockComponent ||
|
||||
block instanceof EmbedYoutubeBlockComponent ||
|
||||
block instanceof EmbedLinkedDocBlockComponent ||
|
||||
block instanceof EmbedSyncedDocBlockComponent
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user