Files
AFFiNE-Mirror/blocksuite/affine/blocks/block-surface-ref/src/portal/note.ts
T

165 lines
4.5 KiB
TypeScript

import type { CanvasRenderer } from '@blocksuite/affine-block-surface';
import type { NoteBlockModel } from '@blocksuite/affine-model';
import {
DefaultTheme,
NoteDisplayMode,
NoteShadow,
} from '@blocksuite/affine-model';
import {
EDGELESS_BLOCK_CHILD_BORDER_WIDTH,
EDGELESS_BLOCK_CHILD_PADDING,
} from '@blocksuite/affine-shared/consts';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { SpecProvider } from '@blocksuite/affine-shared/utils';
import {
BlockStdScope,
type EditorHost,
RANGE_QUERY_EXCLUDE_ATTR,
ShadowlessElement,
} from '@blocksuite/block-std';
import { deserializeXYWH } from '@blocksuite/global/gfx';
import { WithDisposable } from '@blocksuite/global/lit';
import { type BlockModel, type Query } from '@blocksuite/store';
import { css, nothing } from 'lit';
import { property } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
import { html } from 'lit/static-html.js';
export class SurfaceRefNotePortal extends WithDisposable(ShadowlessElement) {
static override styles = css`
surface-ref-note-portal {
position: relative;
}
`;
ancestors = new Set<string>();
query: Query | null = null;
override connectedCallback() {
super.connectedCallback();
const ancestors = new Set<string>();
let parent: BlockModel | null = this.model;
while (parent) {
this.ancestors.add(parent.id);
parent = this.model.doc.getParent(parent.id);
}
const query: Query = {
mode: 'include',
match: Array.from(ancestors).map(id => ({
id,
viewType: 'display',
})),
};
this.query = query;
const doc = this.model.doc;
this._disposables.add(() => {
doc.doc.clearQuery(query, true);
});
}
override firstUpdated() {
this.disposables.add(
this.model.propsUpdated.subscribe(() => this.requestUpdate())
);
}
override render() {
const { model, index } = this;
const { displayMode, edgeless } = model;
if (!!displayMode && displayMode === NoteDisplayMode.DocOnly)
return nothing;
const backgroundColor = this.host.std
.get(ThemeProvider)
.generateColorProperty(model.background, DefaultTheme.noteBackgrounColor);
const [modelX, modelY, modelW, modelH] = deserializeXYWH(model.xywh);
const style = {
zIndex: `${index}`,
width: modelW + 'px',
height:
edgeless.collapse && edgeless.collapsedHeight
? edgeless.collapsedHeight + 'px'
: undefined,
transform: `translate(${modelX}px, ${modelY}px)`,
padding: `${EDGELESS_BLOCK_CHILD_PADDING}px`,
border: `${EDGELESS_BLOCK_CHILD_BORDER_WIDTH}px none var(--affine-black-10)`,
backgroundColor,
boxShadow: `var(${NoteShadow.Sticker})`,
position: 'absolute',
borderRadius: '0px',
boxSizing: 'border-box',
pointerEvents: 'none',
overflow: 'hidden',
transformOrigin: '0 0',
userSelect: 'none',
};
return html`
<div
class="surface-ref-note-portal"
style=${styleMap(style)}
data-model-height="${modelH}"
data-portal-reference-block-id="${model.id}"
>
${this.renderPreview()}
</div>
`;
}
renderPreview() {
if (!this.query) {
console.error('Query is not set before rendering note preview');
return nothing;
}
const doc = this.model.doc.doc.getStore({
query: this.query,
readonly: true,
});
const previewSpec = SpecProvider._.getSpec('preview:page');
return new BlockStdScope({
store: doc,
extensions: previewSpec.value.slice(),
}).render();
}
override updated() {
setTimeout(() => {
const editableElements = Array.from<HTMLDivElement>(
this.querySelectorAll('[contenteditable]')
);
const blocks = Array.from(this.querySelectorAll(`[data-block-id]`));
editableElements.forEach(element => {
if (element.contentEditable === 'true')
element.contentEditable = 'false';
});
blocks.forEach(element => {
element.setAttribute(RANGE_QUERY_EXCLUDE_ATTR, 'true');
});
}, 500);
}
@property({ attribute: false })
accessor host!: EditorHost;
@property({ attribute: false })
accessor index!: number;
@property({ attribute: false })
accessor model!: NoteBlockModel;
@property({ attribute: false })
accessor renderer!: CanvasRenderer;
}
declare global {
interface HTMLElementTagNameMap {
'surface-ref-note-portal': SurfaceRefNotePortal;
}
}