Mention object icons & bug fixes (#8357)

* Mention object icons & bug fixes

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

* ff

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

* fmt

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

* ff

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

* rv

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

* ff

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

* fmt

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

* fixed test

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

---------

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>
This commit is contained in:
Victor Ilyushchenko 2025-03-27 18:33:19 +03:00 committed by GitHub
parent 62d6a76b4a
commit 0bbf5773e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 171 additions and 115 deletions

View File

@ -394,6 +394,10 @@ export function createModel (builder: Builder): void {
presenter: documents.component.DocumentMetaPresenter
})
builder.mixin(documents.class.DocumentMeta, core.class.Class, view.mixin.ObjectTitle, {
titleProvider: documents.function.DocumentMetaTitleProvider
})
builder.mixin(documents.class.DocumentMeta, core.class.Class, view.mixin.LinkProvider, {
encode: documents.function.GetDocumentMetaLinkFragment
})

View File

@ -145,7 +145,7 @@ export class TProject extends TDoc implements Project {
}
@Model(documents.class.DocumentMeta, core.class.Doc, DOMAIN_DOCUMENTS)
@UX(documents.string.Document)
@UX(documents.string.ControlledDocument, documents.icon.Document)
export class TDocumentMeta extends TDoc implements DocumentMeta {
@Prop(Collection(documents.class.Document), documents.string.Documents)
documents!: CollectionSize<Document>

View File

@ -111,7 +111,7 @@ export class TResource extends TDoc implements Resource {
}
@Model(drive.class.Folder, drive.class.Resource, DOMAIN_DRIVE)
@UX(drive.string.Folder)
@UX(drive.string.Folder, drive.icon.Folder)
export class TFolder extends TResource implements Folder {
@Prop(TypeRef(drive.class.Folder), drive.string.Parent)
@Index(IndexKind.Indexed)
@ -126,7 +126,7 @@ export class TFolder extends TResource implements Folder {
}
@Model(drive.class.File, drive.class.Resource, DOMAIN_DRIVE)
@UX(drive.string.File)
@UX(drive.string.File, drive.icon.File)
export class TFile extends TResource implements File {
@Prop(TypeRef(drive.class.Folder), drive.string.Parent)
@Index(IndexKind.Indexed)

View File

@ -26,6 +26,7 @@
export let shrink: number = 1
export let accent: boolean = false
export let noOverflow: boolean = false
export let inlineReference: boolean = false
function clickHandler (e: MouseEvent): void {
if (disabled) return
@ -72,6 +73,7 @@
class:noOverflow
class:inline
class:colorInherit
class:antiMention={inlineReference}
class:fs-bold={accent}
style:flex-shrink={shrink}
on:click={clickHandler}
@ -85,6 +87,7 @@
class:noOverflow
class:inline
class:colorInherit
class:antiMention={inlineReference}
class:fs-bold={accent}
style:flex-shrink={shrink}
on:click={clickHandler}
@ -95,7 +98,7 @@
<style lang="scss">
span,
a {
a:not(.antiMention) {
min-width: 0;
font-weight: inherit;

View File

@ -14,19 +14,23 @@
-->
<script lang="ts">
import { Class, Doc, Ref } from '@hcengineering/core'
import { Component } from '@hcengineering/ui'
import { Component, Icon } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { createQuery } from '../../utils'
import { createQuery, getClient } from '../../utils'
export let _id: Ref<Doc> | undefined = undefined
export let _class: Ref<Class<Doc>> | undefined = undefined
export let title: string = ''
const client = getClient()
const hierarchy = client.getHierarchy()
const docQuery = createQuery()
let doc: Doc | undefined = undefined
$: icon = _class !== undefined ? hierarchy.getClass(_class).icon : null
$: if (_class != null && _id != null) {
docQuery.query(_class, { _id }, (r) => {
doc = r.shift()
@ -35,7 +39,9 @@
</script>
{#if !doc}
<span class="antiMention">@{title}</span>
<span class="antiMention">
{#if icon}<Icon {icon} size="small" />{' '}{:else}@{/if}{title}
</span>
{:else}
<Component
is={view.component.ObjectMention}

View File

@ -21,7 +21,7 @@ describe('dsl', () => {
)
)
expect(jsonToHTML(doc)).toEqual(
'<p>Hello, <span data-type="reference" class="antiMention" data-id="123" data-objectclass="world" data-label="World">@World</span></p><p>Check out <a target="_blank" rel="noopener noreferrer" class="cursor-pointer" href="https://example.com"><u>this link</u></a>.</p>'
'<p>Hello, <span data-type="reference" data-id="123" data-objectclass="world" data-label="World" class="antiMention">@World</span></p><p>Check out <a target="_blank" rel="noopener noreferrer" class="cursor-pointer" href="https://example.com"><u>this link</u></a>.</p>'
)
})
})

View File

@ -16,6 +16,7 @@
import { Node, mergeAttributes } from '@tiptap/core'
import { getDataAttribute } from './utils'
import { Class, Doc, Ref } from '@hcengineering/core'
import { Attrs } from '@tiptap/pm/model'
export interface ReferenceNodeProps {
id: Ref<Doc>
@ -24,7 +25,6 @@ export interface ReferenceNodeProps {
}
export interface ReferenceOptions {
renderLabel: (props: { options: ReferenceOptions, props: ReferenceNodeProps }) => string
suggestion: { char?: string }
HTMLAttributes: Record<string, any>
}
@ -37,8 +37,6 @@ export const ReferenceNode = Node.create<ReferenceOptions>({
group: 'inline',
inline: true,
selectable: true,
atom: true,
draggable: true,
addAttributes () {
return {
@ -50,10 +48,6 @@ export const ReferenceNode = Node.create<ReferenceOptions>({
addOptions () {
return {
renderLabel ({ options, props }) {
// eslint-disable-next-line
return `${options.suggestion.char}${props.label ?? props.id}`
},
suggestion: { char: '@' },
HTMLAttributes: {}
}
@ -62,22 +56,14 @@ export const ReferenceNode = Node.create<ReferenceOptions>({
parseHTML () {
return [
{
tag: `span[data-type="${this.name}"]`,
getAttrs: (el) => {
const id = (el as HTMLSpanElement).getAttribute('id')?.trim()
const label = (el as HTMLSpanElement).getAttribute('label')?.trim()
const objectclass = (el as HTMLSpanElement).getAttribute('objectclass')?.trim()
if (id == null || label == null || objectclass == null) {
return false
}
return {
id,
label,
objectclass
}
}
priority: 60,
tag: 'span[data-type="reference"]',
getAttrs
},
{
priority: 60,
tag: 'a[data-type="reference"]',
getAttrs
}
]
},
@ -88,20 +74,31 @@ export const ReferenceNode = Node.create<ReferenceOptions>({
mergeAttributes(
{
'data-type': this.name,
'data-id': node.attrs.id,
'data-objectclass': node.attrs.objectclass,
'data-label': node.attrs.label,
class: 'antiMention'
},
this.options.HTMLAttributes,
HTMLAttributes
),
this.options.renderLabel({
options: this.options,
props: node.attrs as ReferenceNodeProps
})
`${this.options.suggestion.char}${node.attrs.label ?? node.attrs.id}`
]
},
renderText ({ node }) {
const options = this.options
return options.renderLabel({ options, props: node.attrs as ReferenceNodeProps })
}
})
function getAttrs (el: HTMLSpanElement): Attrs | false {
const id = el.dataset.id?.trim()
const label = el.dataset.label?.trim()
const objectclass = el.dataset.objectclass?.trim()
if (id == null || label == null || objectclass == null) {
return false
}
return {
id,
label,
objectclass
}
}

View File

@ -518,14 +518,28 @@
}
}
.antiMention {
display: inline-flex;
.antiMention, .antiMention:visited {
display: inline;
font-weight: normal;
padding: 0 .25rem;
width: fit-content;
color: var(--theme-link-color);
background-color: var(--theme-mention-bg-color);
text-decoration: none;
border-radius: .25rem;
cursor: pointer;
user-select: text;
&:hover {
text-decoration: none !important;
background-color: var(--theme-mention-focused-bg-color);
}
> svg {
display: inline-block;
vertical-align: sub;
margin-bottom: 1px;
width: .875rem;
}
}
.antiDivider {

View File

@ -36,7 +36,7 @@
</script>
{#if inline && value}
<ObjectMention object={value} {disabled} {noUnderline} {onClick} component={card.component.EditCard} />
<ObjectMention object={value} {disabled} {onClick} component={card.component.EditCard} />
{:else if value}
{#if type === 'link'}
<div class="flex-row-center">

View File

@ -34,13 +34,7 @@
{#if value}
{#if inline}
<ObjectMention
object={value}
{disabled}
{accent}
{noUnderline}
component={contact.component.EditOrganizationPanel}
/>
<ObjectMention object={value} {disabled} component={contact.component.EditOrganizationPanel} />
{:else if type === 'link'}
<DocNavLink {disabled} object={value} {accent} {noUnderline} component={contact.component.EditOrganizationPanel}>
<div class="flex-presenter" style:max-width={maxWidth} use:tooltip={{ label: getEmbeddedLabel(value.name) }}>

View File

@ -41,7 +41,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} {colorInherit} onClick={onEdit} />
<ObjectMention object={value} {disabled} onClick={onEdit} />
{:else if type === 'link'}
<DocNavLink object={value} onClick={onEdit} {disabled} {noUnderline} {colorInherit} {accent} noOverflow>
<span

View File

@ -114,7 +114,8 @@ import {
getVisibleFilters,
isFolder,
renameFolder,
sortDocumentStates
sortDocumentStates,
getDocumentMetaTitle
} from './utils'
export { DocumentStatusTag, DocumentTitle, DocumentVersionPresenter, StatePresenter }
@ -462,6 +463,7 @@ export default async (): Promise<Resources> => ({
ControlledDocumentReferenceObjectProvider: controlledDocumentReferenceObjectProvider,
ProjectDocumentReferenceObjectProvider: projectDocumentReferenceObjectProvider,
ControlledDocumentTitleProvider: getControlledDocumentTitle,
DocumentMetaTitleProvider: getDocumentMetaTitle,
Comment: comment,
IsCommentVisible: isCommentVisible
},

View File

@ -249,6 +249,7 @@ export default mergeIds(documentsId, documents, {
CanOpenDocument: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
CanPrintDocument: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
CanTransferDocument: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
ControlledDocumentTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>
ControlledDocumentTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
DocumentMetaTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>
}
})

View File

@ -716,6 +716,18 @@ export async function documentIdentifierProvider (client: Client, ref: Ref<Docum
return document.code
}
export async function getDocumentMetaTitle (
client: Client,
ref: Ref<DocumentMeta>,
doc?: DocumentMeta
): Promise<string> {
const object = doc ?? (await client.findOne(documents.class.DocumentMeta, { _id: ref }))
if (object === undefined) return ''
return object.title
}
export async function controlledDocumentReferenceObjectProvider (
client: Client,
ref: Ref<ControlledDocument>,

View File

@ -37,7 +37,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} component={document.component.EditDoc} />
<ObjectMention object={value} {disabled} component={document.component.EditDoc} />
{:else if type === 'link'}
<DocNavLink {disabled} object={value} {accent} {noUnderline} component={document.component.EditDoc}>
<div

View File

@ -34,7 +34,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else if type === 'link'}
<DocNavLink {disabled} object={value} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.name) }}>

View File

@ -39,7 +39,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else if type === 'link'}
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.title) }}>

View File

@ -53,7 +53,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else if type === 'link'}
<DocNavLink object={value} onClick={handleClick} {disabled} {accent} {noUnderline}>
<div class="flex-presenter">

View File

@ -34,7 +34,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else if type === 'link'}
<DocNavLink {disabled} object={value} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.title) }}>

View File

@ -32,7 +32,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {noUnderline} {accent} />
<ObjectMention object={value} {disabled} />
{:else if type === 'link'}
<DocNavLink object={value} {disabled} {noUnderline} {accent}>
<div class="flex-presenter">

View File

@ -31,7 +31,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else if type === 'link'}
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.title) }}>

View File

@ -36,7 +36,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else if type === 'link'}
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(roomName) }}>

