mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-20 03:26:47 +08:00
chore: merge blocksuite source code (#9213)
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
import {
|
||||
getClosestBlockComponentByElement,
|
||||
getRectByBlockComponent,
|
||||
matchFlavours,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockComponent } from '@blocksuite/block-std';
|
||||
import { type Point, Rect } from '@blocksuite/global/utils';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
|
||||
import type { EditingState } from '../types.js';
|
||||
import { DropFlags, getDropRectByPoint } from './query.js';
|
||||
|
||||
/**
|
||||
* A dropping type.
|
||||
*/
|
||||
export type DroppingType = 'none' | 'before' | 'after' | 'database';
|
||||
|
||||
export type DropResult = {
|
||||
type: DroppingType;
|
||||
rect: Rect;
|
||||
modelState: EditingState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the drop target.
|
||||
*/
|
||||
export function calcDropTarget(
|
||||
point: Point,
|
||||
model: BlockModel,
|
||||
element: Element,
|
||||
draggingElements: BlockComponent[] = [],
|
||||
scale: number = 1,
|
||||
flavour: string | null = null // for block-hub
|
||||
): DropResult | null {
|
||||
const schema = model.doc.getSchemaByFlavour('affine:database');
|
||||
const children = schema?.model.children ?? [];
|
||||
|
||||
let shouldAppendToDatabase = true;
|
||||
|
||||
if (children.length) {
|
||||
if (draggingElements.length) {
|
||||
shouldAppendToDatabase = draggingElements
|
||||
.map(el => el.model)
|
||||
.every(m => children.includes(m.flavour));
|
||||
} else if (flavour) {
|
||||
shouldAppendToDatabase = children.includes(flavour);
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldAppendToDatabase && !matchFlavours(model, ['affine:database'])) {
|
||||
const databaseBlockComponent = element.closest('affine-database');
|
||||
if (databaseBlockComponent) {
|
||||
element = databaseBlockComponent;
|
||||
model = databaseBlockComponent.model;
|
||||
}
|
||||
}
|
||||
|
||||
let type: DroppingType = 'none';
|
||||
const height = 3 * scale;
|
||||
const { rect: domRect, flag } = getDropRectByPoint(point, model, element);
|
||||
|
||||
if (flag === DropFlags.EmptyDatabase) {
|
||||
// empty database
|
||||
const rect = Rect.fromDOMRect(domRect);
|
||||
rect.top -= height / 2;
|
||||
rect.height = height;
|
||||
type = 'database';
|
||||
|
||||
return {
|
||||
type,
|
||||
rect,
|
||||
modelState: {
|
||||
model,
|
||||
rect: domRect,
|
||||
element: element as BlockComponent,
|
||||
},
|
||||
};
|
||||
} else if (flag === DropFlags.Database) {
|
||||
// not empty database
|
||||
const distanceToTop = Math.abs(domRect.top - point.y);
|
||||
const distanceToBottom = Math.abs(domRect.bottom - point.y);
|
||||
const before = distanceToTop < distanceToBottom;
|
||||
type = before ? 'before' : 'after';
|
||||
|
||||
return {
|
||||
type,
|
||||
rect: Rect.fromLWTH(
|
||||
domRect.left,
|
||||
domRect.width,
|
||||
(before ? domRect.top - 1 : domRect.bottom) - height / 2,
|
||||
height
|
||||
),
|
||||
modelState: {
|
||||
model,
|
||||
rect: domRect,
|
||||
element: element as BlockComponent,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const distanceToTop = Math.abs(domRect.top - point.y);
|
||||
const distanceToBottom = Math.abs(domRect.bottom - point.y);
|
||||
const before = distanceToTop < distanceToBottom;
|
||||
|
||||
type = before ? 'before' : 'after';
|
||||
let offsetY = 4;
|
||||
|
||||
if (type === 'before') {
|
||||
// before
|
||||
let prev;
|
||||
let prevRect;
|
||||
|
||||
prev = element.previousElementSibling;
|
||||
if (prev) {
|
||||
if (
|
||||
draggingElements.length &&
|
||||
prev === draggingElements[draggingElements.length - 1]
|
||||
) {
|
||||
type = 'none';
|
||||
} else {
|
||||
prevRect = getRectByBlockComponent(prev);
|
||||
}
|
||||
} else {
|
||||
prev = element.parentElement?.previousElementSibling;
|
||||
if (prev) {
|
||||
prevRect = prev.getBoundingClientRect();
|
||||
}
|
||||
}
|
||||
|
||||
if (prevRect) {
|
||||
offsetY = (domRect.top - prevRect.bottom) / 2;
|
||||
}
|
||||
} else {
|
||||
// after
|
||||
let next;
|
||||
let nextRect;
|
||||
|
||||
next = element.nextElementSibling;
|
||||
if (next) {
|
||||
if (draggingElements.length && next === draggingElements[0]) {
|
||||
type = 'none';
|
||||
next = null;
|
||||
}
|
||||
} else {
|
||||
next = getClosestBlockComponentByElement(
|
||||
element.parentElement
|
||||
)?.nextElementSibling;
|
||||
}
|
||||
|
||||
if (next) {
|
||||
nextRect = getRectByBlockComponent(next);
|
||||
offsetY = (nextRect.top - domRect.bottom) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'none') return null;
|
||||
|
||||
let top = domRect.top;
|
||||
if (type === 'before') {
|
||||
top -= offsetY;
|
||||
} else {
|
||||
top += domRect.height + offsetY;
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
rect: Rect.fromLWTH(domRect.left, domRect.width, top - height / 2, height),
|
||||
modelState: {
|
||||
model,
|
||||
rect: domRect,
|
||||
element: element as BlockComponent,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Compat with SSR
|
||||
export * from '../types.js';
|
||||
export * from './drag-and-drop.js';
|
||||
export * from './query.js';
|
||||
export {
|
||||
createButtonPopper,
|
||||
getBlockProps,
|
||||
getImageFilesFromLocal,
|
||||
isMiddleButtonPressed,
|
||||
isRightButtonPressed,
|
||||
isValidUrl,
|
||||
matchFlavours,
|
||||
on,
|
||||
once,
|
||||
openFileOrFiles,
|
||||
requestThrottledConnectedFrame,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
@@ -0,0 +1,211 @@
|
||||
import {
|
||||
getRectByBlockComponent,
|
||||
matchFlavours,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { BLOCK_ID_ATTR, type EditorHost } from '@blocksuite/block-std';
|
||||
import type { Point } from '@blocksuite/global/utils';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
|
||||
import type { RootBlockComponent } from '../../index.js';
|
||||
|
||||
const ATTR_SELECTOR = `[${BLOCK_ID_ATTR}]`;
|
||||
|
||||
/**
|
||||
* This function is used to build model's "normal" block path.
|
||||
* If this function does not meet your needs, you may need to build path manually to satisfy your needs.
|
||||
* You should not modify this function.
|
||||
*/
|
||||
export function buildPath(model: BlockModel | null): string[] {
|
||||
const path: string[] = [];
|
||||
let current = model;
|
||||
while (current) {
|
||||
path.unshift(current.id);
|
||||
current = current.doc.getParent(current);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
export function getRootByEditorHost(
|
||||
editorHost: EditorHost
|
||||
): RootBlockComponent | null {
|
||||
return (
|
||||
getPageRootByEditorHost(editorHost) ??
|
||||
getEdgelessRootByEditorHost(editorHost)
|
||||
);
|
||||
}
|
||||
|
||||
/** If it's not in the page mode, it will return `null` directly */
|
||||
export function getPageRootByEditorHost(editorHost: EditorHost) {
|
||||
return editorHost.querySelector('affine-page-root');
|
||||
}
|
||||
|
||||
/** If it's not in the edgeless mode, it will return `null` directly */
|
||||
export function getEdgelessRootByEditorHost(editorHost: EditorHost) {
|
||||
return editorHost.querySelector('affine-edgeless-root');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get block component by model.
|
||||
* Note that this function is used for compatibility only, and may be removed in the future.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export function getBlockComponentByModel(
|
||||
editorHost: EditorHost,
|
||||
model: BlockModel | null
|
||||
) {
|
||||
if (!model) return null;
|
||||
return editorHost.view.getBlock(model.id);
|
||||
}
|
||||
|
||||
function isEdgelessChildNote({ classList }: Element) {
|
||||
return classList.contains('note-background');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hovering note with given a point in edgeless mode.
|
||||
*/
|
||||
export function getHoveringNote(point: Point) {
|
||||
return (
|
||||
document.elementsFromPoint(point.x, point.y).find(isEdgelessChildNote) ||
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the table of the database.
|
||||
*/
|
||||
function getDatabaseBlockTableElement(element: Element) {
|
||||
return element.querySelector('.affine-database-block-table');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the column header of the database.
|
||||
*/
|
||||
function getDatabaseBlockColumnHeaderElement(element: Element) {
|
||||
return element.querySelector('.affine-database-column-header');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rows of the database.
|
||||
*/
|
||||
function getDatabaseBlockRowsElement(element: Element) {
|
||||
return element.querySelector('.affine-database-block-rows');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flag for the drop target.
|
||||
*/
|
||||
export enum DropFlags {
|
||||
Normal,
|
||||
Database,
|
||||
EmptyDatabase,
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the drop rect by block and point.
|
||||
*/
|
||||
export function getDropRectByPoint(
|
||||
point: Point,
|
||||
model: BlockModel,
|
||||
element: Element
|
||||
): {
|
||||
rect: DOMRect;
|
||||
flag: DropFlags;
|
||||
} {
|
||||
const result = {
|
||||
rect: getRectByBlockComponent(element),
|
||||
flag: DropFlags.Normal,
|
||||
};
|
||||
|
||||
const isDatabase = matchFlavours(model, ['affine:database']);
|
||||
|
||||
if (isDatabase) {
|
||||
const table = getDatabaseBlockTableElement(element);
|
||||
if (!table) {
|
||||
return result;
|
||||
}
|
||||
let bounds = table.getBoundingClientRect();
|
||||
if (model.isEmpty.value) {
|
||||
result.flag = DropFlags.EmptyDatabase;
|
||||
|
||||
if (point.y < bounds.top) return result;
|
||||
|
||||
const header = getDatabaseBlockColumnHeaderElement(element);
|
||||
assertExists(header);
|
||||
bounds = header.getBoundingClientRect();
|
||||
result.rect = new DOMRect(
|
||||
result.rect.left,
|
||||
bounds.bottom,
|
||||
result.rect.width,
|
||||
1
|
||||
);
|
||||
} else {
|
||||
result.flag = DropFlags.Database;
|
||||
const rows = getDatabaseBlockRowsElement(element);
|
||||
assertExists(rows);
|
||||
const rowsBounds = rows.getBoundingClientRect();
|
||||
|
||||
if (point.y < rowsBounds.top || point.y > rowsBounds.bottom)
|
||||
return result;
|
||||
|
||||
const elements = document.elementsFromPoint(point.x, point.y);
|
||||
const len = elements.length;
|
||||
let e;
|
||||
let i = 0;
|
||||
for (; i < len; i++) {
|
||||
e = elements[i];
|
||||
|
||||
if (e.classList.contains('affine-database-block-row-cell-content')) {
|
||||
result.rect = getCellRect(e, bounds);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (e.classList.contains('affine-database-block-row')) {
|
||||
e = e.querySelector(ATTR_SELECTOR);
|
||||
assertExists(e);
|
||||
result.rect = getCellRect(e, bounds);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const parent = element.parentElement;
|
||||
if (parent?.classList.contains('affine-database-block-row-cell-content')) {
|
||||
result.flag = DropFlags.Database;
|
||||
result.rect = getCellRect(parent);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getCellRect(element: Element, bounds?: DOMRect) {
|
||||
if (!bounds) {
|
||||
const table = element.closest('.affine-database-block-table');
|
||||
assertExists(table);
|
||||
bounds = table.getBoundingClientRect();
|
||||
}
|
||||
// affine-database-block-row-cell
|
||||
const col = element.parentElement;
|
||||
assertExists(col);
|
||||
// affine-database-block-row
|
||||
const row = col.parentElement;
|
||||
assertExists(row);
|
||||
const colRect = col.getBoundingClientRect();
|
||||
return new DOMRect(
|
||||
bounds.left,
|
||||
colRect.top,
|
||||
colRect.right - bounds.left,
|
||||
colRect.height
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the element has class name in the class list.
|
||||
*/
|
||||
export function hasClassNameInList(element: Element, classList: string[]) {
|
||||
return classList.some(className => element.classList.contains(className));
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
import type { FrameBlockModel, NoteBlockModel } from '@blocksuite/affine-model';
|
||||
import { NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import {
|
||||
DocModeProvider,
|
||||
NotificationProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { getBlockProps, matchFlavours } from '@blocksuite/affine-shared/utils';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import {
|
||||
type BlockModel,
|
||||
type BlockSnapshot,
|
||||
type Doc,
|
||||
type DraftModel,
|
||||
Slice,
|
||||
} from '@blocksuite/store';
|
||||
|
||||
import { GfxBlockModel } from '../../root-block/edgeless/block-model.js';
|
||||
import {
|
||||
getElementProps,
|
||||
mapFrameIds,
|
||||
sortEdgelessElements,
|
||||
} from '../../root-block/edgeless/utils/clone-utils.js';
|
||||
import {
|
||||
isFrameBlock,
|
||||
isNoteBlock,
|
||||
} from '../../root-block/edgeless/utils/query.js';
|
||||
import { getSurfaceBlock } from '../../surface-ref-block/utils.js';
|
||||
|
||||
export function promptDocTitle(host: EditorHost, autofill?: string) {
|
||||
const notification = host.std.getOptional(NotificationProvider);
|
||||
if (!notification) return Promise.resolve(undefined);
|
||||
|
||||
return notification.prompt({
|
||||
title: 'Create linked doc',
|
||||
message: 'Enter a title for the new doc.',
|
||||
placeholder: 'Untitled',
|
||||
autofill,
|
||||
confirmText: 'Confirm',
|
||||
cancelText: 'Cancel',
|
||||
});
|
||||
}
|
||||
|
||||
export function getTitleFromSelectedModels(selectedModels: DraftModel[]) {
|
||||
const firstBlock = selectedModels[0];
|
||||
if (
|
||||
matchFlavours(firstBlock, ['affine:paragraph']) &&
|
||||
firstBlock.type.startsWith('h')
|
||||
) {
|
||||
return firstBlock.text.toString();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function notifyDocCreated(host: EditorHost, doc: Doc) {
|
||||
const notification = host.std.getOptional(NotificationProvider);
|
||||
if (!notification) return;
|
||||
|
||||
const abortController = new AbortController();
|
||||
const clear = () => {
|
||||
doc.history.off('stack-item-added', addHandler);
|
||||
doc.history.off('stack-item-popped', popHandler);
|
||||
disposable.dispose();
|
||||
};
|
||||
const closeNotify = () => {
|
||||
abortController.abort();
|
||||
clear();
|
||||
};
|
||||
|
||||
// edit or undo or switch doc, close notify toast
|
||||
const addHandler = doc.history.on('stack-item-added', closeNotify);
|
||||
const popHandler = doc.history.on('stack-item-popped', closeNotify);
|
||||
const disposable = host.slots.unmounted.on(closeNotify);
|
||||
|
||||
notification.notify({
|
||||
title: 'Linked doc created',
|
||||
message: 'You can click undo to recovery block content',
|
||||
accent: 'info',
|
||||
duration: 10 * 1000,
|
||||
action: {
|
||||
label: 'Undo',
|
||||
onClick: () => {
|
||||
doc.undo();
|
||||
clear();
|
||||
},
|
||||
},
|
||||
abort: abortController.signal,
|
||||
onClose: clear,
|
||||
});
|
||||
}
|
||||
|
||||
export function addBlocksToDoc(
|
||||
targetDoc: Doc,
|
||||
model: BlockModel,
|
||||
parentId: string
|
||||
) {
|
||||
// Add current block to linked doc
|
||||
const blockProps = getBlockProps(model);
|
||||
const newModelId = targetDoc.addBlock(
|
||||
model.flavour as BlockSuite.Flavour,
|
||||
blockProps,
|
||||
parentId
|
||||
);
|
||||
// Add children to linked doc, parent is the new model
|
||||
const children = model.children;
|
||||
if (children.length > 0) {
|
||||
children.forEach(child => {
|
||||
addBlocksToDoc(targetDoc, child, newModelId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function convertSelectedBlocksToLinkedDoc(
|
||||
std: BlockSuite.Std,
|
||||
doc: Doc,
|
||||
selectedModels: DraftModel[] | Promise<DraftModel[]>,
|
||||
docTitle?: string
|
||||
) {
|
||||
const models = await selectedModels;
|
||||
const slice = std.clipboard.sliceToSnapshot(Slice.fromModels(doc, models));
|
||||
if (!slice) {
|
||||
return;
|
||||
}
|
||||
const firstBlock = models[0];
|
||||
assertExists(firstBlock);
|
||||
// if title undefined, use the first heading block content as doc title
|
||||
const title = docTitle || getTitleFromSelectedModels(models);
|
||||
const linkedDoc = createLinkedDocFromSlice(std, doc, slice.content, title);
|
||||
// insert linked doc card
|
||||
doc.addSiblingBlocks(
|
||||
doc.getBlock(firstBlock.id)!.model,
|
||||
[
|
||||
{
|
||||
flavour: 'affine:embed-linked-doc',
|
||||
pageId: linkedDoc.id,
|
||||
},
|
||||
],
|
||||
'before'
|
||||
);
|
||||
// delete selected elements
|
||||
models.forEach(model => doc.deleteBlock(model));
|
||||
return linkedDoc;
|
||||
}
|
||||
|
||||
export function createLinkedDocFromSlice(
|
||||
std: BlockSuite.Std,
|
||||
doc: Doc,
|
||||
snapshots: BlockSnapshot[],
|
||||
docTitle?: string
|
||||
) {
|
||||
// const modelsWithChildren = (list:BlockModel[]):BlockModel[]=>list.flatMap(model=>[model,...modelsWithChildren(model.children)])
|
||||
const linkedDoc = doc.collection.createDoc({});
|
||||
linkedDoc.load(() => {
|
||||
const rootId = linkedDoc.addBlock('affine:page', {
|
||||
title: new doc.Text(docTitle),
|
||||
});
|
||||
linkedDoc.addBlock('affine:surface', {}, rootId);
|
||||
const noteId = linkedDoc.addBlock('affine:note', {}, rootId);
|
||||
snapshots.forEach(snapshot => {
|
||||
std.clipboard
|
||||
.pasteBlockSnapshot(snapshot, linkedDoc, noteId)
|
||||
.catch(console.error);
|
||||
});
|
||||
});
|
||||
return linkedDoc;
|
||||
}
|
||||
|
||||
export function createLinkedDocFromNote(
|
||||
doc: Doc,
|
||||
note: NoteBlockModel,
|
||||
docTitle?: string
|
||||
) {
|
||||
const linkedDoc = doc.collection.createDoc({});
|
||||
linkedDoc.load(() => {
|
||||
const rootId = linkedDoc.addBlock('affine:page', {
|
||||
title: new doc.Text(docTitle),
|
||||
});
|
||||
linkedDoc.addBlock('affine:surface', {}, rootId);
|
||||
const blockProps = getBlockProps(note);
|
||||
// keep note props & show in both mode
|
||||
const noteId = linkedDoc.addBlock(
|
||||
'affine:note',
|
||||
{
|
||||
...blockProps,
|
||||
hidden: false,
|
||||
displayMode: NoteDisplayMode.DocAndEdgeless,
|
||||
},
|
||||
rootId
|
||||
);
|
||||
// Add note to linked doc recursively
|
||||
note.children.forEach(model => {
|
||||
addBlocksToDoc(linkedDoc, model, noteId);
|
||||
});
|
||||
});
|
||||
|
||||
return linkedDoc;
|
||||
}
|
||||
|
||||
export function createLinkedDocFromEdgelessElements(
|
||||
host: EditorHost,
|
||||
elements: BlockSuite.EdgelessModel[],
|
||||
docTitle?: string
|
||||
) {
|
||||
const linkedDoc = host.doc.collection.createDoc({});
|
||||
linkedDoc.load(() => {
|
||||
const rootId = linkedDoc.addBlock('affine:page', {
|
||||
title: new host.doc.Text(docTitle),
|
||||
});
|
||||
const surfaceId = linkedDoc.addBlock('affine:surface', {}, rootId);
|
||||
const surface = getSurfaceBlock(linkedDoc);
|
||||
if (!surface) return;
|
||||
|
||||
const sortedElements = sortEdgelessElements(elements);
|
||||
const ids = new Map<string, string>();
|
||||
sortedElements.forEach(model => {
|
||||
let newId = model.id;
|
||||
if (model instanceof GfxBlockModel) {
|
||||
const blockProps = getBlockProps(model);
|
||||
if (isNoteBlock(model)) {
|
||||
newId = linkedDoc.addBlock('affine:note', blockProps, rootId);
|
||||
// Add note children to linked doc recursively
|
||||
model.children.forEach(model => {
|
||||
addBlocksToDoc(linkedDoc, model, newId);
|
||||
});
|
||||
} else {
|
||||
if (isFrameBlock(model)) {
|
||||
mapFrameIds(blockProps as unknown as FrameBlockModel, ids);
|
||||
}
|
||||
|
||||
newId = linkedDoc.addBlock(
|
||||
model.flavour as BlockSuite.Flavour,
|
||||
blockProps,
|
||||
surfaceId
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const props = getElementProps(model, ids);
|
||||
newId = surface.addElement(props);
|
||||
}
|
||||
ids.set(model.id, newId);
|
||||
});
|
||||
});
|
||||
|
||||
host.std.get(DocModeProvider).setPrimaryMode('edgeless', linkedDoc.id);
|
||||
return linkedDoc;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
DarkLoadingIcon,
|
||||
EmbedCardDarkBannerIcon,
|
||||
EmbedCardDarkCubeIcon,
|
||||
EmbedCardDarkHorizontalIcon,
|
||||
EmbedCardDarkListIcon,
|
||||
EmbedCardDarkVerticalIcon,
|
||||
EmbedCardLightBannerIcon,
|
||||
EmbedCardLightCubeIcon,
|
||||
EmbedCardLightHorizontalIcon,
|
||||
EmbedCardLightListIcon,
|
||||
EmbedCardLightVerticalIcon,
|
||||
LightLoadingIcon,
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import {
|
||||
ColorScheme,
|
||||
type DocMode,
|
||||
DocModes,
|
||||
type ReferenceInfo,
|
||||
} from '@blocksuite/affine-model';
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
type EmbedCardIcons = {
|
||||
LoadingIcon: TemplateResult<1>;
|
||||
EmbedCardBannerIcon: TemplateResult<1>;
|
||||
EmbedCardHorizontalIcon: TemplateResult<1>;
|
||||
EmbedCardListIcon: TemplateResult<1>;
|
||||
EmbedCardVerticalIcon: TemplateResult<1>;
|
||||
EmbedCardCubeIcon: TemplateResult<1>;
|
||||
};
|
||||
|
||||
export function getEmbedCardIcons(theme: ColorScheme): EmbedCardIcons {
|
||||
if (theme === ColorScheme.Light) {
|
||||
return {
|
||||
LoadingIcon: LightLoadingIcon,
|
||||
EmbedCardBannerIcon: EmbedCardLightBannerIcon,
|
||||
EmbedCardHorizontalIcon: EmbedCardLightHorizontalIcon,
|
||||
EmbedCardListIcon: EmbedCardLightListIcon,
|
||||
EmbedCardVerticalIcon: EmbedCardLightVerticalIcon,
|
||||
EmbedCardCubeIcon: EmbedCardLightCubeIcon,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
LoadingIcon: DarkLoadingIcon,
|
||||
EmbedCardBannerIcon: EmbedCardDarkBannerIcon,
|
||||
EmbedCardHorizontalIcon: EmbedCardDarkHorizontalIcon,
|
||||
EmbedCardListIcon: EmbedCardDarkListIcon,
|
||||
EmbedCardVerticalIcon: EmbedCardDarkVerticalIcon,
|
||||
EmbedCardCubeIcon: EmbedCardDarkCubeIcon,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function extractSearchParams(link: string) {
|
||||
try {
|
||||
const url = new URL(link);
|
||||
const mode = url.searchParams.get('mode') as DocMode | undefined;
|
||||
|
||||
if (mode && DocModes.includes(mode)) {
|
||||
const params: ReferenceInfo['params'] = { mode: mode as DocMode };
|
||||
const blockIds = url.searchParams
|
||||
.get('blockIds')
|
||||
?.trim()
|
||||
.split(',')
|
||||
.map(id => id.trim())
|
||||
.filter(id => id.length);
|
||||
const elementIds = url.searchParams
|
||||
.get('elementIds')
|
||||
?.trim()
|
||||
.split(',')
|
||||
.map(id => id.trim())
|
||||
.filter(id => id.length);
|
||||
|
||||
if (blockIds?.length) {
|
||||
params.blockIds = blockIds;
|
||||
}
|
||||
|
||||
if (elementIds?.length) {
|
||||
params.elementIds = elementIds;
|
||||
}
|
||||
|
||||
return { params };
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user