diff --git a/packages/affine/components/src/rich-text/inline/presets/nodes/affine-text.ts b/packages/affine/components/src/rich-text/inline/presets/nodes/affine-text.ts index a714669e7d431..5050c4713940e 100644 --- a/packages/affine/components/src/rich-text/inline/presets/nodes/affine-text.ts +++ b/packages/affine/components/src/rich-text/inline/presets/nodes/affine-text.ts @@ -9,7 +9,7 @@ import type { AffineTextAttributes } from '../../../extension/index.js'; export function affineTextStyles( props: AffineTextAttributes, override?: Readonly -): ReturnType { +): StyleInfo { let textDecorations = ''; if (props.underline) { textDecorations += 'underline'; @@ -31,7 +31,7 @@ export function affineTextStyles( }; } - return styleMap({ + return { 'font-weight': props.bold ? 'bolder' : 'inherit', 'font-style': props.italic ? 'italic' : 'normal', 'background-color': props.background ? props.background : undefined, @@ -39,26 +39,26 @@ export function affineTextStyles( 'text-decoration': textDecorations.length > 0 ? textDecorations : 'none', ...inlineCodeStyle, ...override, - }); + }; } export class AffineText extends ShadowlessElement { override render() { const style = this.delta.attributes ? affineTextStyles(this.delta.attributes) - : styleMap({}); + : {}; // we need to avoid \n appearing before and after the span element, which will // cause the unexpected space if (this.delta.attributes?.code) { - return html``; } // we need to avoid \n appearing before and after the span element, which will // cause the unexpected space - return html``; } diff --git a/packages/affine/components/src/rich-text/inline/presets/nodes/link-node/affine-link.ts b/packages/affine/components/src/rich-text/inline/presets/nodes/link-node/affine-link.ts index 72f41c0b089cc..735e663548b8f 100644 --- a/packages/affine/components/src/rich-text/inline/presets/nodes/link-node/affine-link.ts +++ b/packages/affine/components/src/rich-text/inline/presets/nodes/link-node/affine-link.ts @@ -1,5 +1,7 @@ +import type { ReferenceInfo, ReferenceParams } from '@blocksuite/affine-model'; import type { BlockComponent } from '@blocksuite/block-std'; +import { ParseDocUrlProvider } from '@blocksuite/affine-shared/services'; import { BLOCK_ID_ATTR, ShadowlessElement } from '@blocksuite/block-std'; import { type DeltaInsert, @@ -10,11 +12,13 @@ import { import { css, html } from 'lit'; import { property } from 'lit/decorators.js'; import { ref } from 'lit/directives/ref.js'; -import { styleMap } from 'lit/directives/style-map.js'; - -import type { AffineTextAttributes } from '../../../../extension/index.js'; +import { type StyleInfo, styleMap } from 'lit/directives/style-map.js'; import { HoverController } from '../../../../../hover/index.js'; +import { + type AffineTextAttributes, + RefNodeSlotsProvider, +} from '../../../../extension/index.js'; import { affineTextStyles } from '../affine-text.js'; import { toggleLinkPopup } from './link-popup/toggle-link-popup.js'; @@ -25,6 +29,38 @@ export class AffineLink extends ShadowlessElement { } `; + // The link has been identified. + private _identified: boolean = false; + + private _onClick = (e: MouseEvent) => { + if (!this._identified) { + this._identified = true; + this._identify(); + } + + const referenceInfo = this._referenceInfo; + if (!referenceInfo) return; + + const refNodeSlotsProvider = this.std?.getOptional(RefNodeSlotsProvider); + if (!refNodeSlotsProvider) return; + + e.preventDefault(); + + refNodeSlotsProvider.docLinkClicked.emit(referenceInfo); + }; + + // see https://github.com/toeverything/AFFiNE/issues/1540 + private _onMouseUp = () => { + const anchorElement = this.querySelector('a'); + if (!anchorElement || !anchorElement.isContentEditable) return; + anchorElement.contentEditable = 'false'; + setTimeout(() => { + anchorElement.removeAttribute('contenteditable'); + }, 0); + }; + + private _referenceInfo: ReferenceInfo | null = null; + private _whenHover = new HoverController( this, ({ abortController }) => { @@ -80,11 +116,7 @@ export class AffineLink extends ShadowlessElement { } get link() { - const link = this.delta.attributes?.link; - if (!link) { - return ''; - } - return link; + return this.delta.attributes?.link ?? ''; } get selfInlineRange() { @@ -97,50 +129,59 @@ export class AffineLink extends ShadowlessElement { return std; } - // see https://github.com/toeverything/AFFiNE/issues/1540 - private _onMouseUp() { - const anchorElement = this.querySelector('a'); - if (!anchorElement || !anchorElement.isContentEditable) return; - anchorElement.contentEditable = 'false'; - setTimeout(() => { - anchorElement.removeAttribute('contenteditable'); - }, 0); + // Identify if url is an in-app link + private _identify() { + const link = this.link; + if (!link) return; + + const result = this.std + ?.getOptional(ParseDocUrlProvider) + ?.parseDocUrl(link); + if (!result) return; + + const { docId: pageId, mode, blockIds, elementIds } = result; + + let params: ReferenceParams | undefined = undefined; + if (mode || blockIds?.length || elementIds?.length) { + params = { mode, blockIds, elementIds }; + } + + this._referenceInfo = { pageId, params }; + } + + private _renderLink(style: StyleInfo) { + return html``; } override render() { - const linkStyles = { + const linkStyle = { color: 'var(--affine-link-color)', fill: 'var(--affine-link-color)', 'text-decoration': 'none', cursor: 'pointer', }; + if (this.delta.attributes && this.delta.attributes?.code) { const codeStyle = affineTextStyles(this.delta.attributes); - return html``; + return html` + ${this._renderLink(linkStyle)} + `; } - const styles = this.delta.attributes - ? affineTextStyles(this.delta.attributes, linkStyles) - : styleMap({}); + const style = this.delta.attributes + ? affineTextStyles(this.delta.attributes, linkStyle) + : {}; - return html``; + return this._renderLink(style); } @property({ type: Object }) diff --git a/packages/affine/components/src/rich-text/inline/presets/nodes/reference-node/reference-node.ts b/packages/affine/components/src/rich-text/inline/presets/nodes/reference-node/reference-node.ts index 9ec987a50b529..a219179edc9c4 100644 --- a/packages/affine/components/src/rich-text/inline/presets/nodes/reference-node/reference-node.ts +++ b/packages/affine/components/src/rich-text/inline/presets/nodes/reference-node/reference-node.ts @@ -1,7 +1,6 @@ import type { ReferenceInfo } from '@blocksuite/affine-model'; import type { Doc, DocMeta } from '@blocksuite/store'; -import { getModelByElement } from '@blocksuite/affine-shared/utils'; import { BLOCK_ID_ATTR, type BlockComponent, @@ -20,6 +19,7 @@ import { css, html, nothing } from 'lit'; import { property, state } from 'lit/decorators.js'; import { choose } from 'lit/directives/choose.js'; import { ref } from 'lit/directives/ref.js'; +import { styleMap } from 'lit/directives/style-map.js'; import type { ReferenceNodeConfigProvider } from './reference-config.js'; @@ -70,18 +70,12 @@ export class AffineReference extends WithDisposable(ShadowlessElement) { } `; - private _refAttribute: NonNullable = { - type: 'LinkedPage', - pageId: '0', - }; - private _updateRefMeta = (doc: Doc) => { const refAttribute = this.delta.attributes?.reference; if (!refAttribute) { return; } - this._refAttribute = refAttribute; const refMeta = doc.collection.meta.docMetas.find( doc => doc.id === refAttribute.pageId ); @@ -166,8 +160,8 @@ export class AffineReference extends WithDisposable(ShadowlessElement) { get referenceInfo(): ReferenceInfo { const reference = this.delta.attributes?.reference; - const id = this.doc?.id; - if (!reference) return { pageId: id ?? '' }; + const id = this.doc?.id ?? ''; + if (!reference) return { pageId: id }; const { pageId, params } = reference; const info: ReferenceInfo = { pageId }; @@ -199,22 +193,9 @@ export class AffineReference extends WithDisposable(ShadowlessElement) { private _onClick() { if (!this.config.interactable) return; - - const refMeta = this.refMeta; - const model = getModelByElement(this); - if (!refMeta) { - // The doc is deleted - console.warn('The doc is deleted', this._refAttribute.pageId); - return; - } - if (!model || refMeta.id === model.doc.id) { - // the doc is the current doc. - return; - } - const targetDocId = refMeta.id; this.std .getOptional(RefNodeSlotsProvider) - ?.docLinkClicked.emit({ pageId: targetDocId }); + ?.docLinkClicked.emit(this.referenceInfo); } override connectedCallback() { @@ -252,13 +233,9 @@ export class AffineReference extends WithDisposable(ShadowlessElement) { // linking block/element isLinkedNode() { - const reference = this.delta.attributes?.reference; - if (!reference?.params) return false; - const { mode, blockIds, elementIds } = reference.params; - if (!mode) return false; - if (blockIds && blockIds.length > 0) return true; - if (elementIds && elementIds.length > 0) return true; - return false; + return Boolean( + this.referenceInfo.params && Object.keys(this.referenceInfo.params).length + ); } override render() { @@ -314,7 +291,7 @@ export class AffineReference extends WithDisposable(ShadowlessElement) { ${this.config.interactable ? ref(this._whenHover.setReference) : ''} data-selected=${this.selected} class="affine-reference" - style=${style} + style=${styleMap(style)} @click=${this._onClick} >${content}`; diff --git a/packages/affine/components/src/rich-text/inline/presets/nodes/reference-node/reference-popup.ts b/packages/affine/components/src/rich-text/inline/presets/nodes/reference-node/reference-popup.ts index 8d58f0c45e1cb..4b80d65b59e12 100644 --- a/packages/affine/components/src/rich-text/inline/presets/nodes/reference-node/reference-popup.ts +++ b/packages/affine/components/src/rich-text/inline/presets/nodes/reference-node/reference-popup.ts @@ -156,10 +156,6 @@ export class ReferencePopup extends WithDisposable(LitElement) { } private _openDoc() { - const pageId = this.referenceDocId; - const block = this.block; - if (pageId === block.doc.id) return; - this.std .getOptional(RefNodeSlotsProvider) ?.docLinkClicked.emit(this.referenceInfo);