Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracker: Rewrite AssigneePresenter #1568

Merged
merged 7 commits into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 101 additions & 11 deletions plugins/contact-resources/src/components/PersonContent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,125 @@
<script lang="ts">
import { formatName, Person } from '@anticrm/contact'
import { Hierarchy } from '@anticrm/core'
import { IntlString } from '@anticrm/platform'
import { Avatar } from '@anticrm/presentation'
import { getPanelURI } from '@anticrm/ui'
import { getPanelURI, Label } from '@anticrm/ui'
import view from '@anticrm/view'

export let value: Person | undefined
export let inline: boolean = false
export let isInteractive = true
export let shouldShowName = true
export let shouldShowPlaceholder = false

const avatarSize = 'x-small'
export let defaultName: IntlString | undefined = undefined
export let avatarSize: 'inline' | 'tiny' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' = 'x-small'
export let onEdit: ((event: MouseEvent) => void) | undefined = undefined
</script>

{#if value || shouldShowPlaceholder}
{#if !isInteractive}
{#if value}
<div class="contentPresenter mContentPresenterNotInteractive">
<div class="eContentPresenterIcon">
<Avatar size={avatarSize} avatar={value.avatar} />
</div>
{#if shouldShowName}
<span class="eContentPresenterLabel">{formatName(value.name)}</span>
{/if}
</div>
{:else}
<div class="contentPresenter mContentPresenterNotInteractive">
<div class="eContentPresenterIcon">
<Avatar size={avatarSize} avatar={undefined} />
</div>
{#if defaultName}
<div class="eContentPresenterLabel">
<Label label={defaultName} />
</div>
{/if}
</div>
{/if}
{:else if value || shouldShowPlaceholder}
{#if onEdit}
<div class="contentPresenter" class:inline-presenter={inline} on:click={onEdit}>
<div class="eContentPresenterIcon">
<Avatar size={avatarSize} avatar={value?.avatar} />
</div>
{#if value && shouldShowName}
<span class="eContentPresenterLabel">{formatName(value.name)}</span>
{/if}
</div>
{:else if value}
<a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use https://svelte.dev/tutorial/svelte-element and you could simplify logic without having duplicates.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

class="flex-presenter"
class="contentPresenter"
class:inline-presenter={inline}
href="#{getPanelURI(view.component.EditDoc, value._id, Hierarchy.mixinOrClass(value), 'content')}"
>
<div class="icon">
<Avatar size={avatarSize} avatar={value?.avatar} />
<div class="eContentPresenterIcon">
<Avatar size={avatarSize} avatar={value.avatar} />
</div>
{#if shouldShowName}
<span class="label">{formatName(value.name)}</span>
<span class="eContentPresenterLabel">{formatName(value.name)}</span>
{/if}
</a>
{:else}
<div class="icon">
{/if}
{:else}
<div class="contentPresenter" on:click={onEdit}>
<div class="eContentPresenterIcon">
<Avatar size={avatarSize} avatar={undefined} />
</div>
{/if}
{#if defaultName}
<Label label={defaultName} />
{/if}
</div>
{/if}

<style lang="scss">
.contentPresenter {
display: flex;
align-items: center;
flex-wrap: nowrap;
cursor: pointer;

&.mContentPresenterNotInteractive {
cursor: default;

&:hover {
.eContentPresenterIcon {
color: var(--theme-content-dark-color);
}
.eContentPresenterLabel {
text-decoration: none;
color: var(--theme-content-accent-color);
}
}
}
.eContentPresenterIcon {
margin-right: 0.5rem;
color: var(--theme-content-dark-color);
}
.eContentPresenterLabel {
min-width: 0;
font-weight: 500;
text-align: left;
color: var(--theme-content-accent-color);

overflow: hidden;
visibility: visible;
display: -webkit-box;
/* autoprefixer: ignore next */
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
user-select: none;
}
&:hover {
.eContentPresenterIcon {
color: var(--theme-caption-color);
}
.eContentPresenterLabel {
text-decoration: underline;
color: var(--theme-caption-color);
}
}
}
</style>
31 changes: 26 additions & 5 deletions plugins/contact-resources/src/components/PersonPresenter.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 Hardcore Engineering Inc.
// Copyright © 2022 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
Expand All @@ -20,10 +19,14 @@
import PersonContent from './PersonContent.svelte'
export let value: Person
export let inline: boolean = false
export let inline = false
export let isInteractive = true
export let shouldShowName = true
export let shouldShowPlaceholder = false
export let defaultName: IntlString | undefined = undefined
export let tooltipLabels: { personLabel: IntlString; placeholderLabel?: IntlString } | undefined = undefined
export let avatarSize: 'inline' | 'tiny' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' = 'x-small'
export let onEdit: ((event: MouseEvent) => void) | undefined = undefined
</script>

{#if value || shouldShowPlaceholder}
Expand All @@ -32,9 +35,27 @@
label={value ? tooltipLabels.personLabel : tooltipLabels.placeholderLabel}
props={{ value: formatName(value?.name) }}
>
<PersonContent {inline} {value} {shouldShowName} {shouldShowPlaceholder} />
<PersonContent
{value}
{inline}
{onEdit}
{avatarSize}
{defaultName}
{isInteractive}
{shouldShowName}
{shouldShowPlaceholder}
/>
</Tooltip>
{:else}
<PersonContent {inline} {value} {shouldShowName} {shouldShowPlaceholder} />
<PersonContent
{value}
{inline}
{onEdit}
{avatarSize}
{defaultName}
{isInteractive}
{shouldShowName}
{shouldShowPlaceholder}
/>
{/if}
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -13,56 +13,46 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Employee, formatName } from '@anticrm/contact'
import { Ref, WithLookup } from '@anticrm/core'
import contact, { Employee } from '@anticrm/contact'
import { Class, Doc, Ref } from '@anticrm/core'
import { Issue, Team } from '@anticrm/tracker'
import { Avatar, UsersPopup, getClient } from '@anticrm/presentation'
import { eventToHTMLElement, showPopup, Tooltip } from '@anticrm/ui'

import { UsersPopup, getClient } from '@anticrm/presentation'
import { AttributeModel } from '@anticrm/view'
import { eventToHTMLElement, showPopup } from '@anticrm/ui'
import { getObjectPresenter } from '@anticrm/view-resources'
import { IntlString } from '@anticrm/platform'
import tracker from '../../plugin'
import { IntlString, translate } from '@anticrm/platform'
import { onMount } from 'svelte'

export let value: WithLookup<Issue>
export let employees: (WithLookup<Employee> | undefined)[] = []
export let value: Employee | null
export let issueId: Ref<Issue>
export let defaultClass: Ref<Class<Doc>> | undefined = undefined
export let currentSpace: Ref<Team> | undefined = undefined
export let isEditable: boolean = true
export let shouldShowLabel: boolean = false
export let defaultName: IntlString | undefined = undefined

const client = getClient()

let defaultNameString: string = ''

$: employee = (value?.$lookup?.assignee ?? employees.find(x => x?._id === value?.assignee)) as Employee | undefined
$: avatar = employee?.avatar
$: formattedName = employee?.name ? formatName(employee.name) : defaultNameString
$: label = employee ? tracker.string.AssignedTo : tracker.string.AssignTo

$: getDefaultNameString = async () => {
if (!defaultName) {
return
}

const result = await translate(defaultName, {})

if (!result) {
return
let presenter: AttributeModel | undefined

$: if (value || defaultClass) {
if (value) {
getObjectPresenter(client, value._class, { key: '' }).then((p) => {
presenter = p
})
} else if (defaultClass) {
getObjectPresenter(client, defaultClass, { key: '' }).then((p) => {
presenter = p
})
}

defaultNameString = result
}

onMount(() => {
getDefaultNameString()
})

const handleAssigneeChanged = async (result: Employee | null | undefined) => {
if (!isEditable || result === undefined) {
return
}

const currentIssue = await client.findOne(tracker.class.Issue, { space: currentSpace, _id: value._id })
const currentIssue = await client.findOne(tracker.class.Issue, { space: currentSpace, _id: issueId })

if (currentIssue === undefined) {
return
Expand All @@ -81,7 +71,7 @@
UsersPopup,
{
_class: contact.class.Employee,
selected: employee?._id,
selected: value?._id,
allowDeselect: true,
placeholder: tracker.string.AssignTo
},
Expand All @@ -91,36 +81,16 @@
}
</script>

{#if isEditable}
<Tooltip {label} props={{ value: formattedName }}>
<div class="flex-presenter" on:click={handleAssigneeEditorOpened}>
<div class="icon">
<Avatar size={'tiny'} {avatar} />
</div>
{#if shouldShowLabel}
<div class="label nowrap ml-2">
{formattedName}
</div>
{/if}
</div>
</Tooltip>
{:else}
<div class="presenter">
<div class="icon">
<Avatar size={'tiny'} {avatar} />
</div>
{#if shouldShowLabel}
<div class="label nowrap ml-2">
{formattedName}
</div>
{/if}
</div>
{#if presenter}
<svelte:component
this={presenter.presenter}
{value}
{defaultName}
avatarSize={'tiny'}
isInteractive={true}
shouldShowPlaceholder={true}
shouldShowName={shouldShowLabel}
onEdit={handleAssigneeEditorOpened}
tooltipLabels={{ personLabel: tracker.string.AssignedTo, placeholderLabel: tracker.string.AssignTo }}
/>
{/if}

<style lang="scss">
.presenter {
display: flex;
align-items: center;
flex-wrap: nowrap;
}
</style>
Loading