Skip to content

Commit

Permalink
feat(blocks): allow peek view to be closed by the caller
Browse files Browse the repository at this point in the history
  • Loading branch information
pengx17 committed Oct 25, 2024
1 parent 829361a commit 879aac4
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ export function AffinePageReference({
if (e.shiftKey && ref.current) {
e.preventDefault();
e.stopPropagation();
peekView.open(ref.current).catch(console.error);
peekView
.open({
element: ref.current,
})
.catch(console.error);
}

if (isInPeekView) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import type { EditorService } from '@affine/core/modules/editor';
import { EditorSettingService } from '@affine/core/modules/editor-settting';
import { resolveLinkToDoc } from '@affine/core/modules/navigation';
import type { PeekViewService } from '@affine/core/modules/peek-view';
import type { ActivePeekView } from '@affine/core/modules/peek-view/entities/peek-view';
import {
CreationQuickSearchSession,
DocsQuickSearchSession,
Expand All @@ -35,6 +34,8 @@ import type {
AffineReference,
DocMode,
DocModeProvider,
PeekOptions,

Check failure on line 37 in packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx

View workflow job for this annotation

GitHub Actions / Lint

Module '"@blocksuite/affine/blocks"' has no exported member 'PeekOptions'.
PeekViewService as BSPeekViewService,
QuickSearchResult,
RootService,
} from '@blocksuite/affine/blocks';
Expand Down Expand Up @@ -243,11 +244,27 @@ export function patchEmbedLinkedDocBlockConfig(framework: FrameworkProvider) {

export function patchPeekViewService(service: PeekViewService) {
return PeekViewExtension({
peek: (target: ActivePeekView['target'], template?: TemplateResult) => {
logger.debug('center peek', target, template);
return service.peekView.open(target, template);
peek: (

Check failure on line 247 in packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx

View workflow job for this annotation

GitHub Actions / Lint

Type '(element: { target: HTMLElement; docId: string; blockIds?: string[]; template?: TemplateResult; }, options?: any) => Promise<void>' is not assignable to type '{ (pageRef: { docId: string; blockId?: string | undefined; }): Promise<void>; (target: HTMLElement, template?: TemplateResult | undefined): Promise<...>; <Element extends BlockComponent>(target: Element, template?: TemplateResult | undefined): Promise<...>; }'.
element: {
target: HTMLElement;
docId: string;
blockIds?: string[];
template?: TemplateResult;
},
options?: PeekOptions
) => {
logger.debug('center peek', element);
const { template, target, ...props } = element;
return service.peekView.open(
{
element: target,
...props,
},
template,
options?.abortSignal
);
},
});
} satisfies BSPeekViewService);
}

export function patchDocModeService(
Expand Down
174 changes: 103 additions & 71 deletions packages/frontend/core/src/modules/peek-view/entities/peek-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@ import { firstValueFrom, map, race } from 'rxjs';
import { resolveLinkToDoc } from '../../navigation';
import type { WorkbenchService } from '../../workbench';

export type PeekViewTarget =
export type PeekViewElement =
| HTMLElement
| BlockComponent
| AffineReference
| HTMLAnchorElement
| { docId: string; blockIds?: string[] };
| HTMLAnchorElement;

export interface PeekViewTarget {
element?: PeekViewElement;
docId?: string;
blockIds?: string[];
}

export interface DocPeekViewInfo {
type: 'doc';
Expand Down Expand Up @@ -101,79 +106,83 @@ function resolvePeekInfoFromPeekTarget(
};
}

if (peekTarget instanceof AffineReference) {
const referenceInfo = peekTarget.referenceInfo;
if (referenceInfo) {
const { pageId: docId } = referenceInfo;
const info: DocPeekViewInfo = {
type: 'doc',
docId,
};
Object.assign(info, referenceInfo.params);
return info;
}
} else if ('model' in peekTarget) {
const blockModel = peekTarget.model;
if (isEmbedLinkedDocModel(blockModel)) {
const info: DocPeekViewInfo = {
type: 'doc',
docId: blockModel.pageId,
};
Object.assign(info, blockModel.params);
return info;
} else if (isEmbedSyncedDocModel(blockModel)) {
return {
type: 'doc',
docId: blockModel.pageId,
};
} else if (isSurfaceRefModel(blockModel)) {
const refModel = (peekTarget as SurfaceRefBlockComponent).referenceModel;
// refModel can be null if the reference is invalid
if (refModel) {
const docId =
'doc' in refModel ? refModel.doc.id : refModel.surface.doc.id;
return {
const element = peekTarget.element;

if (element) {
if (element instanceof AffineReference) {
const referenceInfo = element.referenceInfo;
if (referenceInfo) {
const { pageId: docId } = referenceInfo;
const info: DocPeekViewInfo = {
type: 'doc',
docId,
mode: 'edgeless',
xywh: refModel.xywh,
};
Object.assign(info, referenceInfo.params);
return info;
}
} else if (isImageBlockModel(blockModel)) {
return {
type: 'image',
docId: blockModel.doc.id,
blockIds: [blockModel.id],
};
} else if (isAIChatBlockModel(blockModel)) {
return {
type: 'ai-chat-block',
docId: blockModel.doc.id,
model: blockModel,
host: peekTarget.host,
};
}
} else if (peekTarget instanceof HTMLAnchorElement) {
const maybeDoc = resolveLinkToDoc(peekTarget.href);
if (maybeDoc) {
const info: DocPeekViewInfo = {
type: 'doc',
docId: maybeDoc.docId,
};

if (maybeDoc.mode) {
info.mode = maybeDoc.mode;
}
if (maybeDoc.blockIds?.length) {
info.blockIds = maybeDoc.blockIds;
}
if (maybeDoc.elementIds?.length) {
info.elementIds = maybeDoc.elementIds;
} else if ('model' in element) {
const blockModel = element.model;
if (isEmbedLinkedDocModel(blockModel)) {
const info: DocPeekViewInfo = {
type: 'doc',
docId: blockModel.pageId,
};
Object.assign(info, blockModel.params);
return info;
} else if (isEmbedSyncedDocModel(blockModel)) {
return {
type: 'doc',
docId: blockModel.pageId,
};
} else if (isSurfaceRefModel(blockModel)) {
const refModel = (element as SurfaceRefBlockComponent).referenceModel;
// refModel can be null if the reference is invalid
if (refModel) {
const docId =
'doc' in refModel ? refModel.doc.id : refModel.surface.doc.id;
return {
type: 'doc',
docId,
mode: 'edgeless',
xywh: refModel.xywh,
};
}
} else if (isImageBlockModel(blockModel)) {
return {
type: 'image',
docId: blockModel.doc.id,
blockIds: [blockModel.id],
};
} else if (isAIChatBlockModel(blockModel)) {
return {
type: 'ai-chat-block',
docId: blockModel.doc.id,
model: blockModel,
host: element.host,
};
}
} else if (element instanceof HTMLAnchorElement) {
const maybeDoc = resolveLinkToDoc(element.href);
if (maybeDoc) {
const info: DocPeekViewInfo = {
type: 'doc',
docId: maybeDoc.docId,
};

return info;
if (maybeDoc.mode) {
info.mode = maybeDoc.mode;
}
if (maybeDoc.blockIds?.length) {
info.blockIds = maybeDoc.blockIds;
}
if (maybeDoc.elementIds?.length) {
info.elementIds = maybeDoc.elementIds;
}

return info;
}
}
} else if ('docId' in peekTarget) {
} else if ('docId' in peekTarget && peekTarget.docId) {
return {
type: 'doc',
docId: peekTarget.docId,
Expand Down Expand Up @@ -208,7 +217,8 @@ export class PeekViewEntity extends Entity {
// return true if the peek view will be handled
open = async (
target: ActivePeekView['target'],
template?: TemplateResult
template?: TemplateResult,
abortSignal?: AbortSignal
) => {
const resolvedInfo = resolvePeekInfoFromPeekTarget(target, template);
if (!resolvedInfo) {
Expand All @@ -220,7 +230,11 @@ export class PeekViewEntity extends Entity {
// if there is an active peek view and it is a doc peek view, we will navigate it first
if (active?.info.type === 'doc' && this.show$.value?.value) {
// TODO(@pengx17): scroll to the viewing position?
this.workbenchService.workbench.openDoc(active.info.docId);
this.workbenchService.workbench.openDoc({
docId: active.info.docId,
blockIds: active.info.blockIds,
elementIds: active.info.elementIds,
});
}

this._active$.next({ target, info: resolvedInfo });
Expand All @@ -231,6 +245,24 @@ export class PeekViewEntity extends Entity {
? 'zoom'
: 'fade',
});

if (abortSignal) {
const abortListener = () => {
if (this.active$.value?.target === target) {
this.close();
}
};

abortSignal.addEventListener('abort', abortListener);

const showSubscription = this.show$.subscribe(v => {
if (!v && !abortSignal.aborted) {
abortSignal.removeEventListener('abort', abortListener);
showSubscription.unsubscribe();
}
});
}

return firstValueFrom(race(this._active$, this.show$).pipe(map(() => {})));
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ function DocPeekPreviewEditor({
if (!refNodeSlots) return;
// doc change event inside peek view should be handled by peek view
disposableGroup.add(
// todo(@pengx17): seems not working
refNodeSlots.docLinkClicked.on(options => {
peekView
.open({
type: 'doc',
docId: options.pageId,
...options.params,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ const getRendererProps = (
children: preview,
controls,
target:
activePeekView?.target instanceof HTMLElement
? activePeekView.target
activePeekView?.target.element instanceof HTMLElement
? activePeekView.target.element
: undefined,
mode: getMode(activePeekView.info),
dialogFrame: activePeekView.info.type !== 'image',
Expand All @@ -108,8 +108,8 @@ export const PeekViewManagerModal = () => {

useEffect(() => {
const subscription = peekViewEntity.show$.subscribe(() => {
if (activePeekView?.target instanceof BlockComponent) {
activePeekView.target.requestUpdate();
if (activePeekView?.target.element instanceof BlockComponent) {
activePeekView.target.element.requestUpdate();
}
});

Expand Down

0 comments on commit 879aac4

Please sign in to comment.