mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 12:55:00 +00:00
feat(editor): append note to page button (#9762)
Close [BS-2310](https://linear.app/affine-design/issue/BS-2310/note-display-in-page-%E7%9A%84%E8%A1%8C%E4%B8%BA), [BS-2312](https://linear.app/affine-design/issue/BS-2312/edgeless-note-%E7%9A%84-element-toolbar-%E6%B7%BB%E5%8A%A0display-in-page%E6%8C%89%E9%92%AE) and [BS-2313](https://linear.app/affine-design/issue/BS-2313/添加display-in-page的toast提示,以及打开toc的按钮)
This commit is contained in:
@@ -47,6 +47,7 @@
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.6",
|
||||
"@vanilla-extract/css": "^1.17.0",
|
||||
"date-fns": "^4.0.0",
|
||||
"dompurify": "^3.1.6",
|
||||
"fflate": "^0.8.2",
|
||||
|
||||
@@ -30,16 +30,18 @@ import {
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
FeatureFlagService,
|
||||
NotificationProvider,
|
||||
SidebarExtensionIdentifier,
|
||||
ThemeProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { matchFlavours } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
assertExists,
|
||||
Bound,
|
||||
countBy,
|
||||
maxBy,
|
||||
WithDisposable,
|
||||
} from '@blocksuite/global/utils';
|
||||
import { LinkedPageIcon } from '@blocksuite/icons/lit';
|
||||
import { html, LitElement, nothing, type TemplateResult } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { join } from 'lit/directives/join.js';
|
||||
@@ -52,6 +54,7 @@ import {
|
||||
} from '../../edgeless/components/panel/line-styles-panel.js';
|
||||
import { getTooltipWithShortcut } from '../../edgeless/components/utils.js';
|
||||
import type { EdgelessRootBlockComponent } from '../../edgeless/edgeless-root-block.js';
|
||||
import * as styles from './styles.css';
|
||||
|
||||
const SIZE_LIST = [
|
||||
{ name: 'None', value: 0 },
|
||||
@@ -141,15 +144,17 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
|
||||
.getFlag('enable_advanced_block_visibility');
|
||||
}
|
||||
|
||||
private get _pageBlockHeaderEnabled() {
|
||||
return this.doc.get(FeatureFlagService).getFlag('enable_page_block_header');
|
||||
}
|
||||
|
||||
private get doc() {
|
||||
return this.edgeless.doc;
|
||||
}
|
||||
|
||||
private get _enableAutoHeight() {
|
||||
return !(
|
||||
this.edgeless.doc
|
||||
.get(FeatureFlagService)
|
||||
.getFlag('enable_page_block_header') &&
|
||||
this._pageBlockHeaderEnabled &&
|
||||
this.notes.length === 1 &&
|
||||
this.notes[0].parent?.children.find(child =>
|
||||
matchFlavours(child, ['affine:note'])
|
||||
@@ -195,13 +200,16 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.doc.captureSync();
|
||||
|
||||
this.crud.updateElement(note.id, { displayMode: newMode });
|
||||
|
||||
const noteParent = this.doc.getParent(note);
|
||||
assertExists(noteParent);
|
||||
if (!noteParent) return;
|
||||
|
||||
const noteParentChildNotes = noteParent.children.filter(block =>
|
||||
matchFlavours(block, ['affine:note'])
|
||||
) as NoteBlockModel[];
|
||||
);
|
||||
const noteParentLastNote =
|
||||
noteParentChildNotes[noteParentChildNotes.length - 1];
|
||||
|
||||
@@ -218,6 +226,61 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
|
||||
if (newMode === NoteDisplayMode.DocOnly) {
|
||||
this.edgeless.service.selection.clear();
|
||||
}
|
||||
|
||||
const abortController = new AbortController();
|
||||
const clear = () => {
|
||||
this.doc.history.off('stack-item-added', addHandler);
|
||||
this.doc.history.off('stack-item-popped', popHandler);
|
||||
disposable.dispose();
|
||||
};
|
||||
const closeNotify = () => {
|
||||
abortController.abort();
|
||||
clear();
|
||||
};
|
||||
|
||||
const addHandler = this.doc.history.on('stack-item-added', closeNotify);
|
||||
const popHandler = this.doc.history.on('stack-item-popped', closeNotify);
|
||||
const disposable = this.edgeless.std.host.slots.unmounted.on(closeNotify);
|
||||
|
||||
const undo = () => {
|
||||
this.doc.undo();
|
||||
closeNotify();
|
||||
};
|
||||
|
||||
const viewInToc = () => {
|
||||
const sidebar = this.edgeless.std.getOptional(SidebarExtensionIdentifier);
|
||||
sidebar?.open('outline');
|
||||
closeNotify();
|
||||
};
|
||||
|
||||
const notification = this.edgeless.std.getOptional(NotificationProvider);
|
||||
notification?.notify({
|
||||
title: 'Note displayed in Page Mode',
|
||||
message:
|
||||
'Content added to your page. Find it in the TOC for quick navigation.',
|
||||
accent: 'success',
|
||||
duration: 1000 * 1000,
|
||||
footer: html`<div class=${styles.viewInPageNotifyFooter}>
|
||||
<button
|
||||
class=${styles.viewInPageNotifyFooterButton}
|
||||
@click=${undo}
|
||||
data-testid="undo-display-in-page"
|
||||
>
|
||||
Undo
|
||||
</button>
|
||||
<button
|
||||
class=${styles.viewInPageNotifyFooterButton}
|
||||
@click=${viewInToc}
|
||||
data-testid="view-in-toc"
|
||||
>
|
||||
View in Toc
|
||||
</button>
|
||||
</div>`,
|
||||
abort: abortController.signal,
|
||||
onClose: () => {
|
||||
clear();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _setShadowType(shadowType: string) {
|
||||
@@ -286,6 +349,11 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
|
||||
const currentMode = DisplayModeMap[displayMode];
|
||||
const onlyOne = len === 1;
|
||||
const isDocOnly = displayMode === NoteDisplayMode.DocOnly;
|
||||
const isFirstNote =
|
||||
onlyOne &&
|
||||
note.parent?.children.find(child =>
|
||||
matchFlavours(child, ['affine:note'])
|
||||
) === note;
|
||||
const theme = this.edgeless.std.get(ThemeProvider).theme;
|
||||
const buttons = [
|
||||
onlyOne && this._advancedVisibilityEnabled
|
||||
@@ -315,6 +383,32 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
|
||||
`
|
||||
: nothing,
|
||||
|
||||
onlyOne &&
|
||||
!isFirstNote &&
|
||||
this._pageBlockHeaderEnabled &&
|
||||
!this._advancedVisibilityEnabled
|
||||
? html`<editor-icon-button
|
||||
aria-label="Display In Page"
|
||||
.showTooltip=${displayMode === NoteDisplayMode.DocAndEdgeless}
|
||||
.tooltip=${'This note is part of Page Mode. Click to remove it from the page.'}
|
||||
data-testid="display-in-page"
|
||||
@click=${() =>
|
||||
this._setDisplayMode(
|
||||
note,
|
||||
displayMode === NoteDisplayMode.EdgelessOnly
|
||||
? NoteDisplayMode.DocAndEdgeless
|
||||
: NoteDisplayMode.EdgelessOnly
|
||||
)}
|
||||
>
|
||||
${LinkedPageIcon({ width: '20px', height: '20px' })}
|
||||
<span class="label"
|
||||
>${displayMode === NoteDisplayMode.EdgelessOnly
|
||||
? 'Display In Page'
|
||||
: 'Displayed In Page'}</span
|
||||
>
|
||||
</editor-icon-button>`
|
||||
: nothing,
|
||||
|
||||
isDocOnly
|
||||
? nothing
|
||||
: when(
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const viewInPageNotifyFooter = style({
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
gap: '12px',
|
||||
});
|
||||
|
||||
export const viewInPageNotifyFooterButton = style({
|
||||
padding: '0px 6px',
|
||||
borderRadius: '4px',
|
||||
color: cssVarV2('text/primary'),
|
||||
|
||||
fontSize: cssVar('fontSm'),
|
||||
lineHeight: '22px',
|
||||
fontWeight: '500',
|
||||
textAlign: 'center',
|
||||
|
||||
':hover': {
|
||||
background: cssVarV2('layer/background/hoverOverlay'),
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user