feat(editor): add more open doc options to editor toolbar (#9588)

fix AF-2036, AF-2092
This commit is contained in:
pengx17
2025-01-09 08:04:21 +00:00
parent 890a962196
commit f78857bb11
41 changed files with 558 additions and 271 deletions
@@ -13,7 +13,6 @@ import {
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import {
CaptionIcon,
CenterPeekIcon,
CopyIcon,
EditIcon,
ExpandFullSmallIcon,
@@ -44,6 +43,8 @@ import {
GenerateDocUrlProvider,
type GenerateDocUrlService,
type LinkEventType,
OpenDocExtensionIdentifier,
type OpenDocMode,
type TelemetryEvent,
TelemetryProvider,
ThemeProvider,
@@ -266,8 +267,8 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) {
return bound.h / EMBED_CARD_HEIGHT[this.model.style];
};
private readonly _open = () => {
this._blockComponent?.open();
private readonly _open = ({ openMode }: { openMode?: OpenDocMode } = {}) => {
this._blockComponent?.open({ openMode });
};
private readonly _openEditPopup = (e: MouseEvent) => {
@@ -493,12 +494,6 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) {
);
}
get _openButtonDisabled() {
return (
isEmbedLinkedDocBlock(this.model) && this.model.pageId === this._doc.id
);
}
get _originalDocInfo(): AliasInfo | undefined {
const model = this.model;
const doc = isInternalEmbedModel(model)
@@ -543,20 +538,46 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) {
}
private _openMenuButton() {
const buttons: MenuItem[] = [];
const openDocConfig = this.std.get(OpenDocExtensionIdentifier);
const buttons: MenuItem[] = openDocConfig.items
.map(item => {
if (
item.type === 'open-in-center-peek' &&
this._blockComponent &&
!isPeekable(this._blockComponent)
) {
return null;
}
if (
isEmbedLinkedDocBlock(this.model) ||
isEmbedSyncedDocBlock(this.model)
) {
buttons.push({
type: 'open-this-doc',
label: 'Open this doc',
icon: ExpandFullSmallIcon,
action: this._open,
disabled: this._openButtonDisabled,
});
} else if (this._canShowFullScreenButton) {
if (
!(
isEmbedLinkedDocBlock(this.model) ||
isEmbedSyncedDocBlock(this.model)
)
) {
return null;
}
return {
label: item.label,
type: item.type,
icon: item.icon,
disabled:
this.model.pageId === this._doc.id &&
item.type === 'open-in-active-view',
action: () => {
if (item.type === 'open-in-center-peek') {
this._peek();
} else {
this._open({ openMode: item.type });
}
},
};
})
.filter(item => item !== null);
// todo: abstract this?
if (this._canShowFullScreenButton) {
buttons.push({
type: 'open-this-doc',
label: 'Open this doc',
@@ -565,19 +586,6 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) {
});
}
// open in new tab
if (this._blockComponent && isPeekable(this._blockComponent)) {
buttons.push({
type: 'open-in-center-peek',
label: 'Open in center peek',
icon: CenterPeekIcon,
action: () => this._peek(),
});
}
// open in split view
if (buttons.length === 0) {
return nothing;
}
@@ -12,7 +12,10 @@ import type { ImageBlockComponent } from '@blocksuite/affine-block-image';
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import { isPeekable, peek } from '@blocksuite/affine-components/peek';
import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import {
OpenDocExtensionIdentifier,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import { Bound, getCommonBoundWithRotation } from '@blocksuite/global/utils';
import {
ArrowDownBigBottomIcon,
@@ -23,11 +26,13 @@ import {
CopyIcon,
DeleteIcon,
DuplicateIcon,
ExpandFullIcon,
FrameIcon,
GroupIcon,
LinkedPageIcon,
OpenInNewIcon,
ResetIcon,
SplitViewIcon,
} from '@blocksuite/icons/lit';
import { duplicate } from '../../../edgeless/utils/clipboard-utils.js';
@@ -135,11 +140,12 @@ export const reorderGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
};
// Open Group
// TODO: construct this group dynamically
export const openGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
type: 'open',
items: [
{
icon: OpenInNewIcon({ width: '20', height: '20' }),
icon: ExpandFullIcon({ width: '20', height: '20' }),
label: 'Open this doc',
type: 'open',
generate: ctx => {
@@ -163,6 +169,62 @@ export const openGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
disabled,
};
},
when: ctx => {
const openDocService = ctx.std.get(OpenDocExtensionIdentifier);
return openDocService.isAllowed('open-in-active-view');
},
},
{
icon: SplitViewIcon({ width: '20', height: '20' }),
label: 'Open in split view',
type: 'open-in-split-view',
generate: ctx => {
const linkedDocBlock = ctx.getLinkedDocBlock();
if (!linkedDocBlock) return;
return {
action: () => {
const blockComponent = ctx.firstBlockComponent;
if (!blockComponent) return;
if (!('open' in blockComponent)) return;
if (typeof blockComponent.open !== 'function') return;
blockComponent.open({ openMode: 'open-in-new-view' });
},
};
},
when: ctx => {
const openDocService = ctx.std.get(OpenDocExtensionIdentifier);
return openDocService.isAllowed('open-in-new-view');
},
},
{
icon: OpenInNewIcon({ width: '20', height: '20' }),
label: 'Open in new tab',
type: 'open-in-new-tab',
generate: ctx => {
const linkedDocBlock = ctx.getLinkedDocBlock();
if (!linkedDocBlock) return;
return {
action: () => {
const blockComponent = ctx.firstBlockComponent;
if (!blockComponent) return;
if (!('open' in blockComponent)) return;
if (typeof blockComponent.open !== 'function') return;
blockComponent.open({ openMode: 'open-in-new-tab' });
},
};
},
when: ctx => {
const openDocService = ctx.std.get(OpenDocExtensionIdentifier);
return openDocService.isAllowed('open-in-new-tab');
},
},
{
icon: CenterPeekIcon({ width: '20', height: '20' }),
@@ -184,6 +246,10 @@ export const openGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
},
};
},
when: ctx => {
const openDocService = ctx.std.get(OpenDocExtensionIdentifier);
return openDocService.isAllowed('open-in-center-peek');
},
},
],
};
@@ -12,10 +12,8 @@ import {
} from '@blocksuite/affine-block-embed';
import {
CaptionIcon,
CenterPeekIcon,
CopyIcon,
EditIcon,
ExpandFullSmallIcon,
MoreVerticalIcon,
OpenIcon,
PaletteIcon,
@@ -48,6 +46,7 @@ import {
GenerateDocUrlProvider,
type GenerateDocUrlService,
type LinkEventType,
OpenDocExtensionIdentifier,
type TelemetryEvent,
TelemetryProvider,
ThemeProvider,
@@ -503,34 +502,42 @@ export class EmbedCardToolbar extends WidgetComponent<
}
private _openMenuButton() {
const buttons: MenuItem[] = [];
if (
this.focusModel &&
(isEmbedLinkedDocBlock(this.focusModel) ||
isEmbedSyncedDocBlock(this.focusModel))
) {
buttons.push({
type: 'open-this-doc',
label: 'Open this doc',
icon: ExpandFullSmallIcon,
action: () => this.focusBlock?.open(),
});
}
// open in new tab
const openDocConfig = this.std.get(OpenDocExtensionIdentifier);
const element = this.focusBlock;
if (element && isPeekable(element)) {
buttons.push({
type: 'open-in-center-peek',
label: 'Open in center peek',
icon: CenterPeekIcon,
action: () => peek(element),
});
}
const buttons: MenuItem[] = openDocConfig.items
.map(item => {
if (
item.type === 'open-in-center-peek' &&
element &&
!isPeekable(element)
) {
return null;
}
// open in split view
if (
!(
this.focusModel &&
(isEmbedLinkedDocBlock(this.focusModel) ||
isEmbedSyncedDocBlock(this.focusModel))
)
) {
return null;
}
return {
label: item.label,
type: item.type,
icon: item.icon,
action: () => {
if (item.type === 'open-in-center-peek') {
element && peek(element);
} else {
this.focusBlock?.open({ openMode: item.type });
}
},
};
})
.filter(item => item !== null);
if (buttons.length === 0) {
return nothing;