View File

@ -46,7 +46,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} title={name} />
<ObjectMention object={value} {disabled} title={name} />
{:else}
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(name) }}>

View File

@ -32,7 +32,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} title={version} />
<ObjectMention object={value} {disabled} title={version} />
{:else}
<DocNavLink object={value} {disabled} {noUnderline} {accent}>
<div class="flex-presenter">

View File

@ -35,7 +35,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else}
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.name) }}>

View File

@ -35,7 +35,7 @@
{#if value && shortLabel}
{#if inline}
<ObjectMention object={value} {disabled} {noUnderline} {accent} />
<ObjectMention object={value} {disabled} />
{:else if type === 'link'}
<DocNavLink object={value} {disabled} {noUnderline} {accent}>
<div class="flex-presenter">

View File

@ -41,7 +41,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} component={recruit.component.EditVacancy} />
<ObjectMention object={value} {disabled} component={recruit.component.EditVacancy} />
{:else if type === 'link'}
<div class="flex-between flex-gap-2 w-full">
<DocNavLink {disabled} object={value} {accent} {noUnderline} component={recruit.component.EditVacancy}>

View File

@ -35,7 +35,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} {colorInherit} onClick={onEdit} />
<ObjectMention object={value} {disabled} onClick={onEdit} />
{:else if type === 'link'}
<DocNavLink object={value} onClick={onEdit} {disabled} {noUnderline} {colorInherit} {accent} noOverflow>
<div class="flex-presenter" style:max-width={maxWidth}>

