Issue preview (#2041)

Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
Denis Bykhov 2022-06-09 20:49:10 +06:00 committed by GitHub
parent 4e5fc37506
commit a43d07962b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 151 additions and 45 deletions

View File

@ -2,6 +2,10 @@
## 0.6.26 (upcoming)
Tracker:
- Issue preview
## 0.6.25
Tracker:

View File

@ -358,6 +358,10 @@ export function createModel (builder: Builder): void {
presenter: tracker.component.IssuePresenter
})
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.PreviewPresenter, {
presenter: tracker.component.IssuePreview
})
builder.mixin(tracker.class.TypeIssuePriority, core.class.Class, view.mixin.AttributePresenter, {
presenter: tracker.component.PriorityPresenter
})

View File

@ -138,7 +138,8 @@
"Save": "Save",
"IncludeItemsThatMatch": "Include items that match",
"AnyFilter": "any filter",
"AllFilters": "all filters"
"AllFilters": "all filters",
"NoDescription": "No description"
},
"status": {}
}

View File

@ -88,7 +88,8 @@
"Save": "Сохранить",
"IncludeItemsThatMatch": "Включить элементы, которые соответствуют",
"AnyFilter": "любому фильтру",
"AllFilters": "всем фильтрам"
"AllFilters": "всем фильтрам",
"NoDescription": "Нет описания"
},
"status": {}
}

View File

@ -253,7 +253,6 @@
width="min-content"
size="small"
shouldShowLabel={true}
tooltipFill={false}
on:change={({ detail }) => (object.status = detail)}
/>
<PrioritySelector priority={object.priority} onPriorityChange={handlePriorityChanged} />

View File

