mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-11 09:51:53 +00:00
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:
parent
62d6a76b4a
commit
0bbf5773e5
@ -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
|
||||
})
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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>'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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">
|
||||
|
@ -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) }}>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
},
|
||||
|
@ -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>>
|
||||
}
|
||||
})
|
||||
|
@ -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>,
|
||||
|
@ -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
|
||||
|
@ -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) }}>
|
||||
|
@ -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) }}>
|
||||
|
@ -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">
|
||||
|
@ -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) }}>
|
||||
|
@ -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">
|
||||
|
@ -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) }}>
|
||||
|
@ -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) }}>
|
||||
|
@ -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) }}>
|
||||
|
@ -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">
|
||||
|
@ -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) }}>
|
||||
|
@ -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">
|
||||
|
@ -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}>
|
||||
|
@ -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}>
|
||||
|
@ -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) }}>
|
||||
|
@ -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 }}>
|
||||
|
@ -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) }}>
|
||||
|
@ -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 }}>
|
||||
|
@ -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) }}>
|
||||
|
@ -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) }}>
|
||||
|
@ -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
|
||||
|
@ -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'}>
|
||||
|
@ -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">
|
||||
|
@ -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 }}>
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
|
@ -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> {
|
||||
|
@ -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> {
|
||||
|
@ -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()
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user