mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-06 11:59:51 +08:00
chore: merge blocksuite source code (#9213)
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import {
|
||||
CloseIcon,
|
||||
createDefaultDoc,
|
||||
GenerateDocUrlProvider,
|
||||
} from '@blocksuite/blocks';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import type { AffineEditorContainer } from '@blocksuite/presets';
|
||||
import type { BlockCollection, DocCollection } from '@blocksuite/store';
|
||||
import { css, html, nothing } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { removeModeFromStorage } from '../mock-services.js';
|
||||
|
||||
@customElement('docs-panel')
|
||||
export class DocsPanel extends WithDisposable(ShadowlessElement) {
|
||||
static override styles = css`
|
||||
docs-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
background-color: var(--affine-background-secondary-color);
|
||||
font-family: var(--affine-font-family);
|
||||
height: 100%;
|
||||
padding: 12px;
|
||||
gap: 4px;
|
||||
}
|
||||
.doc-item:hover .delete-doc-icon {
|
||||
display: flex;
|
||||
}
|
||||
.doc-item {
|
||||
color: var(--affine-text-primary-color);
|
||||
}
|
||||
.delete-doc-icon {
|
||||
display: none;
|
||||
padding: 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.delete-doc-icon:hover {
|
||||
background-color: var(--affine-hover-color);
|
||||
}
|
||||
.delete-doc-icon svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: var(--affine-secondary-color);
|
||||
fill: var(--affine-secondary-color);
|
||||
}
|
||||
.new-doc-button {
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid var(--affine-border-color);
|
||||
border-radius: 4px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: var(--affine-text-primary-color);
|
||||
}
|
||||
.new-doc-button:hover {
|
||||
background-color: var(--affine-hover-color);
|
||||
}
|
||||
`;
|
||||
|
||||
createDoc = () => {
|
||||
createDocBlock(this.editor.doc.collection);
|
||||
};
|
||||
|
||||
gotoDoc = (doc: BlockCollection) => {
|
||||
const url = this.editor.std
|
||||
.getOptional(GenerateDocUrlProvider)
|
||||
?.generateDocUrl(doc.id);
|
||||
if (url) history.pushState({}, '', url);
|
||||
|
||||
this.editor.doc = doc.getDoc();
|
||||
this.editor.doc.load();
|
||||
this.editor.doc.resetHistory();
|
||||
this.requestUpdate();
|
||||
};
|
||||
|
||||
private get collection() {
|
||||
return this.editor.doc.collection;
|
||||
}
|
||||
|
||||
private get docs() {
|
||||
return [...this.collection.docs.values()];
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (!(event.target instanceof Node)) return;
|
||||
|
||||
const toggleButton = document.querySelector(
|
||||
'sl-button[data-docs-panel-toggle]'
|
||||
);
|
||||
if (toggleButton?.contains(event.target as Node)) return;
|
||||
|
||||
if (!this.contains(event.target)) {
|
||||
this.onClose?.();
|
||||
}
|
||||
};
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
this.disposables.add(() => {
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
});
|
||||
});
|
||||
|
||||
this.disposables.add(
|
||||
this.editor.doc.collection.slots.docUpdated.on(() => {
|
||||
this.requestUpdate();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected override render(): unknown {
|
||||
const { docs, collection } = this;
|
||||
return html`
|
||||
<div @click="${this.createDoc}" class="new-doc-button">New Doc</div>
|
||||
${repeat(
|
||||
docs,
|
||||
v => v.id,
|
||||
doc => {
|
||||
const style = styleMap({
|
||||
backgroundColor:
|
||||
this.editor.doc.id === doc.id
|
||||
? 'var(--affine-hover-color)'
|
||||
: undefined,
|
||||
padding: '4px 4px 4px 8px',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
});
|
||||
const click = () => {
|
||||
this.gotoDoc(doc);
|
||||
};
|
||||
const deleteDoc = (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const isDeleteCurrent = doc.id === this.editor.doc.id;
|
||||
|
||||
collection.removeDoc(doc.id);
|
||||
removeModeFromStorage(doc.id);
|
||||
// When delete the current doc, we need to set the editor doc to the first remaining doc
|
||||
if (isDeleteCurrent) {
|
||||
this.editor.doc = this.docs[0].getDoc();
|
||||
}
|
||||
};
|
||||
return html`<div class="doc-item" @click="${click}" style="${style}">
|
||||
${doc.meta?.title || 'Untitled'}
|
||||
${docs.length > 1
|
||||
? html`<div @click="${deleteDoc}" class="delete-doc-icon">
|
||||
${CloseIcon}
|
||||
</div>`
|
||||
: nothing}
|
||||
</div>`;
|
||||
}
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor editor!: AffineEditorContainer;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onClose!: () => void;
|
||||
}
|
||||
|
||||
function createDocBlock(collection: DocCollection) {
|
||||
const id = collection.idGenerator();
|
||||
createDefaultDoc(collection, { id });
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'docs-panel': DocsPanel;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user