@ -0,0 +1,111 @@
<!--
// 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
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Ref, SortingOrder } from '@anticrm/core'
import { createQuery, getClient, MessageViewer } from '@anticrm/presentation'
import { Issue, IssueStatus, Team } from '@anticrm/tracker'
import tracker from '../../plugin'
import { Label } from '@anticrm/ui'
import AssigneeEditor from './AssigneeEditor.svelte'
import PriorityEditor from './PriorityEditor.svelte'
import StatusEditor from './StatusEditor.svelte'
export let object: Issue
let issue: Issue | undefined
const client = getClient()
$: space = object.space
const issueQuery = createQuery()
const spaceQuery = createQuery()
const statusesQuery = createQuery()
issueQuery.query(
object._class,
{ _id: object._id },
(res) => {
issue = res[0]
},
{ limit: 1 }
)
let statuses: IssueStatus[] = []
$: statusesQuery.query(
tracker.class.IssueStatus,
{ attachedTo: space },
(res) => {
statuses = res
},
{
lookup: { category: tracker.class.IssueStatusCategory },
sort: { rank: SortingOrder.Ascending }
}
)
let currentTeam: Team | undefined
$: spaceQuery.query(tracker.class.Team, { _id: space }, (res) => ([currentTeam] = res))
$: issueName = currentTeam && issue && `${currentTeam.identifier}-${issue.number}`
const limit: number = 350
let cHeight: number
let parent: Issue | undefined
async function getParent (attachedTo: Ref<Issue> | undefined): Promise<void> {
if (attachedTo === undefined || attachedTo === tracker.ids.NoParent) {
parent = undefined
} else {
parent = await client.findOne(tracker.class.Issue, { _id: attachedTo })
}
}
$: getParent(issue?.attachedTo as Ref<Issue>)
</script>
<div class="w-165">
{#if parent}
<div class="mb-4 ml-2">{parent.title}</div>
{/if}
{#if issue}
<div class="fs-title text-xl ml-2">{issueName} {issue.title}</div>
<div class="flex mt-4 mb-4">
<StatusEditor value={issue} {statuses} shouldShowLabel kind={'transparent'} />
<PriorityEditor value={issue} shouldShowLabel />
{#if issue.assignee}
<AssigneeEditor value={issue} tooltipFill={false} />
{/if}
</div>
{#if issue.description}
<div class="descr ml-2" class:mask={cHeight >= limit} bind:clientHeight={cHeight}>
<MessageViewer message={issue.description} />
</div>
{:else}
<div class="ml-2 content-trans-color">
<Label label={tracker.string.NoDescription} />
</div>
{/if}
{/if}
</div>
<style lang="scss">
.descr {
overflow: hidden;
max-height: 25rem;
&.mask {
mask: linear-gradient(to top, rgba(0, 0, 0, 0) 0, black 5rem);
}
}
</style>

View File

@ -49,20 +49,16 @@
</script>
{#if value}
{#if isEditable}
<div class="clear-mins" use:tooltip={{ label: tracker.string.SetPriority }}>
<PrioritySelector
{kind}
{size}
{width}
{justify}
{isEditable}
{shouldShowLabel}
priority={value.priority}
onPriorityChange={handlePriorityChanged}
/>
</div>
{:else}
<PrioritySelector {kind} {size} {width} {justify} {isEditable} {shouldShowLabel} priority={value.priority} />
{/if}
<div class="clear-mins" use:tooltip={isEditable ? { label: tracker.string.SetPriority } : undefined}>
<PrioritySelector
{kind}
{size}
{width}
{justify}
{isEditable}
{shouldShowLabel}
bind:priority={value.priority}
onPriorityChange={handlePriorityChanged}
/>
</div>
{/if}

View File

@ -17,7 +17,7 @@
import { AttachedData, Ref, SortingOrder, WithLookup } from '@anticrm/core'
import { Issue, IssueStatus } from '@anticrm/tracker'
import { getClient } from '@anticrm/presentation'
import { Tooltip, TooltipAlignment } from '@anticrm/ui'
import { tooltip, TooltipAlignment } from '@anticrm/ui'
import type { ButtonKind, ButtonSize } from '@anticrm/ui'
import tracker from '../../plugin'
import StatusSelector from '../StatusSelector.svelte'
@ -27,7 +27,6 @@
export let isEditable: boolean = true
export let shouldShowLabel: boolean = false
export let tooltipAlignment: TooltipAlignment | undefined = undefined
export let tooltipFill = true
export let kind: ButtonKind = 'link'
export let size: ButtonSize = 'large'
@ -62,21 +61,10 @@
</script>
{#if value && statuses}
{#if isEditable}
<Tooltip label={tracker.string.SetStatus} direction={tooltipAlignment} fill={tooltipFill}>
<StatusSelector
{kind}
{size}
{width}
{justify}
{isEditable}
{shouldShowLabel}
{statuses}
selectedStatusId={value.status}
onStatusChange={handleStatusChanged}
/>
</Tooltip>
{:else}
<div
class="clear-mins"
use:tooltip={isEditable ? { label: tracker.string.SetStatus, direction: tooltipAlignment } : undefined}
>
<StatusSelector
{kind}
{size}
@ -85,8 +73,8 @@
{isEditable}
{shouldShowLabel}
{statuses}
selectedStatusId={value.status}
bind:selectedStatusId={value.status}
onStatusChange={handleStatusChanged}
/>
{/if}
</div>
{/if}

View File

@ -51,6 +51,7 @@ import EditIssue from './components/issues/edit/EditIssue.svelte'
import NewIssueHeader from './components/NewIssueHeader.svelte'
import ListView from './components/issues/ListView.svelte'
import IssuesView from './components/issues/IssuesView.svelte'
import IssuePreview from './components/issues/IssuePreview.svelte'
export default async (): Promise<Resources> => ({
component: {
@ -87,7 +88,8 @@ export default async (): Promise<Resources> => ({
SetParentIssueActionPopup,
EditProject,
IssuesView,
ListView
ListView,
IssuePreview
},
function: {
ProjectVisible: () => false

View File

@ -155,7 +155,8 @@ export default mergeIds(trackerId, tracker, {
Save: '' as IntlString,
IncludeItemsThatMatch: '' as IntlString,
AnyFilter: '' as IntlString,
AllFilters: '' as IntlString
AllFilters: '' as IntlString,
NoDescription: '' as IntlString
},
component: {
NopeComponent: '' as AnyComponent,
@ -191,7 +192,8 @@ export default mergeIds(trackerId, tracker, {
SetParentIssueActionPopup: '' as AnyComponent,
EditProject: '' as AnyComponent,
IssuesView: '' as AnyComponent,
ListView: '' as AnyComponent
ListView: '' as AnyComponent,
IssuePreview: '' as AnyComponent
},
function: {
ProjectVisible: '' as '' as Resource<(spaces: Space[]) => boolean>

View File

@ -17,7 +17,6 @@
import { ViewContext } from '@anticrm/view'
import { onDestroy } from 'svelte'
import { contextStore } from '../context'
import { previewDocument } from '../selection'
export let context: ViewContext
@ -39,7 +38,6 @@
mode: context.mode,
application: context.application ?? cur[(pos !== -1 ? pos : cur.length) - 1]?.application
}
previewDocument.set(undefined)
if (pos === -1) {
len = cur.length
return [...cur, newCur]

View File

@ -158,8 +158,8 @@
<svelte:window on:keydown={handleKeys} />
{#if $previewDocument !== undefined && presenter}
<div transition:fly|local style:position="fixed" style:right={'0'} style:top={'10rem'} style:z-index={'50000'}>
<div class="antiPanel p-10">
<div transition:fly|local style:position="fixed" style:right={'0'} style:top={'10rem'} style:z-index={'500'}>
<div class="antiPanel p-6">
<Component is={presenter} props={{ object: $previewDocument }} />
</div>
</div>