refactor(editor): configurable page block title (#10063)
### What changes - make page block title rendering configurable so that a journal title can be rendered by AFFiNE side. - move page block render logic to a seperate component
This commit is contained in:
@@ -1,13 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
DefaultTheme,
|
DefaultTheme,
|
||||||
NoteBlockModel,
|
NoteBlockModel,
|
||||||
NoteDisplayMode,
|
|
||||||
StrokeStyle,
|
StrokeStyle,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import {
|
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||||
FeatureFlagService,
|
|
||||||
ThemeProvider,
|
|
||||||
} from '@blocksuite/affine-shared/services';
|
|
||||||
import {
|
import {
|
||||||
getClosestBlockComponentByPoint,
|
getClosestBlockComponentByPoint,
|
||||||
handleNativeRangeAtPoint,
|
handleNativeRangeAtPoint,
|
||||||
@@ -37,6 +33,7 @@ import { html, nothing } from 'lit';
|
|||||||
import { property } from 'lit/decorators.js';
|
import { property } from 'lit/decorators.js';
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
|
|
||||||
|
import { isPageBlock } from '../utils';
|
||||||
import * as styles from './edgeless-note-background.css';
|
import * as styles from './edgeless-note-background.css';
|
||||||
|
|
||||||
@requiredProperties({
|
@requiredProperties({
|
||||||
@@ -74,18 +71,6 @@ export class EdgelessNoteBackground extends SignalWatcher(
|
|||||||
return this.std.host.doc;
|
return this.std.host.doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _isPageBlock() {
|
|
||||||
return (
|
|
||||||
this.std.get(FeatureFlagService).getFlag('enable_page_block') &&
|
|
||||||
// is the first page visible note
|
|
||||||
this.note.parent?.children.find(
|
|
||||||
child =>
|
|
||||||
matchFlavours(child, ['affine:note']) &&
|
|
||||||
child.displayMode !== NoteDisplayMode.EdgelessOnly
|
|
||||||
) === this.note
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _tryAddParagraph(x: number, y: number) {
|
private _tryAddParagraph(x: number, y: number) {
|
||||||
const nearest = getClosestBlockComponentByPoint(
|
const nearest = getClosestBlockComponentByPoint(
|
||||||
new Point(x, y)
|
new Point(x, y)
|
||||||
@@ -174,7 +159,7 @@ export class EdgelessNoteBackground extends SignalWatcher(
|
|||||||
@pointerdown=${stopPropagation}
|
@pointerdown=${stopPropagation}
|
||||||
@click=${this._handleClickAtBackground}
|
@click=${this._handleClickAtBackground}
|
||||||
>
|
>
|
||||||
${this._isPageBlock ? this._renderHeader() : nothing}
|
${isPageBlock(this.std, this.note) ? this._renderHeader() : nothing}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { globalStyle, style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const pageBlockTitle = style({
|
||||||
|
position: 'relative',
|
||||||
|
});
|
||||||
|
|
||||||
|
globalStyle(`${pageBlockTitle} .doc-title-container`, {
|
||||||
|
padding: '26px 0px',
|
||||||
|
});
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { NoteBlockModel } from '@blocksuite/affine-model';
|
||||||
|
import {
|
||||||
|
type BlockStdScope,
|
||||||
|
PropTypes,
|
||||||
|
requiredProperties,
|
||||||
|
ShadowlessElement,
|
||||||
|
stdContext,
|
||||||
|
} from '@blocksuite/block-std';
|
||||||
|
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||||
|
import { consume } from '@lit/context';
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { property } from 'lit/decorators.js';
|
||||||
|
|
||||||
|
import { isPageBlock } from '../utils';
|
||||||
|
import * as styles from './edgeless-page-block-title.css';
|
||||||
|
|
||||||
|
@requiredProperties({
|
||||||
|
note: PropTypes.instanceOf(NoteBlockModel),
|
||||||
|
})
|
||||||
|
export class EdgelessPageBlockTitle extends SignalWatcher(
|
||||||
|
WithDisposable(ShadowlessElement)
|
||||||
|
) {
|
||||||
|
override render() {
|
||||||
|
if (!isPageBlock(this.std, this.note)) return;
|
||||||
|
|
||||||
|
const title = this.std.getConfig('affine:note')?.pageBlockTitle({
|
||||||
|
note: this.note,
|
||||||
|
std: this.std,
|
||||||
|
});
|
||||||
|
|
||||||
|
return html`<div class=${styles.pageBlockTitle}>${title}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
@consume({ context: stdContext })
|
||||||
|
accessor std!: BlockStdScope;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor note!: NoteBlockModel;
|
||||||
|
}
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'edgeless-page-block-title': EdgelessPageBlockTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,14 @@ import type { NoteBlockModel } from '@blocksuite/affine-model';
|
|||||||
import { type BlockStdScope, ConfigExtension } from '@blocksuite/block-std';
|
import { type BlockStdScope, ConfigExtension } from '@blocksuite/block-std';
|
||||||
import type { TemplateResult } from 'lit';
|
import type { TemplateResult } from 'lit';
|
||||||
|
|
||||||
|
type NoteBlockContext = {
|
||||||
|
note: NoteBlockModel;
|
||||||
|
std: BlockStdScope;
|
||||||
|
};
|
||||||
|
|
||||||
export type NoteConfig = {
|
export type NoteConfig = {
|
||||||
edgelessNoteHeader: (context: {
|
edgelessNoteHeader: (context: NoteBlockContext) => TemplateResult;
|
||||||
note: NoteBlockModel;
|
pageBlockTitle: (context: NoteBlockContext) => TemplateResult;
|
||||||
std: BlockStdScope;
|
|
||||||
}) => TemplateResult;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function NoteConfigExtension(config: NoteConfig) {
|
export function NoteConfigExtension(config: NoteConfig) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { EdgelessNoteBackground } from './components/edgeless-note-background';
|
import { EdgelessNoteBackground } from './components/edgeless-note-background';
|
||||||
import { EdgelessNoteMask } from './components/edgeless-note-mask';
|
import { EdgelessNoteMask } from './components/edgeless-note-mask';
|
||||||
|
import { EdgelessPageBlockTitle } from './components/edgeless-page-block-title';
|
||||||
import type { NoteConfig } from './config';
|
import type { NoteConfig } from './config';
|
||||||
import { NoteBlockComponent } from './note-block';
|
import { NoteBlockComponent } from './note-block';
|
||||||
import {
|
import {
|
||||||
@@ -13,6 +14,7 @@ export function effects() {
|
|||||||
customElements.define(AFFINE_EDGELESS_NOTE, EdgelessNoteBlockComponent);
|
customElements.define(AFFINE_EDGELESS_NOTE, EdgelessNoteBlockComponent);
|
||||||
customElements.define('edgeless-note-mask', EdgelessNoteMask);
|
customElements.define('edgeless-note-mask', EdgelessNoteMask);
|
||||||
customElements.define('edgeless-note-background', EdgelessNoteBackground);
|
customElements.define('edgeless-note-background', EdgelessNoteBackground);
|
||||||
|
customElements.define('edgeless-page-block-title', EdgelessPageBlockTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine-shared/consts';
|
import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine-shared/consts';
|
||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
import { globalStyle, style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
export const ACTIVE_NOTE_EXTRA_PADDING = 20;
|
export const ACTIVE_NOTE_EXTRA_PADDING = 20;
|
||||||
|
|
||||||
@@ -58,14 +58,6 @@ export const noteBackground = style({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
globalStyle(`${edgelessNoteContainer} > doc-title`, {
|
|
||||||
position: 'relative',
|
|
||||||
});
|
|
||||||
|
|
||||||
globalStyle(`${edgelessNoteContainer} > doc-title .doc-title-container`, {
|
|
||||||
padding: '26px 0px',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const pageContent = style({
|
export const pageContent = style({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
|||||||
@@ -3,11 +3,7 @@ import type { DocTitle } from '@blocksuite/affine-components/doc-title';
|
|||||||
import { MoreIndicatorIcon } from '@blocksuite/affine-components/icons';
|
import { MoreIndicatorIcon } from '@blocksuite/affine-components/icons';
|
||||||
import { NoteDisplayMode } from '@blocksuite/affine-model';
|
import { NoteDisplayMode } from '@blocksuite/affine-model';
|
||||||
import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine-shared/consts';
|
import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine-shared/consts';
|
||||||
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
import { stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||||
import {
|
|
||||||
matchFlavours,
|
|
||||||
stopPropagation,
|
|
||||||
} from '@blocksuite/affine-shared/utils';
|
|
||||||
import { toGfxBlockComponent } from '@blocksuite/block-std';
|
import { toGfxBlockComponent } from '@blocksuite/block-std';
|
||||||
import { Bound } from '@blocksuite/global/utils';
|
import { Bound } from '@blocksuite/global/utils';
|
||||||
import { html, nothing } from 'lit';
|
import { html, nothing } from 'lit';
|
||||||
@@ -19,19 +15,13 @@ import { styleMap } from 'lit/directives/style-map.js';
|
|||||||
import { NoteBlockComponent } from './note-block';
|
import { NoteBlockComponent } from './note-block';
|
||||||
import { ACTIVE_NOTE_EXTRA_PADDING } from './note-edgeless-block.css';
|
import { ACTIVE_NOTE_EXTRA_PADDING } from './note-edgeless-block.css';
|
||||||
import * as styles from './note-edgeless-block.css';
|
import * as styles from './note-edgeless-block.css';
|
||||||
|
import { isPageBlock } from './utils';
|
||||||
|
|
||||||
export const AFFINE_EDGELESS_NOTE = 'affine-edgeless-note';
|
export const AFFINE_EDGELESS_NOTE = 'affine-edgeless-note';
|
||||||
|
|
||||||
export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||||
NoteBlockComponent
|
NoteBlockComponent
|
||||||
) {
|
) {
|
||||||
private get _isPageBlock() {
|
|
||||||
return (
|
|
||||||
this.std.get(FeatureFlagService).getFlag('enable_page_block') &&
|
|
||||||
this._isFirstVisibleNote()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _isShowCollapsedContent() {
|
private get _isShowCollapsedContent() {
|
||||||
return (
|
return (
|
||||||
this.model.edgeless.collapse &&
|
this.model.edgeless.collapse &&
|
||||||
@@ -88,7 +78,7 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleKeyDown(e: KeyboardEvent) {
|
private _handleKeyDown(e: KeyboardEvent) {
|
||||||
if (e.key === 'ArrowUp' && this._isPageBlock) {
|
if (e.key === 'ArrowUp') {
|
||||||
this._docTitle?.inlineEditor?.focusEnd();
|
this._docTitle?.inlineEditor?.focusEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,16 +93,6 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _isFirstVisibleNote() {
|
|
||||||
return (
|
|
||||||
this.model.parent?.children.find(
|
|
||||||
child =>
|
|
||||||
matchFlavours(child, ['affine:note']) &&
|
|
||||||
child.displayMode !== NoteDisplayMode.EdgelessOnly
|
|
||||||
) === this.model
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _leaved() {
|
private _leaved() {
|
||||||
if (this._isHover) {
|
if (this._isHover) {
|
||||||
this._isHover = false;
|
this._isHover = false;
|
||||||
@@ -259,12 +239,9 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
|||||||
.note=${this.model}
|
.note=${this.model}
|
||||||
></edgeless-note-background>
|
></edgeless-note-background>
|
||||||
|
|
||||||
${this._isPageBlock && !collapse
|
<edgeless-page-block-title
|
||||||
? html`<doc-title
|
.note=${this.model}
|
||||||
.doc=${this.doc}
|
></edgeless-page-block-title>
|
||||||
.wrapText=${!collapse}
|
|
||||||
></doc-title>`
|
|
||||||
: nothing}
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="edgeless-note-page-content"
|
class="edgeless-note-page-content"
|
||||||
@@ -284,7 +261,7 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
|||||||
.editing=${this._editing}
|
.editing=${this._editing}
|
||||||
></edgeless-note-mask>
|
></edgeless-note-mask>
|
||||||
|
|
||||||
${isCollapsable && !this._isPageBlock
|
${isCollapsable && !isPageBlock(this.std, this.model)
|
||||||
? html`<div
|
? html`<div
|
||||||
class="${classMap({
|
class="${classMap({
|
||||||
[styles.collapseButton]: true,
|
[styles.collapseButton]: true,
|
||||||
|
|||||||
18
blocksuite/affine/block-note/src/utils.ts
Normal file
18
blocksuite/affine/block-note/src/utils.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { type NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model';
|
||||||
|
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
||||||
|
import { matchFlavours } from '@blocksuite/affine-shared/utils';
|
||||||
|
import type { BlockStdScope } from '@blocksuite/block-std';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We define a note block as a page block if it is the first visible note
|
||||||
|
*/
|
||||||
|
export function isPageBlock(std: BlockStdScope, note: NoteBlockModel) {
|
||||||
|
return (
|
||||||
|
std.get(FeatureFlagService).getFlag('enable_page_block') &&
|
||||||
|
note.parent?.children.find(
|
||||||
|
child =>
|
||||||
|
matchFlavours(child, ['affine:note']) &&
|
||||||
|
child.displayMode !== NoteDisplayMode.EdgelessOnly
|
||||||
|
) === note
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -156,7 +156,7 @@ const usePatchSpecs = (mode: DocMode) => {
|
|||||||
builder.extend(
|
builder.extend(
|
||||||
[
|
[
|
||||||
patchReferenceRenderer(reactToLit, referenceRenderer),
|
patchReferenceRenderer(reactToLit, referenceRenderer),
|
||||||
patchForEdgelessNoteConfig(reactToLit),
|
patchForEdgelessNoteConfig(framework, reactToLit),
|
||||||
patchNotificationService(confirmModal),
|
patchNotificationService(confirmModal),
|
||||||
patchPeekViewService(peekViewService),
|
patchPeekViewService(peekViewService),
|
||||||
patchOpenDocExtension(),
|
patchOpenDocExtension(),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { DesktopApiService } from '@affine/core/modules/desktop-api';
|
|||||||
import { type DocService, DocsService } from '@affine/core/modules/doc';
|
import { type DocService, DocsService } from '@affine/core/modules/doc';
|
||||||
import type { EditorService } from '@affine/core/modules/editor';
|
import type { EditorService } from '@affine/core/modules/editor';
|
||||||
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
||||||
|
import { JournalService } from '@affine/core/modules/journal';
|
||||||
import { resolveLinkToDoc } from '@affine/core/modules/navigation';
|
import { resolveLinkToDoc } from '@affine/core/modules/navigation';
|
||||||
import type { PeekViewService } from '@affine/core/modules/peek-view';
|
import type { PeekViewService } from '@affine/core/modules/peek-view';
|
||||||
import {
|
import {
|
||||||
@@ -79,7 +80,7 @@ import {
|
|||||||
SplitViewIcon,
|
SplitViewIcon,
|
||||||
} from '@blocksuite/icons/lit';
|
} from '@blocksuite/icons/lit';
|
||||||
import { type FrameworkProvider } from '@toeverything/infra';
|
import { type FrameworkProvider } from '@toeverything/infra';
|
||||||
import { type TemplateResult } from 'lit';
|
import { html, type TemplateResult } from 'lit';
|
||||||
import { customElement } from 'lit/decorators.js';
|
import { customElement } from 'lit/decorators.js';
|
||||||
import { literal } from 'lit/static-html.js';
|
import { literal } from 'lit/static-html.js';
|
||||||
import { pick } from 'lodash-es';
|
import { pick } from 'lodash-es';
|
||||||
@@ -87,6 +88,7 @@ import { pick } from 'lodash-es';
|
|||||||
import type { DocProps } from '../../../../../blocksuite/initialization';
|
import type { DocProps } from '../../../../../blocksuite/initialization';
|
||||||
import { AttachmentEmbedPreview } from '../../../../attachment-viewer/pdf-viewer-embedded';
|
import { AttachmentEmbedPreview } from '../../../../attachment-viewer/pdf-viewer-embedded';
|
||||||
import { generateUrl } from '../../../../hooks/affine/use-share-url';
|
import { generateUrl } from '../../../../hooks/affine/use-share-url';
|
||||||
|
import { BlocksuiteEditorJournalDocTitle } from '../../journal-doc-title';
|
||||||
import { EdgelessNoteHeader } from './widgets/edgeless-note-header';
|
import { EdgelessNoteHeader } from './widgets/edgeless-note-header';
|
||||||
import { createKeyboardToolbarConfig } from './widgets/keyboard-toolbar';
|
import { createKeyboardToolbarConfig } from './widgets/keyboard-toolbar';
|
||||||
|
|
||||||
@@ -656,11 +658,21 @@ export function patchForClipboardInElectron(framework: FrameworkProvider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function patchForEdgelessNoteConfig(
|
export function patchForEdgelessNoteConfig(
|
||||||
|
framework: FrameworkProvider,
|
||||||
reactToLit: (element: ElementOrFactory) => TemplateResult
|
reactToLit: (element: ElementOrFactory) => TemplateResult
|
||||||
) {
|
) {
|
||||||
return NoteConfigExtension({
|
return NoteConfigExtension({
|
||||||
edgelessNoteHeader: ({ note }) =>
|
edgelessNoteHeader: ({ note }) =>
|
||||||
reactToLit(<EdgelessNoteHeader note={note} />),
|
reactToLit(<EdgelessNoteHeader note={note} />),
|
||||||
|
pageBlockTitle: ({ note }) => {
|
||||||
|
const journalService = framework.get(JournalService);
|
||||||
|
const isJournal = !!journalService.journalDate$(note.doc.id).value;
|
||||||
|
if (isJournal) {
|
||||||
|
return reactToLit(<BlocksuiteEditorJournalDocTitle page={note.doc} />);
|
||||||
|
} else {
|
||||||
|
return html`<doc-title .doc=${note.doc}></doc-title>`;
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user