mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-26 13:01:48 +03:00
Issue preview (#2041)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
4e5fc37506
commit
a43d07962b
@ -2,6 +2,10 @@
|
||||
|
||||
## 0.6.26 (upcoming)
|
||||
|
||||
Tracker:
|
||||
|
||||
- Issue preview
|
||||
|
||||
## 0.6.25
|
||||
|
||||
Tracker:
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -138,7 +138,8 @@
|
||||
"Save": "Save",
|
||||
"IncludeItemsThatMatch": "Include items that match",
|
||||
"AnyFilter": "any filter",
|
||||
"AllFilters": "all filters"
|
||||
"AllFilters": "all filters",
|
||||
"NoDescription": "No description"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
@ -88,7 +88,8 @@
|
||||
"Save": "Сохранить",
|
||||
"IncludeItemsThatMatch": "Включить элементы, которые соответствуют",
|
||||
"AnyFilter": "любому фильтру",
|
||||
"AllFilters": "всем фильтрам"
|
||||
"AllFilters": "всем фильтрам",
|
||||
"NoDescription": "Нет описания"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
@ -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} />
|
||||
|
@ -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>
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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]
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user