mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-07 01:09:54 +08:00
feat(editor): add more open doc options to editor toolbar (#9588)
fix AF-2036, AF-2092
This commit is contained in:
@@ -17,7 +17,6 @@ import type {
|
||||
insertEmbedLinkedDocCommand,
|
||||
} from './embed-linked-doc-block/commands/insert-embed-linked-doc';
|
||||
import { EmbedEdgelessLinkedDocBlockComponent } from './embed-linked-doc-block/embed-edgeless-linked-doc-block';
|
||||
import type { EmbedLinkedDocBlockConfig } from './embed-linked-doc-block/embed-linked-doc-config';
|
||||
import {
|
||||
EmbedLoomBlockComponent,
|
||||
type EmbedLoomBlockService,
|
||||
@@ -123,9 +122,7 @@ declare global {
|
||||
interface CommandContext {
|
||||
insertedLinkType?: Promise<InsertedLinkType>;
|
||||
}
|
||||
interface BlockConfigs {
|
||||
'affine:embed-linked-doc': EmbedLinkedDocBlockConfig;
|
||||
}
|
||||
|
||||
interface Commands {
|
||||
insertEmbedLinkedDoc: typeof insertEmbedLinkedDocCommand;
|
||||
}
|
||||
|
||||
+9
-4
@@ -4,7 +4,11 @@ import {
|
||||
EMBED_CARD_WIDTH,
|
||||
} from '@blocksuite/affine-shared/consts';
|
||||
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
||||
import { cloneReferenceInfoWithoutAliases } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
cloneReferenceInfoWithoutAliases,
|
||||
isNewTabTrigger,
|
||||
isNewViewTrigger,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { Bound } from '@blocksuite/global/utils';
|
||||
|
||||
import { toEdgelessEmbedBlock } from '../common/to-edgeless-embed-block.js';
|
||||
@@ -65,9 +69,10 @@ export class EmbedEdgelessLinkedDocBlockComponent extends toEdgelessEmbedBlock(
|
||||
}
|
||||
|
||||
protected override _handleClick(evt: MouseEvent): void {
|
||||
if (this.config.handleClick) {
|
||||
this.config.handleClick(evt, this.host, this.referenceInfo$.peek());
|
||||
return;
|
||||
if (isNewTabTrigger(evt)) {
|
||||
this.open({ openMode: 'open-in-new-tab', event: evt });
|
||||
} else if (isNewViewTrigger(evt)) {
|
||||
this.open({ openMode: 'open-in-new-view', event: evt });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-35
@@ -14,11 +14,15 @@ import {
|
||||
DocDisplayMetaProvider,
|
||||
DocModeProvider,
|
||||
FeatureFlagService,
|
||||
OpenDocExtensionIdentifier,
|
||||
type OpenDocMode,
|
||||
ThemeProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
cloneReferenceInfo,
|
||||
cloneReferenceInfoWithoutAliases,
|
||||
isNewTabTrigger,
|
||||
isNewViewTrigger,
|
||||
matchFlavours,
|
||||
referenceToNode,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
@@ -39,10 +43,6 @@ import {
|
||||
renderLinkedDocInCard,
|
||||
} from '../common/render-linked-doc.js';
|
||||
import { SyncedDocErrorIcon } from '../embed-synced-doc-block/styles.js';
|
||||
import {
|
||||
type EmbedLinkedDocBlockConfig,
|
||||
EmbedLinkedDocBlockConfigIdentifier,
|
||||
} from './embed-linked-doc-config.js';
|
||||
import { styles } from './styles.js';
|
||||
import { getEmbedLinkedDocIcons } from './utils.js';
|
||||
|
||||
@@ -205,10 +205,18 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
|
||||
.icon(pageId, { params, title, referenced: true }).value;
|
||||
});
|
||||
|
||||
open = () => {
|
||||
this.std
|
||||
.getOptional(RefNodeSlotsProvider)
|
||||
?.docLinkClicked.emit(this.referenceInfo$.peek());
|
||||
open = ({
|
||||
openMode,
|
||||
event,
|
||||
}: {
|
||||
openMode?: OpenDocMode;
|
||||
event?: MouseEvent;
|
||||
} = {}) => {
|
||||
this.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.emit({
|
||||
...this.referenceInfo$.peek(),
|
||||
openMode,
|
||||
event,
|
||||
});
|
||||
};
|
||||
|
||||
refreshData = () => {
|
||||
@@ -228,12 +236,6 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
|
||||
);
|
||||
});
|
||||
|
||||
get config(): EmbedLinkedDocBlockConfig {
|
||||
return (
|
||||
this.std.provider.getOptional(EmbedLinkedDocBlockConfigIdentifier) || {}
|
||||
);
|
||||
}
|
||||
|
||||
get docTitle() {
|
||||
return this.model.title || this.linkedDoc?.meta?.title || 'Untitled';
|
||||
}
|
||||
@@ -247,22 +249,16 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
|
||||
}
|
||||
|
||||
private _handleDoubleClick(event: MouseEvent) {
|
||||
if (this.config.handleDoubleClick) {
|
||||
this.config.handleDoubleClick(
|
||||
event,
|
||||
this.host,
|
||||
this.referenceInfo$.peek()
|
||||
);
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPeekable(this)) {
|
||||
return;
|
||||
}
|
||||
event.stopPropagation();
|
||||
this.open();
|
||||
const openDocService = this.std.get(OpenDocExtensionIdentifier);
|
||||
const shouldOpenInPeek =
|
||||
openDocService.isAllowed('open-in-center-peek') && isPeekable(this);
|
||||
this.open({
|
||||
openMode: shouldOpenInPeek
|
||||
? 'open-in-center-peek'
|
||||
: 'open-in-active-view',
|
||||
event,
|
||||
});
|
||||
}
|
||||
|
||||
private _isDocEmpty() {
|
||||
@@ -274,13 +270,11 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
|
||||
}
|
||||
|
||||
protected _handleClick(event: MouseEvent) {
|
||||
if (this.config.handleClick) {
|
||||
this.config.handleClick(event, this.host, this.referenceInfo$.peek());
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
if (isNewTabTrigger(event)) {
|
||||
this.open({ openMode: 'open-in-new-tab', event });
|
||||
} else if (isNewViewTrigger(event)) {
|
||||
this.open({ openMode: 'open-in-new-view', event });
|
||||
}
|
||||
|
||||
this._selectBlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { ReferenceInfo } from '@blocksuite/affine-model';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { createIdentifier } from '@blocksuite/global/di';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
export interface EmbedLinkedDocBlockConfig {
|
||||
handleClick?: (
|
||||
e: MouseEvent,
|
||||
host: EditorHost,
|
||||
referenceInfo: ReferenceInfo
|
||||
) => void;
|
||||
handleDoubleClick?: (
|
||||
e: MouseEvent,
|
||||
host: EditorHost,
|
||||
referenceInfo: ReferenceInfo
|
||||
) => void;
|
||||
}
|
||||
|
||||
export const EmbedLinkedDocBlockConfigIdentifier =
|
||||
createIdentifier<EmbedLinkedDocBlockConfig>('EmbedLinkedDocBlockConfig');
|
||||
|
||||
export function EmbedLinkedDocBlockConfigExtension(
|
||||
config: EmbedLinkedDocBlockConfig
|
||||
): ExtensionType {
|
||||
return {
|
||||
setup: di => {
|
||||
di.addImpl(EmbedLinkedDocBlockConfigIdentifier, () => config);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from './adapters';
|
||||
export type { InsertedLinkType } from './commands';
|
||||
export * from './embed-linked-doc-block';
|
||||
export * from './embed-linked-doc-config';
|
||||
export * from './embed-linked-doc-spec';
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Peekable } from '@blocksuite/affine-components/peek';
|
||||
import { RefNodeSlotsProvider } from '@blocksuite/affine-components/rich-text';
|
||||
import {
|
||||
type DocLinkClickedEvent,
|
||||
RefNodeSlotsProvider,
|
||||
} from '@blocksuite/affine-components/rich-text';
|
||||
import {
|
||||
type AliasInfo,
|
||||
type DocMode,
|
||||
@@ -305,11 +308,13 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
|
||||
.icon(pageId, { params, referenced: true }).value;
|
||||
});
|
||||
|
||||
open = () => {
|
||||
open = (event?: Partial<DocLinkClickedEvent>) => {
|
||||
const pageId = this.model.pageId;
|
||||
if (pageId === this.doc.id) return;
|
||||
|
||||
this.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.emit({ pageId });
|
||||
this.std
|
||||
.getOptional(RefNodeSlotsProvider)
|
||||
?.docLinkClicked.emit({ ...event, pageId });
|
||||
};
|
||||
|
||||
refreshData = () => {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export { AffineLink, toggleLinkPopup } from './link-node/index.js';
|
||||
export * from './reference-node/reference-config.js';
|
||||
export { AffineReference } from './reference-node/reference-node.js';
|
||||
export type { RefNodeSlots } from './reference-node/types.js';
|
||||
export type {
|
||||
DocLinkClickedEvent,
|
||||
RefNodeSlots,
|
||||
} from './reference-node/types.js';
|
||||
|
||||
+43
-29
@@ -3,6 +3,8 @@ import {
|
||||
FeatureFlagService,
|
||||
GenerateDocUrlProvider,
|
||||
type LinkEventType,
|
||||
OpenDocExtensionIdentifier,
|
||||
type OpenDocMode,
|
||||
type TelemetryEvent,
|
||||
TelemetryProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
@@ -26,11 +28,9 @@ import { join } from 'lit/directives/join.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import {
|
||||
CenterPeekIcon,
|
||||
CopyIcon,
|
||||
DeleteIcon,
|
||||
EditIcon,
|
||||
ExpandFullSmallIcon,
|
||||
MoreVerticalIcon,
|
||||
OpenIcon,
|
||||
SmallArrowDownIcon,
|
||||
@@ -47,6 +47,7 @@ import { RefNodeSlotsProvider } from '../../../../extension/index.js';
|
||||
import type { AffineInlineEditor } from '../../affine-inline-specs.js';
|
||||
import { ReferenceAliasPopup } from './reference-alias-popup.js';
|
||||
import { styles } from './styles.js';
|
||||
import type { DocLinkClickedEvent } from './types.js';
|
||||
|
||||
export class ReferencePopup extends WithDisposable(LitElement) {
|
||||
static override styles = styles;
|
||||
@@ -66,10 +67,11 @@ export class ReferencePopup extends WithDisposable(LitElement) {
|
||||
track(this.std, 'CopiedLink', { control: 'copy link' });
|
||||
};
|
||||
|
||||
private readonly _openDoc = () => {
|
||||
this.std
|
||||
.getOptional(RefNodeSlotsProvider)
|
||||
?.docLinkClicked.emit(this.referenceInfo);
|
||||
private readonly _openDoc = (event?: Partial<DocLinkClickedEvent>) => {
|
||||
this.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.emit({
|
||||
...this.referenceInfo,
|
||||
...event,
|
||||
});
|
||||
};
|
||||
|
||||
private readonly _openEditPopup = (e: MouseEvent) => {
|
||||
@@ -134,8 +136,11 @@ export class ReferencePopup extends WithDisposable(LitElement) {
|
||||
);
|
||||
}
|
||||
|
||||
get _openButtonDisabled() {
|
||||
return this.referenceDocId === this.doc.id;
|
||||
_openButtonDisabled(openMode?: OpenDocMode) {
|
||||
if (openMode === 'open-in-active-view') {
|
||||
return this.referenceDocId === this.doc.id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get block() {
|
||||
@@ -246,28 +251,37 @@ export class ReferencePopup extends WithDisposable(LitElement) {
|
||||
}
|
||||
|
||||
private _openMenuButton() {
|
||||
const buttons: MenuItem[] = [
|
||||
{
|
||||
label: 'Open this doc',
|
||||
type: 'open-this-doc',
|
||||
icon: ExpandFullSmallIcon,
|
||||
action: this._openDoc,
|
||||
disabled: this._openButtonDisabled,
|
||||
},
|
||||
];
|
||||
const openDocConfig = this.std.get(OpenDocExtensionIdentifier);
|
||||
|
||||
// open in new tab
|
||||
|
||||
if (isPeekable(this.target)) {
|
||||
buttons.push({
|
||||
label: 'Open in center peek',
|
||||
type: 'open-in-center-peek',
|
||||
icon: CenterPeekIcon,
|
||||
action: () => peek(this.target),
|
||||
});
|
||||
}
|
||||
|
||||
// open in split view
|
||||
const buttons: MenuItem[] = openDocConfig.items
|
||||
.map(item => {
|
||||
if (
|
||||
(item.type === 'open-in-center-peek' && !isPeekable(this.target)) ||
|
||||
!openDocConfig?.isAllowed(item.type)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
label: item.label,
|
||||
type: item.type,
|
||||
icon: item.icon,
|
||||
action: () => {
|
||||
if (item.type === 'open-in-center-peek') {
|
||||
peek(this.target);
|
||||
} else {
|
||||
this._openDoc({ openMode: item.type });
|
||||
}
|
||||
},
|
||||
disabled: this._openButtonDisabled(item.type),
|
||||
when: () => {
|
||||
if (item.type === 'open-in-center-peek') {
|
||||
return isPeekable(this.target);
|
||||
}
|
||||
return openDocConfig?.isAllowed(item.type) ?? true;
|
||||
},
|
||||
};
|
||||
})
|
||||
.filter(item => item !== null);
|
||||
|
||||
if (buttons.length === 0) {
|
||||
return nothing;
|
||||
|
||||
+9
-2
@@ -1,6 +1,13 @@
|
||||
import type { ReferenceInfo } from '@blocksuite/affine-model';
|
||||
import type { OpenDocMode } from '@blocksuite/affine-shared/services';
|
||||
import type { Slot } from '@blocksuite/global/utils';
|
||||
|
||||
export type RefNodeSlots = {
|
||||
docLinkClicked: Slot<ReferenceInfo>;
|
||||
export type DocLinkClickedEvent = ReferenceInfo & {
|
||||
// default is active view
|
||||
openMode?: OpenDocMode;
|
||||
event?: MouseEvent;
|
||||
};
|
||||
|
||||
export type RefNodeSlots = {
|
||||
docLinkClicked: Slot<DocLinkClickedEvent>;
|
||||
};
|
||||
|
||||
@@ -204,6 +204,7 @@ export class EditorMenuAction extends LitElement {
|
||||
|
||||
::slotted(svg) {
|
||||
color: var(--affine-icon-color);
|
||||
font-size: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ export * from './font-loader';
|
||||
export * from './generate-url-service';
|
||||
export * from './native-clipboard-service';
|
||||
export * from './notification-service';
|
||||
export * from './open-doc-config';
|
||||
export * from './page-viewport-service';
|
||||
export * from './parse-url-service';
|
||||
export * from './quick-search-service';
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { createIdentifier } from '@blocksuite/global/di';
|
||||
import { CenterPeekIcon, ExpandFullIcon } from '@blocksuite/icons/lit';
|
||||
import { type ExtensionType } from '@blocksuite/store';
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
export type OpenDocMode =
|
||||
| 'open-in-active-view'
|
||||
| 'open-in-new-view'
|
||||
| 'open-in-new-tab'
|
||||
| 'open-in-center-peek';
|
||||
|
||||
// todo: later this will be used to generate the menu items.
|
||||
// for now we only use it as a hint for whether or not to show the open doc buttons.
|
||||
export interface OpenDocConfigItem {
|
||||
type: OpenDocMode;
|
||||
label: string;
|
||||
icon: TemplateResult<1>;
|
||||
}
|
||||
export interface OpenDocConfig {
|
||||
items: OpenDocConfigItem[];
|
||||
}
|
||||
|
||||
export interface OpenDocService {
|
||||
isAllowed: (mode: OpenDocMode) => boolean;
|
||||
items: OpenDocConfig['items'];
|
||||
}
|
||||
|
||||
export const OpenDocExtensionIdentifier = createIdentifier<OpenDocService>(
|
||||
'AffineOpenDocExtension'
|
||||
);
|
||||
|
||||
const defaultConfig: OpenDocConfig = {
|
||||
items: [
|
||||
{
|
||||
type: 'open-in-active-view',
|
||||
label: 'Open this doc',
|
||||
icon: ExpandFullIcon(),
|
||||
},
|
||||
{
|
||||
type: 'open-in-center-peek',
|
||||
label: 'Open in center peek',
|
||||
icon: CenterPeekIcon(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const OpenDocExtension = (config: OpenDocConfig): ExtensionType => ({
|
||||
setup: di => {
|
||||
di.override(OpenDocExtensionIdentifier, () => {
|
||||
const allowedOpenDocModes = new Set(config.items.map(item => item.type));
|
||||
return {
|
||||
isAllowed: (mode: OpenDocMode) => allowedOpenDocModes.has(mode),
|
||||
items: config.items,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const DefaultOpenDocExtension = OpenDocExtension(defaultConfig);
|
||||
@@ -44,6 +44,16 @@ export function isControlledKeyboardEvent(e: KeyboardEvent) {
|
||||
return e.ctrlKey || e.metaKey || e.altKey;
|
||||
}
|
||||
|
||||
export function isNewTabTrigger(event?: MouseEvent) {
|
||||
return event
|
||||
? (event.ctrlKey || event.metaKey || event.button === 1) && !event.altKey
|
||||
: false;
|
||||
}
|
||||
|
||||
export function isNewViewTrigger(event?: MouseEvent) {
|
||||
return event ? (event.ctrlKey || event.metaKey) && event.altKey : false;
|
||||
}
|
||||
|
||||
export function on<
|
||||
T extends HTMLElement,
|
||||
K extends keyof M,
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
RichTextExtensions,
|
||||
} from '@blocksuite/affine-components/rich-text';
|
||||
import {
|
||||
DefaultOpenDocExtension,
|
||||
DocDisplayMetaService,
|
||||
EditPropsStore,
|
||||
FeatureFlagService,
|
||||
@@ -54,6 +55,7 @@ export const CommonBlockSpecs: ExtensionType[] = [
|
||||
CodeBlockSpec,
|
||||
ImageBlockSpec,
|
||||
ParagraphBlockSpec,
|
||||
DefaultOpenDocExtension,
|
||||
].flat();
|
||||
|
||||
export const PageFirstPartyBlockSpecs: ExtensionType[] = [
|
||||
|
||||
+43
-35
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user