mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-25 14:28:51 +08:00
chore: merge blocksuite source code (#9213)
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import { EmbedSyncedDocBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
BlockHtmlAdapterExtension,
|
||||
type BlockHtmlAdapterMatcher,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
export const embedSyncedDocBlockHtmlAdapterMatcher: BlockHtmlAdapterMatcher = {
|
||||
flavour: EmbedSyncedDocBlockSchema.model.flavour,
|
||||
toMatch: () => false,
|
||||
fromMatch: o => o.node.flavour === EmbedSyncedDocBlockSchema.model.flavour,
|
||||
toBlockSnapshot: {},
|
||||
fromBlockSnapshot: {
|
||||
enter: async (o, context) => {
|
||||
const { configs, walker, walkerContext, job } = context;
|
||||
const type = configs.get('embedSyncedDocExportType');
|
||||
|
||||
// this context is used for nested sync block
|
||||
if (
|
||||
walkerContext.getGlobalContext('embed-synced-doc-counter') === undefined
|
||||
) {
|
||||
walkerContext.setGlobalContext('embed-synced-doc-counter', 0);
|
||||
}
|
||||
let counter = walkerContext.getGlobalContext(
|
||||
'embed-synced-doc-counter'
|
||||
) as number;
|
||||
walkerContext.setGlobalContext('embed-synced-doc-counter', ++counter);
|
||||
|
||||
if (type === 'content') {
|
||||
const syncedDocId = o.node.props.pageId as string;
|
||||
const syncedDoc = job.collection.getDoc(syncedDocId);
|
||||
walkerContext.setGlobalContext('hast:html-root-doc', false);
|
||||
if (!syncedDoc) return;
|
||||
|
||||
if (counter === 1) {
|
||||
const syncedSnapshot = job.docToSnapshot(syncedDoc);
|
||||
if (syncedSnapshot) {
|
||||
await walker.walkONode(syncedSnapshot.blocks);
|
||||
}
|
||||
} else {
|
||||
walkerContext
|
||||
.openNode(
|
||||
{
|
||||
type: 'element',
|
||||
tagName: 'div',
|
||||
properties: {
|
||||
className: ['affine-paragraph-block-container'],
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
'children'
|
||||
)
|
||||
.openNode(
|
||||
{
|
||||
type: 'element',
|
||||
tagName: 'p',
|
||||
properties: {},
|
||||
children: [
|
||||
{ type: 'text', value: syncedDoc.meta?.title ?? '' },
|
||||
],
|
||||
},
|
||||
'children'
|
||||
)
|
||||
.closeNode()
|
||||
.closeNode();
|
||||
}
|
||||
}
|
||||
},
|
||||
leave: (_, context) => {
|
||||
const { walkerContext } = context;
|
||||
const counter = walkerContext.getGlobalContext(
|
||||
'embed-synced-doc-counter'
|
||||
) as number;
|
||||
const currentCounter = counter - 1;
|
||||
walkerContext.setGlobalContext(
|
||||
'embed-synced-doc-counter',
|
||||
currentCounter
|
||||
);
|
||||
// When leave the last embed synced doc block, we need to set the html root doc context to true
|
||||
walkerContext.setGlobalContext(
|
||||
'hast:html-root-doc',
|
||||
currentCounter === 0
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const EmbedSyncedDocBlockHtmlAdapterExtension =
|
||||
BlockHtmlAdapterExtension(embedSyncedDocBlockHtmlAdapterMatcher);
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './html.js';
|
||||
export * from './markdown.js';
|
||||
export * from './plain-text.js';
|
||||
@@ -0,0 +1,64 @@
|
||||
import { EmbedSyncedDocBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
BlockMarkdownAdapterExtension,
|
||||
type BlockMarkdownAdapterMatcher,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
export const embedSyncedDocBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher =
|
||||
{
|
||||
flavour: EmbedSyncedDocBlockSchema.model.flavour,
|
||||
toMatch: () => false,
|
||||
fromMatch: o => o.node.flavour === EmbedSyncedDocBlockSchema.model.flavour,
|
||||
toBlockSnapshot: {},
|
||||
fromBlockSnapshot: {
|
||||
enter: async (o, context) => {
|
||||
const { configs, walker, walkerContext, job } = context;
|
||||
const type = configs.get('embedSyncedDocExportType');
|
||||
|
||||
// this context is used for nested sync block
|
||||
if (
|
||||
walkerContext.getGlobalContext('embed-synced-doc-counter') ===
|
||||
undefined
|
||||
) {
|
||||
walkerContext.setGlobalContext('embed-synced-doc-counter', 0);
|
||||
}
|
||||
let counter = walkerContext.getGlobalContext(
|
||||
'embed-synced-doc-counter'
|
||||
) as number;
|
||||
walkerContext.setGlobalContext('embed-synced-doc-counter', ++counter);
|
||||
|
||||
if (type === 'content') {
|
||||
const syncedDocId = o.node.props.pageId as string;
|
||||
const syncedDoc = job.collection.getDoc(syncedDocId);
|
||||
if (!syncedDoc) return;
|
||||
|
||||
if (counter === 1) {
|
||||
const syncedSnapshot = job.docToSnapshot(syncedDoc);
|
||||
if (syncedSnapshot) {
|
||||
await walker.walkONode(syncedSnapshot.blocks);
|
||||
}
|
||||
} else {
|
||||
// TODO(@L-Sun) may be use the nested content
|
||||
walkerContext
|
||||
.openNode({
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{ type: 'text', value: syncedDoc.meta?.title ?? '' },
|
||||
],
|
||||
})
|
||||
.closeNode();
|
||||
}
|
||||
}
|
||||
},
|
||||
leave: (_, context) => {
|
||||
const { walkerContext } = context;
|
||||
const counter = walkerContext.getGlobalContext(
|
||||
'embed-synced-doc-counter'
|
||||
) as number;
|
||||
walkerContext.setGlobalContext('embed-synced-doc-counter', counter - 1);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const EmbedSyncedDocBlockMarkdownAdapterExtension =
|
||||
BlockMarkdownAdapterExtension(embedSyncedDocBlockMarkdownAdapterMatcher);
|
||||
@@ -0,0 +1,62 @@
|
||||
import { EmbedSyncedDocBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
BlockPlainTextAdapterExtension,
|
||||
type BlockPlainTextAdapterMatcher,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
export const embedSyncedDocBlockPlainTextAdapterMatcher: BlockPlainTextAdapterMatcher =
|
||||
{
|
||||
flavour: EmbedSyncedDocBlockSchema.model.flavour,
|
||||
toMatch: () => false,
|
||||
fromMatch: o => o.node.flavour === EmbedSyncedDocBlockSchema.model.flavour,
|
||||
toBlockSnapshot: {},
|
||||
fromBlockSnapshot: {
|
||||
enter: async (o, context) => {
|
||||
const { configs, walker, walkerContext, job, textBuffer } = context;
|
||||
const type = configs.get('embedSyncedDocExportType');
|
||||
|
||||
// this context is used for nested sync block
|
||||
if (
|
||||
walkerContext.getGlobalContext('embed-synced-doc-counter') ===
|
||||
undefined
|
||||
) {
|
||||
walkerContext.setGlobalContext('embed-synced-doc-counter', 0);
|
||||
}
|
||||
let counter = walkerContext.getGlobalContext(
|
||||
'embed-synced-doc-counter'
|
||||
) as number;
|
||||
walkerContext.setGlobalContext('embed-synced-doc-counter', ++counter);
|
||||
|
||||
let buffer = '';
|
||||
|
||||
if (type === 'content') {
|
||||
const syncedDocId = o.node.props.pageId as string;
|
||||
const syncedDoc = job.collection.getDoc(syncedDocId);
|
||||
if (!syncedDoc) return;
|
||||
|
||||
if (counter === 1) {
|
||||
const syncedSnapshot = job.docToSnapshot(syncedDoc);
|
||||
if (syncedSnapshot) {
|
||||
await walker.walkONode(syncedSnapshot.blocks);
|
||||
}
|
||||
} else {
|
||||
buffer = syncedDoc.meta?.title ?? '';
|
||||
if (buffer) {
|
||||
buffer += '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
textBuffer.content += buffer;
|
||||
},
|
||||
leave: (_, context) => {
|
||||
const { walkerContext } = context;
|
||||
const counter = walkerContext.getGlobalContext(
|
||||
'embed-synced-doc-counter'
|
||||
) as number;
|
||||
walkerContext.setGlobalContext('embed-synced-doc-counter', counter - 1);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const EmbedSyncedDocBlockPlainTextAdapterExtension =
|
||||
BlockPlainTextAdapterExtension(embedSyncedDocBlockPlainTextAdapterMatcher);
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { isGfxBlockComponent, ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { html, nothing } from 'lit';
|
||||
import { property, queryAsync } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
|
||||
import { renderLinkedDocInCard } from '../../common/render-linked-doc.js';
|
||||
import type { EmbedSyncedDocBlockComponent } from '../embed-synced-doc-block.js';
|
||||
import { cardStyles } from '../styles.js';
|
||||
import { getSyncedDocIcons } from '../utils.js';
|
||||
|
||||
export class EmbedSyncedDocCard extends WithDisposable(ShadowlessElement) {
|
||||
static override styles = cardStyles;
|
||||
|
||||
private _dragging = false;
|
||||
|
||||
get blockState() {
|
||||
return this.block.blockState;
|
||||
}
|
||||
|
||||
get editorMode() {
|
||||
return this.block.editorMode;
|
||||
}
|
||||
|
||||
get host() {
|
||||
return this.block.host;
|
||||
}
|
||||
|
||||
get linkedDoc() {
|
||||
return this.block.syncedDoc;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return this.block.model;
|
||||
}
|
||||
|
||||
get std() {
|
||||
return this.block.std;
|
||||
}
|
||||
|
||||
private _handleClick(event: MouseEvent) {
|
||||
event.stopPropagation();
|
||||
if (!isGfxBlockComponent(this.block)) {
|
||||
this._selectBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private _isDocEmpty() {
|
||||
const syncedDoc = this.block.syncedDoc;
|
||||
if (!syncedDoc) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
!!syncedDoc &&
|
||||
!syncedDoc.meta?.title.length &&
|
||||
this.isNoteContentEmpty &&
|
||||
this.isBannerEmpty
|
||||
);
|
||||
}
|
||||
|
||||
private _selectBlock() {
|
||||
const selectionManager = this.host.selection;
|
||||
const blockSelection = selectionManager.create('block', {
|
||||
blockId: this.block.blockId,
|
||||
});
|
||||
selectionManager.setGroup('note', [blockSelection]);
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this.block.handleEvent(
|
||||
'dragStart',
|
||||
() => {
|
||||
this._dragging = true;
|
||||
},
|
||||
{ global: true }
|
||||
);
|
||||
this.block.handleEvent(
|
||||
'dragEnd',
|
||||
() => {
|
||||
this._dragging = false;
|
||||
},
|
||||
{ global: true }
|
||||
);
|
||||
|
||||
const { isCycle } = this.block.blockState;
|
||||
const syncedDoc = this.block.syncedDoc;
|
||||
if (isCycle && syncedDoc) {
|
||||
if (syncedDoc.root) {
|
||||
renderLinkedDocInCard(this);
|
||||
} else {
|
||||
syncedDoc.slots.rootAdded.once(() => {
|
||||
renderLinkedDocInCard(this);
|
||||
});
|
||||
}
|
||||
|
||||
this.disposables.add(
|
||||
syncedDoc.collection.meta.docMetaUpdated.on(() => {
|
||||
renderLinkedDocInCard(this);
|
||||
})
|
||||
);
|
||||
this.disposables.add(
|
||||
syncedDoc.slots.blockUpdated.on(payload => {
|
||||
if (this._dragging) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
payload.type === 'update' &&
|
||||
['', 'caption', 'xywh'].includes(payload.props.key)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
renderLinkedDocInCard(this);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
override render() {
|
||||
const { isLoading, isDeleted, isError, isCycle } = this.blockState;
|
||||
const error = this.isError || isError;
|
||||
|
||||
const isEmpty = this._isDocEmpty() && this.isBannerEmpty;
|
||||
|
||||
const cardClassMap = classMap({
|
||||
loading: isLoading,
|
||||
error,
|
||||
deleted: isDeleted,
|
||||
cycle: isCycle,
|
||||
surface: isGfxBlockComponent(this.block),
|
||||
empty: isEmpty,
|
||||
'banner-empty': this.isBannerEmpty,
|
||||
'note-empty': this.isNoteContentEmpty,
|
||||
});
|
||||
|
||||
const theme = this.std.get(ThemeProvider).theme;
|
||||
const {
|
||||
LoadingIcon,
|
||||
SyncedDocErrorIcon,
|
||||
ReloadIcon,
|
||||
SyncedDocEmptyBanner,
|
||||
SyncedDocErrorBanner,
|
||||
SyncedDocDeletedBanner,
|
||||
} = getSyncedDocIcons(theme, this.editorMode);
|
||||
|
||||
const icon = error
|
||||
? SyncedDocErrorIcon
|
||||
: isLoading
|
||||
? LoadingIcon
|
||||
: this.block.icon$.value;
|
||||
const title = isLoading ? 'Loading...' : this.block.title$;
|
||||
|
||||
const showDefaultNoteContent = isLoading || error || isDeleted || isEmpty;
|
||||
const defaultNoteContent = error
|
||||
? 'This linked doc failed to load.'
|
||||
: isLoading
|
||||
? ''
|
||||
: isDeleted
|
||||
? 'This linked doc is deleted.'
|
||||
: isEmpty
|
||||
? 'Preview of the page will be displayed here.'
|
||||
: '';
|
||||
|
||||
const dateText = this.block.docUpdatedAt.toLocaleString();
|
||||
|
||||
const showDefaultBanner = isLoading || error || isDeleted || isEmpty;
|
||||
|
||||
const defaultBanner = isLoading
|
||||
? SyncedDocEmptyBanner
|
||||
: error
|
||||
? SyncedDocErrorBanner
|
||||
: isDeleted
|
||||
? SyncedDocDeletedBanner
|
||||
: SyncedDocEmptyBanner;
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="affine-embed-synced-doc-card ${cardClassMap}"
|
||||
@click=${this._handleClick}
|
||||
>
|
||||
<div class="affine-embed-synced-doc-card-content">
|
||||
<div class="affine-embed-synced-doc-card-content-title">
|
||||
<div class="affine-embed-synced-doc-card-content-title-icon">
|
||||
${icon}
|
||||
</div>
|
||||
|
||||
<div class="affine-embed-synced-doc-card-content-title-text">
|
||||
${title}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${showDefaultNoteContent
|
||||
? html`<div class="affine-embed-synced-doc-content-note default">
|
||||
${defaultNoteContent}
|
||||
</div>`
|
||||
: nothing}
|
||||
<div class="affine-embed-synced-doc-content-note render"></div>
|
||||
|
||||
${error
|
||||
? html`
|
||||
<div class="affine-embed-synced-doc-card-content-reload">
|
||||
<div
|
||||
class="affine-embed-synced-doc-card-content-reload-button"
|
||||
@click=${() => this.block.refreshData()}
|
||||
>
|
||||
${ReloadIcon} <span>Reload</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<div class="affine-embed-synced-doc-card-content-date">
|
||||
<span>Updated</span>
|
||||
|
||||
<span>${dateText}</span>
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
|
||||
<div class="affine-embed-synced-doc-card-banner render"></div>
|
||||
|
||||
${showDefaultBanner
|
||||
? html`
|
||||
<div class="affine-embed-synced-doc-card-banner default">
|
||||
${defaultBanner}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@queryAsync('.affine-embed-synced-doc-card-banner.render')
|
||||
accessor bannerContainer!: Promise<HTMLDivElement>;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor block!: EmbedSyncedDocBlockComponent;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor isBannerEmpty = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor isError = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor isNoteContentEmpty = false;
|
||||
|
||||
@queryAsync('.affine-embed-synced-doc-content-note.render')
|
||||
accessor noteContainer!: Promise<HTMLDivElement>;
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
import type { AliasInfo } from '@blocksuite/affine-model';
|
||||
import {
|
||||
EMBED_CARD_HEIGHT,
|
||||
EMBED_CARD_WIDTH,
|
||||
} from '@blocksuite/affine-shared/consts';
|
||||
import {
|
||||
ThemeExtensionIdentifier,
|
||||
ThemeProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { BlockStdScope } from '@blocksuite/block-std';
|
||||
import { assertExists, Bound } from '@blocksuite/global/utils';
|
||||
import { html } from 'lit';
|
||||
import { choose } from 'lit/directives/choose.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { guard } from 'lit/directives/guard.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { toEdgelessEmbedBlock } from '../common/to-edgeless-embed-block.js';
|
||||
import { EmbedSyncedDocBlockComponent } from './embed-synced-doc-block.js';
|
||||
|
||||
export class EmbedEdgelessSyncedDocBlockComponent extends toEdgelessEmbedBlock(
|
||||
EmbedSyncedDocBlockComponent
|
||||
) {
|
||||
protected override _renderSyncedView = () => {
|
||||
const { syncedDoc, editorMode } = this;
|
||||
|
||||
assertExists(syncedDoc, 'Doc should exist');
|
||||
|
||||
let containerStyleMap = styleMap({
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
});
|
||||
const modelScale = this.model.scale ?? 1;
|
||||
const bound = Bound.deserialize(this.model.xywh);
|
||||
const width = bound.w / modelScale;
|
||||
const height = bound.h / modelScale;
|
||||
containerStyleMap = styleMap({
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
minHeight: `${height}px`,
|
||||
transform: `scale(${modelScale})`,
|
||||
transformOrigin: '0 0',
|
||||
});
|
||||
|
||||
const themeService = this.std.get(ThemeProvider);
|
||||
const themeExtension = this.std.getOptional(ThemeExtensionIdentifier);
|
||||
const appTheme = themeService.app$.value;
|
||||
let edgelessTheme = themeService.edgeless$.value;
|
||||
if (themeExtension?.getEdgelessTheme && this.syncedDoc?.id) {
|
||||
edgelessTheme = themeExtension.getEdgelessTheme(this.syncedDoc.id).value;
|
||||
}
|
||||
const theme = this.isPageMode ? appTheme : edgelessTheme;
|
||||
|
||||
const isSelected = !!this.selected?.is('block');
|
||||
const scale = this.model.scale ?? 1;
|
||||
|
||||
this.dataset.nestedEditor = '';
|
||||
|
||||
const renderEditor = () => {
|
||||
return choose(editorMode, [
|
||||
[
|
||||
'page',
|
||||
() => html`
|
||||
<div class="affine-page-viewport" data-theme=${appTheme}>
|
||||
${new BlockStdScope({
|
||||
doc: syncedDoc,
|
||||
extensions: this._buildPreviewSpec('page:preview'),
|
||||
}).render()}
|
||||
</div>
|
||||
`,
|
||||
],
|
||||
[
|
||||
'edgeless',
|
||||
() => html`
|
||||
<div class="affine-edgeless-viewport" data-theme=${edgelessTheme}>
|
||||
${new BlockStdScope({
|
||||
doc: syncedDoc,
|
||||
extensions: this._buildPreviewSpec('edgeless:preview'),
|
||||
}).render()}
|
||||
</div>
|
||||
`,
|
||||
],
|
||||
]);
|
||||
};
|
||||
|
||||
return this.renderEmbed(
|
||||
() => html`
|
||||
<div
|
||||
class=${classMap({
|
||||
'affine-embed-synced-doc-container': true,
|
||||
[editorMode]: true,
|
||||
[theme]: true,
|
||||
selected: isSelected,
|
||||
surface: true,
|
||||
})}
|
||||
@click=${this._handleClick}
|
||||
style=${containerStyleMap}
|
||||
?data-scale=${scale}
|
||||
>
|
||||
<div class="affine-embed-synced-doc-editor">
|
||||
${this.isPageMode && this._isEmptySyncedDoc
|
||||
? html`
|
||||
<div class="affine-embed-synced-doc-editor-empty">
|
||||
<span>
|
||||
This is a linked doc, you can add content here.
|
||||
</span>
|
||||
</div>
|
||||
`
|
||||
: guard([editorMode, syncedDoc], renderEditor)}
|
||||
</div>
|
||||
<div class="affine-embed-synced-doc-editor-overlay"></div>
|
||||
</div>
|
||||
`
|
||||
);
|
||||
};
|
||||
|
||||
override convertToCard = (aliasInfo?: AliasInfo) => {
|
||||
const { id, doc, caption, xywh } = this.model;
|
||||
|
||||
const edgelessService = this.rootService;
|
||||
const style = 'vertical';
|
||||
const bound = Bound.deserialize(xywh);
|
||||
bound.w = EMBED_CARD_WIDTH[style];
|
||||
bound.h = EMBED_CARD_HEIGHT[style];
|
||||
|
||||
if (!edgelessService) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-expect-error TODO: fix after edgeless refactor
|
||||
const newId = edgelessService.addBlock(
|
||||
'affine:embed-linked-doc',
|
||||
{
|
||||
xywh: bound.serialize(),
|
||||
style,
|
||||
caption,
|
||||
...this.referenceInfo,
|
||||
...aliasInfo,
|
||||
},
|
||||
// @ts-expect-error TODO: fix after edgeless refactor
|
||||
edgelessService.surface
|
||||
);
|
||||
|
||||
this.std.command.exec('reassociateConnectors', {
|
||||
oldId: id,
|
||||
newId,
|
||||
});
|
||||
|
||||
// @ts-expect-error TODO: fix after edgeless refactor
|
||||
edgelessService.selection.set({
|
||||
editing: false,
|
||||
elements: [newId],
|
||||
});
|
||||
doc.deleteBlock(this.model);
|
||||
};
|
||||
|
||||
get rootService() {
|
||||
return this.std.getService('affine:page');
|
||||
}
|
||||
|
||||
override renderGfxBlock() {
|
||||
const { style, xywh } = this.model;
|
||||
const bound = Bound.deserialize(xywh);
|
||||
|
||||
this.embedContainerStyle.width = `${bound.w}px`;
|
||||
this.embedContainerStyle.height = `${bound.h}px`;
|
||||
|
||||
this.cardStyleMap = {
|
||||
display: 'block',
|
||||
width: `${EMBED_CARD_WIDTH[style]}px`,
|
||||
height: `${EMBED_CARD_WIDTH[style]}px`,
|
||||
transform: `scale(${bound.w / EMBED_CARD_WIDTH[style]}, ${bound.h / EMBED_CARD_HEIGHT[style]})`,
|
||||
transformOrigin: '0 0',
|
||||
};
|
||||
|
||||
return this.renderPageContent();
|
||||
}
|
||||
|
||||
override accessor useCaptionEditor = true;
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
import { Peekable } from '@blocksuite/affine-components/peek';
|
||||
import {
|
||||
REFERENCE_NODE,
|
||||
RefNodeSlotsProvider,
|
||||
} from '@blocksuite/affine-components/rich-text';
|
||||
import {
|
||||
type AliasInfo,
|
||||
type DocMode,
|
||||
type EmbedSyncedDocModel,
|
||||
NoteDisplayMode,
|
||||
type ReferenceInfo,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
DocDisplayMetaProvider,
|
||||
DocModeProvider,
|
||||
ThemeExtensionIdentifier,
|
||||
ThemeProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
cloneReferenceInfo,
|
||||
SpecProvider,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
BlockServiceWatcher,
|
||||
BlockStdScope,
|
||||
type EditorHost,
|
||||
} from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { assertExists, Bound, getCommonBound } from '@blocksuite/global/utils';
|
||||
import {
|
||||
BlockViewType,
|
||||
DocCollection,
|
||||
type GetDocOptions,
|
||||
type Query,
|
||||
} from '@blocksuite/store';
|
||||
import { computed } from '@preact/signals-core';
|
||||
import { html, type PropertyValues } from 'lit';
|
||||
import { query, state } from 'lit/decorators.js';
|
||||
import { choose } from 'lit/directives/choose.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { guard } from 'lit/directives/guard.js';
|
||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { EmbedBlockComponent } from '../common/embed-block-element.js';
|
||||
import { isEmptyDoc } from '../common/render-linked-doc.js';
|
||||
import type { EmbedSyncedDocCard } from './components/embed-synced-doc-card.js';
|
||||
import { blockStyles } from './styles.js';
|
||||
|
||||
@Peekable({
|
||||
enableOn: ({ doc }: EmbedSyncedDocBlockComponent) => !doc.readonly,
|
||||
})
|
||||
export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSyncedDocModel> {
|
||||
static override styles = blockStyles;
|
||||
|
||||
// Caches total bounds, includes all blocks and elements.
|
||||
private _cachedBounds: Bound | null = null;
|
||||
|
||||
private _initEdgelessFitEffect = () => {
|
||||
const fitToContent = () => {
|
||||
if (this.isPageMode) return;
|
||||
|
||||
const controller = this.syncedDocEditorHost?.std.getOptional(
|
||||
GfxControllerIdentifier
|
||||
);
|
||||
if (!controller) return;
|
||||
|
||||
const viewport = controller.viewport;
|
||||
if (!viewport) return;
|
||||
|
||||
if (!this._cachedBounds) {
|
||||
this._cachedBounds = getCommonBound([
|
||||
...controller.layer.blocks.map(block =>
|
||||
Bound.deserialize(block.xywh)
|
||||
),
|
||||
...controller.layer.canvasElements,
|
||||
]);
|
||||
}
|
||||
|
||||
viewport.onResize();
|
||||
|
||||
const { centerX, centerY, zoom } = viewport.getFitToScreenData(
|
||||
this._cachedBounds
|
||||
);
|
||||
viewport.setCenter(centerX, centerY);
|
||||
viewport.setZoom(zoom);
|
||||
};
|
||||
|
||||
const observer = new ResizeObserver(fitToContent);
|
||||
const block = this.embedBlock;
|
||||
|
||||
observer.observe(block);
|
||||
|
||||
this._disposables.add(() => {
|
||||
observer.disconnect();
|
||||
});
|
||||
|
||||
this.syncedDocEditorHost?.updateComplete
|
||||
.then(() => fitToContent())
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
private _pageFilter: Query = {
|
||||
mode: 'loose',
|
||||
match: [
|
||||
{
|
||||
flavour: 'affine:note',
|
||||
props: {
|
||||
displayMode: NoteDisplayMode.EdgelessOnly,
|
||||
},
|
||||
viewType: BlockViewType.Hidden,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
protected _buildPreviewSpec = (name: 'page:preview' | 'edgeless:preview') => {
|
||||
const nextDepth = this.depth + 1;
|
||||
const previewSpecBuilder = SpecProvider.getInstance().getSpec(name);
|
||||
const currentDisposables = this.disposables;
|
||||
|
||||
class EmbedSyncedDocWatcher extends BlockServiceWatcher {
|
||||
static override readonly flavour = 'affine:embed-synced-doc';
|
||||
|
||||
override mounted() {
|
||||
const disposableGroup = this.blockService.disposables;
|
||||
const slots = this.blockService.specSlots;
|
||||
disposableGroup.add(
|
||||
slots.viewConnected.on(({ component }) => {
|
||||
const nextComponent = component as EmbedSyncedDocBlockComponent;
|
||||
nextComponent.depth = nextDepth;
|
||||
currentDisposables.add(() => {
|
||||
nextComponent.depth = 0;
|
||||
});
|
||||
})
|
||||
);
|
||||
disposableGroup.add(
|
||||
slots.viewDisconnected.on(({ component }) => {
|
||||
const nextComponent = component as EmbedSyncedDocBlockComponent;
|
||||
nextComponent.depth = 0;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
previewSpecBuilder.extend([EmbedSyncedDocWatcher]);
|
||||
|
||||
return previewSpecBuilder.value;
|
||||
};
|
||||
|
||||
protected _renderSyncedView = () => {
|
||||
const syncedDoc = this.syncedDoc;
|
||||
const editorMode = this.editorMode;
|
||||
const isPageMode = this.isPageMode;
|
||||
|
||||
assertExists(syncedDoc);
|
||||
|
||||
if (isPageMode) {
|
||||
this.dataset.pageMode = '';
|
||||
}
|
||||
|
||||
const containerStyleMap = styleMap({
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
const themeService = this.std.get(ThemeProvider);
|
||||
const themeExtension = this.std.getOptional(ThemeExtensionIdentifier);
|
||||
const appTheme = themeService.app$.value;
|
||||
let edgelessTheme = themeService.edgeless$.value;
|
||||
if (themeExtension?.getEdgelessTheme && this.syncedDoc?.id) {
|
||||
edgelessTheme = themeExtension.getEdgelessTheme(this.syncedDoc.id).value;
|
||||
}
|
||||
const theme = isPageMode ? appTheme : edgelessTheme;
|
||||
const isSelected = !!this.selected?.is('block');
|
||||
|
||||
this.dataset.nestedEditor = '';
|
||||
|
||||
const renderEditor = () => {
|
||||
return choose(editorMode, [
|
||||
[
|
||||
'page',
|
||||
() => html`
|
||||
<div class="affine-page-viewport" data-theme=${appTheme}>
|
||||
${new BlockStdScope({
|
||||
doc: syncedDoc,
|
||||
extensions: this._buildPreviewSpec('page:preview'),
|
||||
}).render()}
|
||||
</div>
|
||||
`,
|
||||
],
|
||||
[
|
||||
'edgeless',
|
||||
() => html`
|
||||
<div class="affine-edgeless-viewport" data-theme=${edgelessTheme}>
|
||||
${new BlockStdScope({
|
||||
doc: syncedDoc,
|
||||
extensions: this._buildPreviewSpec('edgeless:preview'),
|
||||
}).render()}
|
||||
</div>
|
||||
`,
|
||||
],
|
||||
]);
|
||||
};
|
||||
|
||||
return this.renderEmbed(
|
||||
() => html`
|
||||
<div
|
||||
class=${classMap({
|
||||
'affine-embed-synced-doc-container': true,
|
||||
[editorMode]: true,
|
||||
[theme]: true,
|
||||
selected: isSelected,
|
||||
surface: false,
|
||||
})}
|
||||
@click=${this._handleClick}
|
||||
style=${containerStyleMap}
|
||||
?data-scale=${undefined}
|
||||
>
|
||||
<div class="affine-embed-synced-doc-editor">
|
||||
${isPageMode && this._isEmptySyncedDoc
|
||||
? html`
|
||||
<div class="affine-embed-synced-doc-editor-empty">
|
||||
<span>
|
||||
This is a linked doc, you can add content here.
|
||||
</span>
|
||||
</div>
|
||||
`
|
||||
: guard([editorMode, syncedDoc], renderEditor)}
|
||||
</div>
|
||||
<div
|
||||
class=${classMap({
|
||||
'affine-embed-synced-doc-header-wrapper': true,
|
||||
selected: isSelected,
|
||||
})}
|
||||
>
|
||||
<div class="affine-embed-synced-doc-header">
|
||||
<span class="affine-embed-synced-doc-icon"
|
||||
>${this.icon$.value}</span
|
||||
>
|
||||
<span class="affine-embed-synced-doc-title">${this.title$}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
);
|
||||
};
|
||||
|
||||
protected cardStyleMap = styleMap({
|
||||
position: 'relative',
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
convertToCard = (aliasInfo?: AliasInfo) => {
|
||||
const { doc, caption } = this.model;
|
||||
|
||||
const parent = doc.getParent(this.model);
|
||||
assertExists(parent);
|
||||
const index = parent.children.indexOf(this.model);
|
||||
|
||||
doc.addBlock(
|
||||
'affine:embed-linked-doc',
|
||||
{ caption, ...this.referenceInfo, ...aliasInfo },
|
||||
parent,
|
||||
index
|
||||
);
|
||||
|
||||
this.std.selection.setGroup('note', []);
|
||||
doc.deleteBlock(this.model);
|
||||
};
|
||||
|
||||
covertToInline = () => {
|
||||
const { doc } = this.model;
|
||||
const parent = doc.getParent(this.model);
|
||||
assertExists(parent);
|
||||
const index = parent.children.indexOf(this.model);
|
||||
|
||||
const yText = new DocCollection.Y.Text();
|
||||
yText.insert(0, REFERENCE_NODE);
|
||||
yText.format(0, REFERENCE_NODE.length, {
|
||||
reference: {
|
||||
type: 'LinkedPage',
|
||||
...this.referenceInfo,
|
||||
},
|
||||
});
|
||||
const text = new doc.Text(yText);
|
||||
|
||||
doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text,
|
||||
},
|
||||
parent,
|
||||
index
|
||||
);
|
||||
|
||||
doc.deleteBlock(this.model);
|
||||
};
|
||||
|
||||
protected override embedContainerStyle: StyleInfo = {
|
||||
height: 'unset',
|
||||
};
|
||||
|
||||
icon$ = computed(() => {
|
||||
const { pageId, params } = this.model;
|
||||
return this.std
|
||||
.get(DocDisplayMetaProvider)
|
||||
.icon(pageId, { params, referenced: true }).value;
|
||||
});
|
||||
|
||||
open = () => {
|
||||
const pageId = this.model.pageId;
|
||||
if (pageId === this.doc.id) return;
|
||||
|
||||
this.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.emit({ pageId });
|
||||
};
|
||||
|
||||
refreshData = () => {
|
||||
this._load().catch(e => {
|
||||
console.error(e);
|
||||
this._error = true;
|
||||
});
|
||||
};
|
||||
|
||||
title$ = computed(() => {
|
||||
const { pageId, params } = this.model;
|
||||
return this.std
|
||||
.get(DocDisplayMetaProvider)
|
||||
.title(pageId, { params, referenced: true });
|
||||
});
|
||||
|
||||
private get _rootService() {
|
||||
return this.std.getService('affine:page');
|
||||
}
|
||||
|
||||
get blockState() {
|
||||
return {
|
||||
isLoading: this._loading,
|
||||
isError: this._error,
|
||||
isDeleted: this._deleted,
|
||||
isCycle: this._cycle,
|
||||
};
|
||||
}
|
||||
|
||||
get docTitle() {
|
||||
return this.syncedDoc?.meta?.title || 'Untitled';
|
||||
}
|
||||
|
||||
get docUpdatedAt() {
|
||||
return this._docUpdatedAt;
|
||||
}
|
||||
|
||||
get editorMode() {
|
||||
return this.linkedMode ?? this.syncedDocMode;
|
||||
}
|
||||
|
||||
protected get isPageMode() {
|
||||
return this.editorMode === 'page';
|
||||
}
|
||||
|
||||
get linkedMode() {
|
||||
return this.referenceInfo.params?.mode;
|
||||
}
|
||||
|
||||
get referenceInfo(): ReferenceInfo {
|
||||
return cloneReferenceInfo(this.model);
|
||||
}
|
||||
|
||||
get syncedDoc() {
|
||||
const options: GetDocOptions = { readonly: true };
|
||||
if (this.isPageMode) options.query = this._pageFilter;
|
||||
return this.std.collection.getDoc(this.model.pageId, options);
|
||||
}
|
||||
|
||||
private _checkCycle() {
|
||||
let editorHost: EditorHost | null = this.host;
|
||||
while (editorHost && !this._cycle) {
|
||||
this._cycle = !!editorHost && editorHost.doc.id === this.model.pageId;
|
||||
editorHost =
|
||||
editorHost.parentElement?.closest<EditorHost>('editor-host') ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
private _isClickAtBorder(
|
||||
event: MouseEvent,
|
||||
element: HTMLElement,
|
||||
tolerance = 8
|
||||
): boolean {
|
||||
const { x, y } = event;
|
||||
const rect = element.getBoundingClientRect();
|
||||
if (!rect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
Math.abs(x - rect.left) < tolerance ||
|
||||
Math.abs(x - rect.right) < tolerance ||
|
||||
Math.abs(y - rect.top) < tolerance ||
|
||||
Math.abs(y - rect.bottom) < tolerance
|
||||
);
|
||||
}
|
||||
|
||||
private async _load() {
|
||||
this._loading = true;
|
||||
this._error = false;
|
||||
this._deleted = false;
|
||||
this._cycle = false;
|
||||
|
||||
const syncedDoc = this.syncedDoc;
|
||||
if (!syncedDoc) {
|
||||
this._deleted = true;
|
||||
this._loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._checkCycle();
|
||||
|
||||
if (!syncedDoc.loaded) {
|
||||
try {
|
||||
syncedDoc.load();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this._error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._error && !syncedDoc.root) {
|
||||
await new Promise<void>(resolve => {
|
||||
syncedDoc.slots.rootAdded.once(() => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
this._loading = false;
|
||||
}
|
||||
|
||||
private _selectBlock() {
|
||||
const selectionManager = this.host.selection;
|
||||
const blockSelection = selectionManager.create('block', {
|
||||
blockId: this.blockId,
|
||||
});
|
||||
selectionManager.setGroup('note', [blockSelection]);
|
||||
}
|
||||
|
||||
private _setDocUpdatedAt() {
|
||||
const meta = this.doc.collection.meta.getDocMeta(this.model.pageId);
|
||||
if (meta) {
|
||||
const date = meta.updatedDate || meta.createDate;
|
||||
this._docUpdatedAt = new Date(date);
|
||||
}
|
||||
}
|
||||
|
||||
protected _handleClick(_event: MouseEvent) {
|
||||
this._selectBlock();
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._cardStyle = this.model.style;
|
||||
|
||||
this.style.display = 'block';
|
||||
this._load().catch(e => {
|
||||
console.error(e);
|
||||
this._error = true;
|
||||
});
|
||||
|
||||
this.contentEditable = 'false';
|
||||
|
||||
this.disposables.add(
|
||||
this.model.propsUpdated.on(({ key }) => {
|
||||
if (key === 'pageId' || key === 'style') {
|
||||
this._load().catch(e => {
|
||||
console.error(e);
|
||||
this._error = true;
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this._setDocUpdatedAt();
|
||||
this.disposables.add(
|
||||
this.doc.collection.meta.docMetaUpdated.on(() => {
|
||||
this._setDocUpdatedAt();
|
||||
})
|
||||
);
|
||||
|
||||
if (this._rootService && !this.linkedMode) {
|
||||
const docMode = this._rootService.std.get(DocModeProvider);
|
||||
this.syncedDocMode = docMode.getPrimaryMode(this.model.pageId);
|
||||
this._isEmptySyncedDoc = isEmptyDoc(this.syncedDoc, this.editorMode);
|
||||
this.disposables.add(
|
||||
docMode.onPrimaryModeChange(mode => {
|
||||
this.syncedDocMode = mode;
|
||||
this._isEmptySyncedDoc = isEmptyDoc(this.syncedDoc, this.editorMode);
|
||||
}, this.model.pageId)
|
||||
);
|
||||
}
|
||||
|
||||
this.syncedDoc &&
|
||||
this.disposables.add(
|
||||
this.syncedDoc.slots.blockUpdated.on(() => {
|
||||
this._isEmptySyncedDoc = isEmptyDoc(this.syncedDoc, this.editorMode);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override firstUpdated() {
|
||||
this.disposables.addFromEvent(this, 'click', e => {
|
||||
e.stopPropagation();
|
||||
if (this._isClickAtBorder(e, this)) {
|
||||
e.preventDefault();
|
||||
this._selectBlock();
|
||||
}
|
||||
});
|
||||
|
||||
// Forward docLinkClicked event from the synced doc
|
||||
const refNodeProvider =
|
||||
this.syncedDocEditorHost?.std.getOptional(RefNodeSlotsProvider);
|
||||
if (refNodeProvider) {
|
||||
this.disposables.add(
|
||||
refNodeProvider.docLinkClicked.on(args => {
|
||||
this.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.emit(args);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this._initEdgelessFitEffect();
|
||||
}
|
||||
|
||||
override renderBlock() {
|
||||
delete this.dataset.nestedEditor;
|
||||
|
||||
const syncedDoc = this.syncedDoc;
|
||||
const { isLoading, isError, isDeleted, isCycle } = this.blockState;
|
||||
const isCardOnly = this.depth >= 1;
|
||||
|
||||
if (
|
||||
isLoading ||
|
||||
isError ||
|
||||
isDeleted ||
|
||||
isCardOnly ||
|
||||
isCycle ||
|
||||
!syncedDoc
|
||||
) {
|
||||
return this.renderEmbed(
|
||||
() => html`
|
||||
<affine-embed-synced-doc-card
|
||||
style=${this.cardStyleMap}
|
||||
.block=${this}
|
||||
></affine-embed-synced-doc-card>
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
return this._renderSyncedView();
|
||||
}
|
||||
|
||||
override updated(changedProperties: PropertyValues) {
|
||||
super.updated(changedProperties);
|
||||
this.syncedDocCard?.requestUpdate();
|
||||
}
|
||||
|
||||
@state()
|
||||
private accessor _cycle = false;
|
||||
|
||||
@state()
|
||||
private accessor _deleted = false;
|
||||
|
||||
@state()
|
||||
private accessor _docUpdatedAt: Date = new Date();
|
||||
|
||||
@state()
|
||||
private accessor _error = false;
|
||||
|
||||
@state()
|
||||
protected accessor _isEmptySyncedDoc: boolean = true;
|
||||
|
||||
@state()
|
||||
private accessor _loading = false;
|
||||
|
||||
@state()
|
||||
accessor depth = 0;
|
||||
|
||||
@query(
|
||||
':scope > .affine-block-component > .embed-block-container > affine-embed-synced-doc-card'
|
||||
)
|
||||
accessor syncedDocCard: EmbedSyncedDocCard | null = null;
|
||||
|
||||
@query(
|
||||
':scope > .affine-block-component > .embed-block-container > .affine-embed-synced-doc-container > .affine-embed-synced-doc-editor > div > editor-host'
|
||||
)
|
||||
accessor syncedDocEditorHost: EditorHost | null = null;
|
||||
|
||||
@state()
|
||||
accessor syncedDocMode: DocMode = 'page';
|
||||
|
||||
override accessor useCaptionEditor = true;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { EmbedSyncedDocBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockService } from '@blocksuite/block-std';
|
||||
|
||||
export class EmbedSyncedDocBlockService extends BlockService {
|
||||
static override readonly flavour = EmbedSyncedDocBlockSchema.model.flavour;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
BlockViewExtension,
|
||||
type ExtensionType,
|
||||
FlavourExtension,
|
||||
} from '@blocksuite/block-std';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { EmbedSyncedDocBlockService } from './embed-synced-doc-service.js';
|
||||
|
||||
export const EmbedSyncedDocBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:embed-synced-doc'),
|
||||
EmbedSyncedDocBlockService,
|
||||
BlockViewExtension('affine:embed-synced-doc', model => {
|
||||
return model.parent?.flavour === 'affine:surface'
|
||||
? literal`affine-embed-edgeless-synced-doc-block`
|
||||
: literal`affine-embed-synced-doc-block`;
|
||||
}),
|
||||
];
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './adapters/index.js';
|
||||
export * from './embed-synced-doc-block.js';
|
||||
export * from './embed-synced-doc-spec.js';
|
||||
export { SYNCED_MIN_HEIGHT, SYNCED_MIN_WIDTH } from './styles.js';
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
DarkLoadingIcon,
|
||||
EmbedEdgelessIcon,
|
||||
EmbedPageIcon,
|
||||
LightLoadingIcon,
|
||||
ReloadIcon,
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import { ColorScheme } from '@blocksuite/affine-model';
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
import {
|
||||
DarkSyncedDocDeletedBanner,
|
||||
DarkSyncedDocEmptyBanner,
|
||||
DarkSyncedDocErrorBanner,
|
||||
LightSyncedDocDeletedBanner,
|
||||
LightSyncedDocEmptyBanner,
|
||||
LightSyncedDocErrorBanner,
|
||||
SyncedDocDeletedIcon,
|
||||
SyncedDocErrorIcon,
|
||||
} from './styles.js';
|
||||
|
||||
type SyncedCardImages = {
|
||||
LoadingIcon: TemplateResult<1>;
|
||||
SyncedDocIcon: TemplateResult<1>;
|
||||
SyncedDocErrorIcon: TemplateResult<1>;
|
||||
SyncedDocDeletedIcon: TemplateResult<1>;
|
||||
ReloadIcon: TemplateResult<1>;
|
||||
SyncedDocEmptyBanner: TemplateResult<1>;
|
||||
SyncedDocErrorBanner: TemplateResult<1>;
|
||||
SyncedDocDeletedBanner: TemplateResult<1>;
|
||||
};
|
||||
|
||||
export function getSyncedDocIcons(
|
||||
theme: ColorScheme,
|
||||
editorMode: 'page' | 'edgeless'
|
||||
): SyncedCardImages {
|
||||
if (theme === ColorScheme.Light) {
|
||||
return {
|
||||
LoadingIcon: LightLoadingIcon,
|
||||
SyncedDocIcon: editorMode === 'page' ? EmbedPageIcon : EmbedEdgelessIcon,
|
||||
SyncedDocErrorIcon,
|
||||
SyncedDocDeletedIcon,
|
||||
ReloadIcon,
|
||||
SyncedDocEmptyBanner: LightSyncedDocEmptyBanner,
|
||||
SyncedDocErrorBanner: LightSyncedDocErrorBanner,
|
||||
SyncedDocDeletedBanner: LightSyncedDocDeletedBanner,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
LoadingIcon: DarkLoadingIcon,
|
||||
SyncedDocIcon: editorMode === 'page' ? EmbedPageIcon : EmbedEdgelessIcon,
|
||||
SyncedDocErrorIcon,
|
||||
SyncedDocDeletedIcon,
|
||||
ReloadIcon,
|
||||
SyncedDocEmptyBanner: DarkSyncedDocEmptyBanner,
|
||||
SyncedDocErrorBanner: DarkSyncedDocErrorBanner,
|
||||
SyncedDocDeletedBanner: DarkSyncedDocDeletedBanner,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user