View File

@ -31,7 +31,7 @@
{#if value}
{#if inline}
<ObjectMention title={value?.name} object={value} {disabled} {accent} {noUnderline} />
<ObjectMention title={value?.name} object={value} {disabled} />
{:else}
<DocNavLink object={value} {disabled} {accent} {noUnderline} {onClick}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.name) }}>

View File

@ -37,7 +37,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else}
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: testManagement.string.TestCase }}>

View File

@ -27,7 +27,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else}
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.name) }}>

View File

@ -37,7 +37,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else}
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: testManagement.string.TestResult }}>

View File

@ -27,7 +27,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else}
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.name) }}>

View File

@ -30,7 +30,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
<ObjectMention object={value} {disabled} />
{:else}
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.name) }}>

View File

@ -26,6 +26,7 @@ import { getMetadata, getResource } from '@hcengineering/platform'
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
import view from '@hcengineering/view'
import contact from '@hcengineering/contact'
import { parseLocation, type Location } from '@hcengineering/ui'
import workbench, { type Application } from '@hcengineering/workbench'
@ -38,10 +39,6 @@ export const ReferenceExtension = ReferenceNode.extend<ReferenceExtensionOptions
addOptions () {
return {
HTMLAttributes: {},
renderLabel ({ options, props }) {
// eslint-disable-next-line
return `${options.suggestion.char}${props.label ?? props.id}`
},
suggestion: {
char: '@',
allowSpaces: true,
@ -86,19 +83,21 @@ export const ReferenceExtension = ReferenceNode.extend<ReferenceExtensionOptions
addNodeView () {
return ({ node, HTMLAttributes }) => {
const span = document.createElement('span')
span.setAttribute('data-type', this.name)
span.className = 'antimention'
const root = document.createElement('span')
root.className = 'antiMention'
const attributes = mergeAttributes(
{
'data-type': this.name,
'data-id': node.attrs.id,
'data-objectclass': node.attrs.objectclass,
'data-label': node.attrs.label,
class: 'antiMention'
},
this.options.HTMLAttributes,
HTMLAttributes
)
span.addEventListener('click', (event) => {
root.addEventListener('click', (event) => {
if (event.button !== 0) return
const link = (event.target as HTMLElement)?.closest('span')
@ -112,20 +111,40 @@ export const ReferenceExtension = ReferenceNode.extend<ReferenceExtensionOptions
})
Object.entries(attributes).forEach(([key, value]) => {
span.setAttribute(key, value)
root.setAttribute(key, value)
})
const client = getClient()
const hierarchy = client.getHierarchy()
const query = createQuery(true)
const options = this.options
const renderLabel = (props: ReferenceNodeProps): void => {
span.setAttribute('data-label', props.label)
span.innerText = options.renderLabel({ options, props: props ?? (node.attrs as ReferenceNodeProps) })
root.setAttribute('data-label', props.label)
titleSpan.innerText = `${iconUrl !== '' ? '' : options.suggestion.char}${props.label ?? props.id}`
}
const id = node.attrs.id
const objectclass: Ref<Class<Doc>> = node.attrs.objectclass
const icon =
objectclass !== undefined && !hierarchy.isDerived(objectclass, contact.class.Contact)
? hierarchy.getClass(objectclass).icon
: undefined
const iconUrl = typeof icon === 'string' ? getMetadata(icon) ?? 'https://anticrm.org/logo.svg' : ''
if (iconUrl !== '') {
const svg = root.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
root.appendChild(document.createTextNode(' '))
svg.setAttribute('class', 'svg-small')
svg.setAttribute('fill', 'currentColor')
const use = svg.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'use'))
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', iconUrl)
}
const titleSpan = root.appendChild(document.createElement('span'))
renderLabel({ id, objectclass, label: node.attrs.label })
if (id !== undefined && objectclass !== undefined) {
@ -141,7 +160,7 @@ export const ReferenceExtension = ReferenceNode.extend<ReferenceExtensionOptions
}
return {
dom: span,
dom: root,
update (node, decorations) {
renderLabel({ id, objectclass, label: node.attrs.label })
return true

View File

@ -44,7 +44,7 @@
<div class="flex-row-center">
{#if inline}
<ObjectMention object={value} {disabled} {noUnderline} {accent} {onClick} />
<ObjectMention object={value} {disabled} {onClick} />
{:else}
<DocNavLink object={value} {onClick} {disabled} {noUnderline} {accent} component={view.component.EditDoc}>
<span class="flex-presenter flex-row-center" class:list={kind === 'list'}>

View File

@ -40,7 +40,7 @@
</script>
{#if inline && value}
<ObjectMention object={value} {disabled} {noUnderline} {onClick} component={tracker.component.EditIssue} />
<ObjectMention object={value} {disabled} {onClick} component={tracker.component.EditIssue} />
{:else if value}
{#if type === 'link'}
<div class="flex-row-center">

View File

@ -49,7 +49,7 @@
{#if value}
{#if inline}
<ObjectMention object={value} {disabled} {noUnderline} {accent} {onClick} />
<ObjectMention object={value} {disabled} {onClick} />
{:else}
<DocNavLink object={value} {disabled} {accent} {noUnderline} {onClick}>
<div class="flex-presenter" use:tooltip={{ label: tracker.string.Milestone }}>

View File

@ -31,6 +31,7 @@
export let shrink: number = 1
export let accent: boolean = false
export let noOverflow: boolean = false
export let inlineReference: boolean = false
let _disabled = disabled || $restrictionStore.disableNavigation
$: _disabled = disabled || $restrictionStore.disableNavigation
@ -58,6 +59,17 @@
$: if (object !== undefined) getHref(object)
</script>
<NavLink disabled={_disabled} {onClick} {noUnderline} {inline} {shrink} {href} {colorInherit} {accent} {noOverflow}>
<NavLink
disabled={_disabled}
{onClick}
{noUnderline}
{inline}
{shrink}
{href}
{colorInherit}
{accent}
{noOverflow}
{inlineReference}
>
<slot />
</NavLink>

View File

@ -16,11 +16,13 @@
import { Class, Doc, Ref } from '@hcengineering/core'
import { getResource, translateCB } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
import { AnyComponent, LabelAndProps, themeStore, tooltip } from '@hcengineering/ui'
import { AnyComponent, Icon, LabelAndProps, themeStore, tooltip } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { getReferenceLabel } from '@hcengineering/text-editor-resources/src/components/extension/reference'
import { classIcon } from '../utils'
import DocNavLink from './DocNavLink.svelte'
import contact from '@hcengineering/contact'
export let _id: Ref<Doc> | undefined = undefined
export let _class: Ref<Class<Doc>> | undefined = undefined
@ -28,9 +30,6 @@
export let title: string = ''
export let component: AnyComponent | undefined = undefined
export let disabled: boolean = false
export let accent: boolean = false
export let noUnderline: boolean = false
export let colorInherit: boolean = false
export let onClick: ((event: MouseEvent) => void) | undefined = undefined
const client = getClient()
@ -58,6 +57,9 @@
doc = object
}
$: icon =
doc !== undefined && !hierarchy.isDerived(doc._class, contact.class.Contact) ? classIcon(client, doc._class) : null
$: void updateDocTitle(doc)
$: void updateDocTooltip(doc)
$: void updateDocLabel(doc, _class)
@ -113,19 +115,9 @@
</script>
{#if displayTitle}
<DocNavLink
object={doc}
component={docComponent}
{disabled}
{accent}
{colorInherit}
{noUnderline}
inline
noOverflow
{onClick}
>
<span class="antiMention" class:reference={!disabled} use:tooltip={disabled ? undefined : docTooltip}>
@{displayTitle}
</span>
</DocNavLink>
<span data-type={'reference'} data-id={doc?._id} data-objectclass={doc?._class} data-label={displayTitle}>
<DocNavLink object={doc} component={docComponent} {disabled} inlineReference {onClick}>
{#if icon}<Icon {icon} size="small" />{' '}{:else}@{/if}{displayTitle}
</DocNavLink>
</span>
{/if}

View File

@ -287,7 +287,7 @@ export class DocumentContentPage extends CommonPage {
}
async checkReferenceInTheText (label: string): Promise<void> {
await expect(this.page.locator('span', { hasText: '@' + label })).toHaveAttribute('data-type', 'reference')
await expect(this.page.locator('span.antiMention', { hasText: label })).toHaveAttribute('data-type', 'reference')
}
async executeMoreAction (action: string): Promise<void> {

View File

@ -181,7 +181,7 @@ export class CommonTrackerPage extends CalendarPage {
}
async openLinkFromActivitiesByText (linkText: string): Promise<void> {
await this.linkInActivity().filter({ hasText: linkText }).click()
await this.linkInActivity().filter({ hasText: linkText }).first().click()
}
async addCommentWithImage (comment: string, fileName: string): Promise<void> {

View File

@ -112,12 +112,12 @@ test.describe('Mentions issue tests', () => {
const secondId = await issuesPage.getIssueId(backlinkIssueSecond.title)
await issuesPage.openIssueByName(backlinkIssueSecond.title)
await issuesDetailsPage.addMentions(defaultId)
await issuesDetailsPage.checkCommentExist(`@${defaultId}`)
await issuesDetailsPage.openLinkFromActivitiesByText(`@${defaultId}`)
await issuesDetailsPage.checkCommentExist(defaultId)
await issuesDetailsPage.openLinkFromActivitiesByText(defaultId)
await issuesDetailsPage.checkIssue(backlinkIssueDefault)
await issuesDetailsPage.addMentions(secondId)
await issuesDetailsPage.checkCommentExist(`@${secondId}`)
await issuesDetailsPage.openLinkFromActivitiesByText(`@${secondId}`)
await issuesDetailsPage.checkCommentExist(secondId)
await issuesDetailsPage.openLinkFromActivitiesByText(secondId)
await issuesDetailsPage.checkIssue(backlinkIssueSecond)
await issuesDetailsPage.clickCloseIssueButton()
})