mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-17 14:27:02 +08:00
feat(editor): edgeless page block toolbar (#9707)
Close [BS-2315](https://linear.app/affine-design/issue/BS-2315/page-block-header) ### What Changes - Add header toolbar to page block (the first note in canvas) - Add e2e tests - Add some edgeless e2e test utils. **The package `@blocksuite/affine` was added to `"@affine-test/kit"`**
This commit is contained in:
14
blocksuite/affine/block-note/src/config.ts
Normal file
14
blocksuite/affine/block-note/src/config.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { NoteBlockModel } from '@blocksuite/affine-model';
|
||||
import { type BlockStdScope, ConfigExtension } from '@blocksuite/block-std';
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
export type NoteConfig = {
|
||||
edgelessNoteHeader: (context: {
|
||||
note: NoteBlockModel;
|
||||
std: BlockStdScope;
|
||||
}) => TemplateResult;
|
||||
};
|
||||
|
||||
export function NoteConfigExtension(config: NoteConfig) {
|
||||
return ConfigExtension('affine:note', config);
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import type { indentBlock } from './commands/indent-block';
|
||||
import type { indentBlocks } from './commands/indent-blocks';
|
||||
import type { selectBlock } from './commands/select-block';
|
||||
import type { selectBlocksBetween } from './commands/select-blocks-between';
|
||||
import type { NoteConfig } from './config';
|
||||
import { NoteBlockComponent } from './note-block';
|
||||
import {
|
||||
EdgelessNoteBlockComponent,
|
||||
@@ -48,5 +49,8 @@ declare global {
|
||||
interface BlockServices {
|
||||
'affine:note': NoteBlockService;
|
||||
}
|
||||
interface BlockConfigs {
|
||||
'affine:note': NoteConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './adapters';
|
||||
export * from './commands';
|
||||
export * from './config';
|
||||
export * from './note-block';
|
||||
export * from './note-edgeless-block';
|
||||
export * from './note-service';
|
||||
|
||||
@@ -7,7 +7,10 @@ import {
|
||||
StrokeStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine-shared/consts';
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
FeatureFlagService,
|
||||
ThemeProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
getClosestBlockComponentByPoint,
|
||||
handleNativeRangeAtPoint,
|
||||
@@ -77,7 +80,7 @@ export class EdgelessNoteMask extends WithDisposable(ShadowlessElement) {
|
||||
bottom: `${-extra}px`,
|
||||
right: `${-extra}px`,
|
||||
zIndex: '1',
|
||||
pointerEvents: this.display ? 'auto' : 'none',
|
||||
pointerEvents: this.editing ? 'none' : 'auto',
|
||||
borderRadius: `${
|
||||
this.model.edgeless.style.borderRadius * this.zoom
|
||||
}px`,
|
||||
@@ -86,9 +89,6 @@ export class EdgelessNoteMask extends WithDisposable(ShadowlessElement) {
|
||||
`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor display!: boolean;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor editing!: boolean;
|
||||
|
||||
@@ -128,9 +128,6 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
.edgeless-note-collapse-button.flip {
|
||||
transform: translateX(-50%) rotate(180deg);
|
||||
}
|
||||
.edgeless-note-collapse-button.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.edgeless-note-container:has(.affine-embed-synced-doc-container.editing)
|
||||
> .note-background {
|
||||
@@ -156,8 +153,16 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
);
|
||||
});
|
||||
|
||||
private get _enablePageHeader() {
|
||||
return this.std.get(FeatureFlagService).getFlag('enable_page_block_header');
|
||||
}
|
||||
|
||||
private get _isShowCollapsedContent() {
|
||||
return this.model.edgeless.collapse && (this._isResizing || this._isHover);
|
||||
return (
|
||||
this.model.edgeless.collapse &&
|
||||
this.gfx.selection.has(this.model.id) &&
|
||||
(this._isResizing || this._isHover)
|
||||
);
|
||||
}
|
||||
|
||||
get _zoom() {
|
||||
@@ -240,12 +245,28 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
}
|
||||
}
|
||||
|
||||
private _isFirstNote() {
|
||||
return (
|
||||
this.model.parent?.children.find(child =>
|
||||
matchFlavours(child, ['affine:note'])
|
||||
) === this.model
|
||||
);
|
||||
}
|
||||
|
||||
private _leaved() {
|
||||
if (this._isHover) {
|
||||
this._isHover = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _renderHeader() {
|
||||
const header = this.host.std
|
||||
.getConfig('affine:note')
|
||||
?.edgelessNoteHeader({ note: this.model, std: this.std });
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private _setCollapse(event: MouseEvent) {
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
@@ -466,7 +487,9 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
style=${styleMap(backgroundStyle)}
|
||||
@pointerdown=${stopPropagation}
|
||||
@click=${this._handleClickAtBackground}
|
||||
></div>
|
||||
>
|
||||
${this._enablePageHeader ? this._renderHeader() : nothing}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="edgeless-note-page-content"
|
||||
@@ -479,12 +502,18 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
${this.renderPageContent()}
|
||||
</div>
|
||||
|
||||
${isCollapsable
|
||||
<edgeless-note-mask
|
||||
.model=${this.model}
|
||||
.host=${this.host}
|
||||
.zoom=${this.gfx.viewport.zoom ?? 1}
|
||||
.editing=${this._editing}
|
||||
></edgeless-note-mask>
|
||||
|
||||
${isCollapsable && (!this._isFirstNote() || !this._enablePageHeader)
|
||||
? html`<div
|
||||
class="${classMap({
|
||||
'edgeless-note-collapse-button': true,
|
||||
flip: isCollapseArrowUp,
|
||||
hide: this._isSelected,
|
||||
})}"
|
||||
style=${styleMap({
|
||||
bottom: this._editing ? `${-extra}px` : '0',
|
||||
@@ -497,14 +526,6 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
</div>`
|
||||
: nothing}
|
||||
${this._collapsedContent()}
|
||||
|
||||
<edgeless-note-mask
|
||||
.model=${this.model}
|
||||
.display=${!this._editing}
|
||||
.host=${this.host}
|
||||
.zoom=${this.gfx.viewport.zoom ?? 1}
|
||||
.editing=${this._editing}
|
||||
></edgeless-note-mask>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -518,9 +539,6 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
@state()
|
||||
private accessor _isResizing = false;
|
||||
|
||||
@state()
|
||||
private accessor _isSelected = false;
|
||||
|
||||
@state()
|
||||
private accessor _noteFullHeight = 0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user