mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
USER-79: fixed the sidebar in the Panel. Update IssuePreview layout. (#3201)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
cc3afc40a3
commit
a5a464a112
@ -165,9 +165,11 @@
|
|||||||
{#if $$slots.actions}
|
{#if $$slots.actions}
|
||||||
<div class="popupPanel-body__aside-header">
|
<div class="popupPanel-body__aside-header">
|
||||||
{#if $$slots['actions-label']}
|
{#if $$slots['actions-label']}
|
||||||
<span class="fs-bold w-27 mr-6"><slot name="actions-label" /></span>
|
<span class="fs-bold w-27 mr-4"><slot name="actions-label" /></span>
|
||||||
|
{:else if $$slots.actions}
|
||||||
|
<span class="fs-bold w-27 mr-4" />
|
||||||
{/if}
|
{/if}
|
||||||
<div class="buttons-group xsmall-gap flex flex-grow">
|
<div class="buttons-group xsmall-gap">
|
||||||
<slot name="actions" />
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -177,7 +179,7 @@
|
|||||||
<slot name="custom-attributes" direction="column" />
|
<slot name="custom-attributes" direction="column" />
|
||||||
{:else if $$slots.attributes}<slot name="attributes" direction="column" />{/if}
|
{:else if $$slots.attributes}<slot name="attributes" direction="column" />{/if}
|
||||||
{#if $$slots.aside}<slot name="aside" />{/if}
|
{#if $$slots.aside}<slot name="aside" />{/if}
|
||||||
<div class="h-2 min-h-2 max-h-2" />
|
<div class="space-divider bottom" />
|
||||||
</Scroller>
|
</Scroller>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
export let justify: 'left' | 'center' = 'left'
|
export let justify: 'left' | 'center' = 'left'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="popupPanel-body__aside-grid">
|
<div class="popupPanel-body__aside-grid inCollapsed">
|
||||||
{#each keys as key (typeof key === 'string' ? key : key.key)}
|
{#each keys as key (typeof key === 'string' ? key : key.key)}
|
||||||
<AttributeBarEditor {key} {_class} {object} {showHeader} {readonly} {draft} on:update />
|
<AttributeBarEditor {key} {_class} {object} {showHeader} {readonly} {draft} on:update />
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let size: 'small' | 'medium' | 'large'
|
export let size: 'x-small' | 'small' | 'medium' | 'large'
|
||||||
const fill: string = 'currentColor'
|
const fill: string = 'currentColor'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ export { default as IndexedDocumentPreview } from './components/IndexedDocumentP
|
|||||||
export { default as IndexedDocumentCompare } from './components/IndexedDocumentCompare.svelte'
|
export { default as IndexedDocumentCompare } from './components/IndexedDocumentCompare.svelte'
|
||||||
export { default as DraggableList } from './components/DraggableList.svelte'
|
export { default as DraggableList } from './components/DraggableList.svelte'
|
||||||
export { default as NavLink } from './components/NavLink.svelte'
|
export { default as NavLink } from './components/NavLink.svelte'
|
||||||
|
export { default as IconForward } from './components/icons/Forward.svelte'
|
||||||
export { default } from './plugin'
|
export { default } from './plugin'
|
||||||
export * from './types'
|
export * from './types'
|
||||||
export * from './utils'
|
export * from './utils'
|
||||||
|
@ -496,7 +496,9 @@ input.search {
|
|||||||
.pl-2 { padding-left: .5rem; }
|
.pl-2 { padding-left: .5rem; }
|
||||||
.pl-3 { padding-left: .75rem; }
|
.pl-3 { padding-left: .75rem; }
|
||||||
.pl-4 { padding-left: 1rem; }
|
.pl-4 { padding-left: 1rem; }
|
||||||
|
.pl-7 { padding-left: 1.75rem; }
|
||||||
.pl-8 { padding-left: 2rem; }
|
.pl-8 { padding-left: 2rem; }
|
||||||
|
.pl-9 { padding-left: 2.25rem; }
|
||||||
.pl-10 { padding-left: 2.5rem; }
|
.pl-10 { padding-left: 2.5rem; }
|
||||||
.pr-1 { padding-right: .25rem; }
|
.pr-1 { padding-right: .25rem; }
|
||||||
.pr-2 { padding-right: .5rem; }
|
.pr-2 { padding-right: .5rem; }
|
||||||
@ -602,6 +604,7 @@ input.search {
|
|||||||
.h-7 { height: 1.75rem; }
|
.h-7 { height: 1.75rem; }
|
||||||
.h-8 { height: 2rem; }
|
.h-8 { height: 2rem; }
|
||||||
.h-9 { height: 2.25rem; }
|
.h-9 { height: 2.25rem; }
|
||||||
|
.h-12 { height: 3rem; }
|
||||||
.h-14 { height: 3.5rem; }
|
.h-14 { height: 3.5rem; }
|
||||||
.h-16 { height: 4rem; }
|
.h-16 { height: 4rem; }
|
||||||
.h-18 { height: 4.5rem; }
|
.h-18 { height: 4.5rem; }
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
background: var(--theme-popup-color);
|
background: var(--theme-popup-color);
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
box-shadow: var(--card-shadow);
|
box-shadow: var(--theme-popup-shadow);
|
||||||
|
|
||||||
.antiCard-header {
|
.antiCard-header {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -18,18 +18,38 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-width: 400px;
|
min-width: 400px;
|
||||||
|
|
||||||
background: var(--body-accent);
|
background-color: var(--theme-popup-color);
|
||||||
border: 1px solid var(--divider-color);
|
border: 1px solid var(--theme-popup-divider);
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
box-shadow: var(--popup-shadow);
|
box-shadow: var(--theme-popup-shadow);
|
||||||
// left: 1rem;
|
// left: 1rem;
|
||||||
|
|
||||||
|
&.float {
|
||||||
|
position: fixed;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
top: 5rem;
|
||||||
|
right: .5rem;
|
||||||
|
width: 42rem;
|
||||||
|
height: fit-content;
|
||||||
|
min-height: 0;
|
||||||
|
max-height: 32rem;
|
||||||
|
z-index: 500;
|
||||||
|
|
||||||
|
.ap-header {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 1.5rem 1.75rem .5rem;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ac-header {
|
.ac-header {
|
||||||
&.highlight { background-color: var(--accent-bg-color); }
|
&.highlight { background-color: var(--accent-bg-color); }
|
||||||
&.divide { border-bottom: 1px solid var(--divider-color); }
|
&.divide { border-bottom: 1px solid var(--divider-color); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.ad-section-50 {
|
.ad-section-50 {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -39,7 +59,6 @@
|
|||||||
|
|
||||||
&.divide { border-right: 1px solid var(--divider-color); }
|
&.divide { border-right: 1px solid var(--divider-color); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.ad-tools {
|
.ad-tools {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -58,18 +77,16 @@
|
|||||||
.popupPanel {
|
.popupPanel {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
&.embedded {
|
&.rowContent {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
&:not(.rowContent) { flex-direction: column; }
|
||||||
&:not(.embedded) {
|
&.embedded { width: 100%; }
|
||||||
border-radius: .5rem;
|
&:not(.embedded) { border-radius: .5rem; }
|
||||||
// box-shadow: var(--popup-panel-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.popupPanel-title {
|
.popupPanel-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -258,27 +275,29 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: .75rem 2rem;
|
|
||||||
height: 3.5rem;
|
height: 3.5rem;
|
||||||
min-height: 3.5rem;
|
min-height: 3.5rem;
|
||||||
border-bottom: 1px solid var(--theme-divider-color);
|
border-bottom: 1px solid var(--theme-divider-color);
|
||||||
}
|
}
|
||||||
&-tabsheader {
|
&-tabsheader { padding: 0 2rem; }
|
||||||
padding: 0 2rem;
|
&-header {
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: .75rem .75rem .75rem 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-grid {
|
&-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1.5fr;
|
grid-template-columns: 1fr 1.5fr;
|
||||||
grid-auto-flow: row;
|
grid-auto-rows: minmax(2rem, max-content);
|
||||||
justify-content: start;
|
justify-content: start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
row-gap: .25rem;
|
row-gap: .25rem;
|
||||||
column-gap: 1rem;
|
column-gap: 1rem;
|
||||||
margin: .5rem 2rem;
|
margin: .25rem 2rem 0;
|
||||||
width: calc(100% - 4rem);
|
width: calc(100% - 4rem);
|
||||||
height: min-content;
|
height: min-content;
|
||||||
|
|
||||||
|
&.inCollapsed { margin: 1rem 2rem; }
|
||||||
.divider {
|
.divider {
|
||||||
grid-column: 1 / 3;
|
grid-column: 1 / 3;
|
||||||
margin: .75rem -2rem;
|
margin: .75rem -2rem;
|
||||||
@ -289,9 +308,24 @@
|
|||||||
.labelTop { color: var(--theme-dark-color); }
|
.labelTop { color: var(--theme-dark-color); }
|
||||||
.labelTop {
|
.labelTop {
|
||||||
align-self: start;
|
align-self: start;
|
||||||
margin-top: 0.385rem;
|
margin-top: 0.625rem;
|
||||||
}
|
}
|
||||||
}
|
.textPadding { margin-left: .875rem; }
|
||||||
|
}
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 1.25rem 2rem;
|
||||||
|
}
|
||||||
|
.space-divider {
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: .75rem;
|
||||||
|
|
||||||
|
&.bottom { height: 1.25rem; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// &.asideShown .popupPanel-body__main {
|
// &.asideShown .popupPanel-body__main {
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="popupPanel"
|
class="popupPanel panel"
|
||||||
class:embedded
|
class:embedded
|
||||||
use:resizeObserver={(element) => {
|
use:resizeObserver={(element) => {
|
||||||
panelWidth = element.clientWidth
|
panelWidth = element.clientWidth
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
export let invertScroll: boolean = false
|
export let invertScroll: boolean = false
|
||||||
export let horizontal: boolean = false
|
export let horizontal: boolean = false
|
||||||
export let contentDirection: 'vertical' | 'vertical-reverse' | 'horizontal' = 'vertical'
|
export let contentDirection: 'vertical' | 'vertical-reverse' | 'horizontal' = 'vertical'
|
||||||
|
export let gap: 'gap-1' | 'gap-1-5' | 'gap-2' | 'gap-3' | 'gap-around-2' | 'gap-around-4' | undefined = undefined
|
||||||
export let noStretch: boolean = autoscroll
|
export let noStretch: boolean = autoscroll
|
||||||
export let buttons: 'normal' | 'union' | false = false
|
export let buttons: 'normal' | 'union' | false = false
|
||||||
export let shrink: boolean = false
|
export let shrink: boolean = false
|
||||||
@ -497,7 +498,7 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
bind:this={divBox}
|
bind:this={divBox}
|
||||||
class="box"
|
class="box{gap ? ` ${gap}` : ''}"
|
||||||
class:align-center={contentDirection === 'horizontal'}
|
class:align-center={contentDirection === 'horizontal'}
|
||||||
style:padding
|
style:padding
|
||||||
style:flex-direction={contentDirection === 'vertical'
|
style:flex-direction={contentDirection === 'vertical'
|
||||||
@ -689,9 +690,9 @@
|
|||||||
&.vertical {
|
&.vertical {
|
||||||
min-width: 1.5rem 0;
|
min-width: 1.5rem 0;
|
||||||
}
|
}
|
||||||
&.horizontal {
|
// &.horizontal {
|
||||||
margin-right: 2rem;
|
// margin-right: 2rem;
|
||||||
}
|
// }
|
||||||
&.buttons.vertical {
|
&.buttons.vertical {
|
||||||
margin: 1.5rem 0;
|
margin: 1.5rem 0;
|
||||||
}
|
}
|
||||||
|
@ -232,18 +232,18 @@
|
|||||||
&.link {
|
&.link {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
padding: 0 0.875rem;
|
padding: 0 0.875rem;
|
||||||
height: 2rem;
|
color: var(--theme-dark-color);
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
.btn-icon {
|
.btn-icon {
|
||||||
margin-right: 0.5rem;
|
color: var(--theme-darker-color);
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--theme-caption-color);
|
color: var(--theme-content-color);
|
||||||
background-color: var(--theme-button-hovered);
|
background-color: var(--theme-bg-color);
|
||||||
border-color: var(--theme-divider-color);
|
border-color: var(--theme-divider-color);
|
||||||
.btn-icon {
|
.btn-icon {
|
||||||
color: var(--theme-caption-color);
|
color: var(--theme-content-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Attachment } from '@hcengineering/attachment'
|
import { Attachment } from '@hcengineering/attachment'
|
||||||
import { Ref } from '@hcengineering/core'
|
import { Ref } from '@hcengineering/core'
|
||||||
|
import { Scroller } from '@hcengineering/ui'
|
||||||
import AttachmentPreview from './AttachmentPreview.svelte'
|
import AttachmentPreview from './AttachmentPreview.svelte'
|
||||||
|
|
||||||
export let attachments: Attachment[] = []
|
export let attachments: Attachment[] = []
|
||||||
@ -22,11 +23,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if attachments.length}
|
{#if attachments.length}
|
||||||
<div class="flex flex-wrap">
|
<Scroller contentDirection={'horizontal'} horizontal gap={'gap-3'}>
|
||||||
{#each attachments as attachment}
|
{#each attachments as attachment}
|
||||||
<div class="p-2">
|
<AttachmentPreview value={attachment} isSaved={savedAttachmentsIds?.includes(attachment._id) ?? false} />
|
||||||
<AttachmentPreview value={attachment} isSaved={savedAttachmentsIds?.includes(attachment._id) ?? false} />
|
|
||||||
</div>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</Scroller>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -379,7 +379,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
tbody tr:not(:last-child),
|
tbody tr:not(:last-child),
|
||||||
thead th:first-child .fullfill {
|
thead th:first-child .fullfill,
|
||||||
|
tfoot tr {
|
||||||
border-bottom: 1px solid var(--theme-bg-divider-color);
|
border-bottom: 1px solid var(--theme-bg-divider-color);
|
||||||
}
|
}
|
||||||
tfoot tr,
|
tfoot tr,
|
||||||
|
@ -120,21 +120,19 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<svelte:fragment slot="actions">
|
||||||
|
<Button
|
||||||
|
icon={IconMixin}
|
||||||
|
kind={'transparent'}
|
||||||
|
shape={'round'}
|
||||||
|
selected={showAllMixins}
|
||||||
|
on:click={() => {
|
||||||
|
showAllMixins = !showAllMixins
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="attributes" let:direction={dir}>
|
<svelte:fragment slot="attributes" let:direction={dir}>
|
||||||
<div class="flex flex-reverse flex-no-shrink clear-mins">
|
|
||||||
<Button
|
|
||||||
kind={'transparent'}
|
|
||||||
shape={'round'}
|
|
||||||
selected={showAllMixins}
|
|
||||||
on:click={() => {
|
|
||||||
showAllMixins = !showAllMixins
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svelte:fragment slot="content">
|
|
||||||
<IconMixin size={'small'} />
|
|
||||||
</svelte:fragment>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{#if dir === 'column'}
|
{#if dir === 'column'}
|
||||||
<DocAttributeBar
|
<DocAttributeBar
|
||||||
{object}
|
{object}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { DueDatePresenter } from '@hcengineering/ui'
|
import { DueDatePresenter, ButtonSize, ButtonKind } from '@hcengineering/ui'
|
||||||
import { WithLookup } from '@hcengineering/core'
|
import { WithLookup } from '@hcengineering/core'
|
||||||
import { Task } from '@hcengineering/task'
|
import { Task } from '@hcengineering/task'
|
||||||
|
|
||||||
export let object: WithLookup<Task>
|
export let object: WithLookup<Task>
|
||||||
|
export let width: string | undefined = undefined
|
||||||
|
export let size: ButtonSize = 'medium'
|
||||||
|
export let kind: ButtonKind = 'link'
|
||||||
|
export let editable: boolean = true
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
$: shouldIgnoreOverdue = object.doneState != null
|
$: shouldIgnoreOverdue = object.doneState != null
|
||||||
@ -28,9 +32,11 @@
|
|||||||
|
|
||||||
{#if object}
|
{#if object}
|
||||||
<DueDatePresenter
|
<DueDatePresenter
|
||||||
kind={'link'}
|
{kind}
|
||||||
|
{width}
|
||||||
|
{size}
|
||||||
value={object.dueDate}
|
value={object.dueDate}
|
||||||
editable
|
{editable}
|
||||||
onChange={(e) => handleDueDateChanged(e)}
|
onChange={(e) => handleDueDateChanged(e)}
|
||||||
{shouldIgnoreOverdue}
|
{shouldIgnoreOverdue}
|
||||||
/>
|
/>
|
||||||
|
@ -114,7 +114,7 @@
|
|||||||
<div class="ac-header full divide caption-height">
|
<div class="ac-header full divide caption-height">
|
||||||
<div class="ac-header__wrap-title mr-3">
|
<div class="ac-header__wrap-title mr-3">
|
||||||
<span class="ac-header__title"><Label {label} /></span>
|
<span class="ac-header__title"><Label {label} /></span>
|
||||||
<span class="componentTitle">
|
<span class="componentTitle ml-1">
|
||||||
› <Label label={title} />
|
› <Label label={title} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -146,15 +146,8 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ac-header full divide search-start">
|
<div class="ac-header tabs-start full divide">
|
||||||
<div class="ac-header-full small-gap">
|
<TabList items={filterModeList} selected={filterMode} kind={'plain'} on:select={({ detail }) => detail?.action?.()} />
|
||||||
<TabList
|
|
||||||
items={filterModeList}
|
|
||||||
selected={filterMode}
|
|
||||||
kind="normal"
|
|
||||||
on:select={({ detail }) => detail?.action?.()}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FilterBar
|
<FilterBar
|
||||||
|
@ -48,10 +48,8 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="aside">
|
<svelte:fragment slot="aside">
|
||||||
<div class="flex-row p-4 w-60 left-divider">
|
<div class="popupPanel-body__aside-content">
|
||||||
<div class="fs-title text-xl">
|
<EditBox kind={'large-style'} bind:value={component.label} on:change={() => change('label', component.label)} />
|
||||||
<EditBox bind:value={component.label} on:change={() => change('label', component.label)} />
|
|
||||||
</div>
|
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<StyledTextBox
|
<StyledTextBox
|
||||||
alwaysEdit={true}
|
alwaysEdit={true}
|
||||||
@ -61,7 +59,7 @@
|
|||||||
on:value={(evt) => change('description', evt.detail)}
|
on:value={(evt) => change('description', evt.detail)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<DocAttributeBar object={component} mixins={[]} ignoreKeys={['icon', 'label', 'description']} />
|
|
||||||
</div>
|
</div>
|
||||||
|
<DocAttributeBar object={component} mixins={[]} ignoreKeys={['icon', 'label', 'description']} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</IssuesView>
|
</IssuesView>
|
||||||
|
@ -47,6 +47,6 @@
|
|||||||
$: formatTime(value)
|
$: formatTime(value)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span style="white-space: nowrap;">
|
<span class="textPadding" style="white-space: nowrap;">
|
||||||
{time}
|
{time}
|
||||||
</span>
|
</span>
|
||||||
|
@ -18,9 +18,9 @@
|
|||||||
import chunter from '@hcengineering/chunter'
|
import chunter from '@hcengineering/chunter'
|
||||||
import { CommentPopup } from '@hcengineering/chunter-resources'
|
import { CommentPopup } from '@hcengineering/chunter-resources'
|
||||||
import { Ref } from '@hcengineering/core'
|
import { Ref } from '@hcengineering/core'
|
||||||
import { createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
|
import { createQuery, getClient, MessageViewer, IconForward } from '@hcengineering/presentation'
|
||||||
import { Issue, Project } from '@hcengineering/tracker'
|
import { Issue, Project } from '@hcengineering/tracker'
|
||||||
import { Label, resizeObserver, Scroller } from '@hcengineering/ui'
|
import { Label, Scroller, resizeObserver } from '@hcengineering/ui'
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
import AssigneeEditor from './AssigneeEditor.svelte'
|
import AssigneeEditor from './AssigneeEditor.svelte'
|
||||||
import IssueStatusActivity from './IssueStatusActivity.svelte'
|
import IssueStatusActivity from './IssueStatusActivity.svelte'
|
||||||
@ -50,8 +50,7 @@
|
|||||||
$: issueName = currentProject && issue && `${currentProject.identifier}-${issue.number}`
|
$: issueName = currentProject && issue && `${currentProject.identifier}-${issue.number}`
|
||||||
|
|
||||||
const limit: number = 350
|
const limit: number = 350
|
||||||
|
let cHeight: number = 0
|
||||||
let cHeight: number
|
|
||||||
|
|
||||||
let parent: Issue | undefined
|
let parent: Issue | undefined
|
||||||
|
|
||||||
@ -66,73 +65,87 @@
|
|||||||
$: getParent(issue?.attachedTo as Ref<Issue>)
|
$: getParent(issue?.attachedTo as Ref<Issue>)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex">
|
{#if issue}
|
||||||
<Scroller>
|
<div class="ap-header flex-between">
|
||||||
<div class="w-165 scrollerContent">
|
<div class="flex-col">
|
||||||
{#if parent}
|
<div class="flex-row-center gap-1">
|
||||||
<div class="mb-4 ml-2">{parent.title}</div>
|
{#if parent}
|
||||||
{/if}
|
<span class="overflow-label content-color">{parent.title}</span>
|
||||||
{#if issue}
|
<IconForward size={'x-small'} />
|
||||||
<div class="fs-title text-xl ml-2">{issueName} {issue.title}</div>
|
|
||||||
<div class="flex mt-2">
|
|
||||||
<StatusEditor value={issue} shouldShowLabel kind={'transparent'} />
|
|
||||||
<PriorityEditor value={issue} shouldShowLabel />
|
|
||||||
{#if issue.assignee}
|
|
||||||
<AssigneeEditor value={issue} width={'min-content'} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<IssueStatusActivity {issue} />
|
|
||||||
|
|
||||||
<div class="mb-2">
|
|
||||||
<Label label={tracker.string.Description} />:
|
|
||||||
</div>
|
|
||||||
{#if issue.description}
|
|
||||||
<div
|
|
||||||
class="descr ml-2"
|
|
||||||
class:mask={cHeight >= limit}
|
|
||||||
use:resizeObserver={(element) => {
|
|
||||||
cHeight = element.clientHeight
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MessageViewer message={issue.description} />
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="ml-2 content-dark-color">
|
|
||||||
<Label label={tracker.string.NoDescription} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if issue.attachments}
|
|
||||||
<div class="mt-2 mb-2">
|
|
||||||
<Label label={attachment.string.Attachments} />:
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<AttachmentDocList value={issue} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if issue.comments}
|
|
||||||
<div class="mt-2 mb-2">
|
|
||||||
<Label label={chunter.string.Comments} />:
|
|
||||||
</div>
|
|
||||||
<div class="ml-2">
|
|
||||||
<CommentPopup objectId={issue._id} object={issue} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
<span class="content-dark-color">{issueName}</span>
|
||||||
|
</div>
|
||||||
|
<span class="overflow-label text-xl caption-color">{issue.title}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Scroller padding={'0.75rem 1.75rem 0'}>
|
||||||
|
<div class="flex-row-center gap-2 mb-2">
|
||||||
|
<StatusEditor value={issue} shouldShowLabel kind={'secondary'} />
|
||||||
|
<PriorityEditor value={issue} shouldShowLabel kind={'secondary'} />
|
||||||
|
{#if issue.assignee}
|
||||||
|
<AssigneeEditor value={issue} width={'min-content'} kind={'secondary'} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-preview">
|
||||||
|
<IssueStatusActivity {issue} accentHeader />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 mb-2 overflow-label fs-bold content-dark-color">
|
||||||
|
<Label label={tracker.string.Description} />:
|
||||||
|
</div>
|
||||||
|
{#if issue.description}
|
||||||
|
<div class="description-container" class:masked={cHeight > limit} style:max-height={`${limit}px`}>
|
||||||
|
<div class="description-content" use:resizeObserver={(element) => (cHeight = element.clientHeight)}>
|
||||||
|
<MessageViewer message={issue.description} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="overflow-label content-darker-color">
|
||||||
|
<Label label={tracker.string.NoDescription} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if issue.attachments}
|
||||||
|
<div class="mt-6 mb-2 overflow-label fs-bold content-dark-color">
|
||||||
|
<Label label={attachment.string.Attachments} />:
|
||||||
|
</div>
|
||||||
|
<AttachmentDocList value={issue} />
|
||||||
|
{/if}
|
||||||
|
{#if issue.comments}
|
||||||
|
<div class="mt-6 mb-2 overflow-label fs-bold content-dark-color">
|
||||||
|
<Label label={chunter.string.Comments} />:
|
||||||
|
</div>
|
||||||
|
<CommentPopup objectId={issue._id} object={issue} />
|
||||||
|
{/if}
|
||||||
|
<div class="h-3 flex-no-shrink" />
|
||||||
</Scroller>
|
</Scroller>
|
||||||
</div>
|
<div class="h-3 flex-no-shrink" />
|
||||||
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.descr {
|
.description-container {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
height: auto;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
&.mask {
|
&.masked {
|
||||||
mask: linear-gradient(to top, rgba(0, 0, 0, 0) 0, black 5rem);
|
mask-image: linear-gradient(0deg, #0000 0, #000f 4rem);
|
||||||
|
}
|
||||||
|
.description-content {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
height: max-content;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.grid-preview {
|
||||||
.scrollerContent {
|
display: grid;
|
||||||
height: fit-content;
|
grid-template-columns: 1fr 2fr;
|
||||||
max-height: 32rem;
|
grid-auto-rows: minmax(2rem, max-content);
|
||||||
|
justify-content: start;
|
||||||
|
align-items: center;
|
||||||
|
row-gap: 0.25rem;
|
||||||
|
column-gap: 1rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
import core, { Ref, Timestamp, Tx, TxCollectionCUD, TxCreateDoc, TxUpdateDoc, WithLookup } from '@hcengineering/core'
|
import core, { Ref, Timestamp, Tx, TxCollectionCUD, TxCreateDoc, TxUpdateDoc, WithLookup } from '@hcengineering/core'
|
||||||
import { createQuery } from '@hcengineering/presentation'
|
import { createQuery } from '@hcengineering/presentation'
|
||||||
import { Issue, IssueStatus } from '@hcengineering/tracker'
|
import { Issue, IssueStatus } from '@hcengineering/tracker'
|
||||||
import { Label, ticker } from '@hcengineering/ui'
|
import { Label, ticker, Row } from '@hcengineering/ui'
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
import { statusStore } from '@hcengineering/presentation'
|
import { statusStore } from '@hcengineering/presentation'
|
||||||
import Duration from './Duration.svelte'
|
import Duration from './Duration.svelte'
|
||||||
import StatusPresenter from './StatusPresenter.svelte'
|
import StatusPresenter from './StatusPresenter.svelte'
|
||||||
|
|
||||||
export let issue: Issue
|
export let issue: Issue
|
||||||
|
export let accentHeader: boolean = false
|
||||||
|
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
|
|
||||||
@ -86,20 +87,12 @@
|
|||||||
$: updateStatus(txes, $statusStore.byId, $ticker)
|
$: updateStatus(txes, $statusStore.byId, $ticker)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex-row mt-4 mb-4">
|
<Row>
|
||||||
<span class="content-dark-color"><Label label={tracker.string.StatusHistory} />:</span>
|
<span class="label" class:fs-bold={accentHeader} class:content-dark-color={accentHeader}>
|
||||||
<table class="ml-2">
|
<Label label={tracker.string.StatusHistory} />:
|
||||||
{#each displaySt as st}
|
</span>
|
||||||
<tr>
|
</Row>
|
||||||
<td class="flex-row-center mt-2 mb-2">
|
{#each displaySt as st}
|
||||||
<StatusPresenter value={st.status} />
|
<StatusPresenter value={st.status} />
|
||||||
</td>
|
<Duration value={st.duration} />
|
||||||
<td>
|
{/each}
|
||||||
<div class="ml-8 mr-2">
|
|
||||||
<Duration value={st.duration} />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
@ -114,12 +114,12 @@
|
|||||||
</IssuesHeader>
|
</IssuesHeader>
|
||||||
<FilterBar _class={tracker.class.Issue} query={searchQuery} {viewOptions} on:change={(e) => (resultQuery = e.detail)} />
|
<FilterBar _class={tracker.class.Issue} query={searchQuery} {viewOptions} on:change={(e) => (resultQuery = e.detail)} />
|
||||||
<slot name="afterHeader" />
|
<slot name="afterHeader" />
|
||||||
<div class="flex w-full h-full clear-mins">
|
<div class="popupPanel rowContent">
|
||||||
{#if viewlet}
|
{#if viewlet}
|
||||||
<IssuesContent {viewlet} query={resultQuery} {space} {viewOptions} />
|
<IssuesContent {viewlet} query={resultQuery} {space} {viewOptions} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if $$slots.aside !== undefined && asideShown}
|
{#if $$slots.aside !== undefined && asideShown}
|
||||||
<div class="popupPanel-body__aside flex" class:float={asideFloat} class:shown={asideShown}>
|
<div class="popupPanel-body__aside" class:shown={asideShown}>
|
||||||
<slot name="aside" />
|
<slot name="aside" />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -289,28 +289,14 @@
|
|||||||
|
|
||||||
<svelte:fragment slot="custom-attributes">
|
<svelte:fragment slot="custom-attributes">
|
||||||
{#if issue && currentProject}
|
{#if issue && currentProject}
|
||||||
|
<div class="space-divider" />
|
||||||
<ControlPanel {issue} {showAllMixins} />
|
<ControlPanel {issue} {showAllMixins} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="divider" />
|
<div class="popupPanel-body__aside-grid">
|
||||||
<div class="issue-stats">
|
<div class="divider" />
|
||||||
<IssueStatusActivity {issue} />
|
<IssueStatusActivity {issue} />
|
||||||
</div>
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Panel>
|
</Panel>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.divider {
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
height: 1px;
|
|
||||||
background-color: var(--theme-divider-color);
|
|
||||||
}
|
|
||||||
.issue-stats {
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin: 0 2rem;
|
|
||||||
min-width: 0;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -61,10 +61,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="afterHeader">
|
<svelte:fragment slot="afterHeader">
|
||||||
<div class="p-1 ml-6 flex-row-center">
|
<div class="ac-header search-start full divide">
|
||||||
<div class="flex-row-center">
|
<DatePresenter value={milestone.targetDate} kind={'transparent'} size={'medium'} />
|
||||||
<DatePresenter value={milestone.targetDate} kind={'transparent'} />
|
|
||||||
</div>
|
|
||||||
<div class="flex-row-center ml-2">
|
<div class="flex-row-center ml-2">
|
||||||
{#if milestone?.capacity}
|
{#if milestone?.capacity}
|
||||||
<Label label={tracker.string.CapacityValue} params={{ value: milestone?.capacity }} />
|
<Label label={tracker.string.CapacityValue} params={{ value: milestone?.capacity }} />
|
||||||
@ -74,10 +72,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="aside">
|
<svelte:fragment slot="aside">
|
||||||
<div class="flex-grow p-4 w-60 left-divider">
|
<div class="popupPanel-body__aside-content">
|
||||||
<div class="fs-title text-xl">
|
<EditBox kind={'large-style'} bind:value={milestone.label} on:change={() => change('label', milestone.label)} />
|
||||||
<EditBox bind:value={milestone.label} on:change={() => change('label', milestone.label)} />
|
|
||||||
</div>
|
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<StyledTextBox
|
<StyledTextBox
|
||||||
alwaysEdit={true}
|
alwaysEdit={true}
|
||||||
@ -87,7 +83,7 @@
|
|||||||
on:value={(evt) => change('description', evt.detail)}
|
on:value={(evt) => change('description', evt.detail)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<DocAttributeBar object={milestone} mixins={[]} ignoreKeys={['icon', 'label', 'description']} />
|
|
||||||
</div>
|
</div>
|
||||||
|
<DocAttributeBar object={milestone} mixins={[]} ignoreKeys={['icon', 'label', 'description']} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</IssuesView>
|
</IssuesView>
|
||||||
|
@ -147,17 +147,15 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ac-header full divide search-start">
|
<div class="ac-header tabs-start full divide">
|
||||||
<div class="ac-header-full small-gap">
|
<TabList
|
||||||
<TabList
|
items={modeList}
|
||||||
items={modeList}
|
selected={mode}
|
||||||
selected={mode}
|
kind={'plain'}
|
||||||
kind={'normal'}
|
on:select={(result) => {
|
||||||
on:select={(result) => {
|
if (result.detail !== undefined && result.detail.action) result.detail.action()
|
||||||
if (result.detail !== undefined && result.detail.action) result.detail.action()
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FilterBar
|
<FilterBar
|
||||||
|
@ -226,9 +226,7 @@
|
|||||||
<svelte:window on:keydown={handleKeys} />
|
<svelte:window on:keydown={handleKeys} />
|
||||||
|
|
||||||
{#if $previewDocument !== undefined && presenter}
|
{#if $previewDocument !== undefined && presenter}
|
||||||
<div transition:fly|local style:position="fixed" style:right={'0'} style:top={'10rem'} style:z-index={'500'}>
|
<div class="antiPanel float" transition:fly|local>
|
||||||
<div class="antiPanel p-6">
|
<Component is={presenter} props={{ object: $previewDocument }} />
|
||||||
<Component is={presenter} props={{ object: $previewDocument }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 0.75rem;
|
padding: 0 0.125rem 0 0.75rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--theme-dark-color);
|
color: var(--theme-dark-color);
|
||||||
border-top: 1px solid var(--theme-divider-color);
|
border-top: 1px solid var(--theme-divider-color);
|
||||||
|
@ -300,21 +300,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
<svelte:fragment slot="aside-tabs">
|
<svelte:fragment slot="actions">
|
||||||
<div class="flex-row-center flex-reverse flex-grow">
|
<Button
|
||||||
<Button
|
icon={IconMixin}
|
||||||
kind={'transparent'}
|
kind={'transparent'}
|
||||||
shape={'round'}
|
shape={'round'}
|
||||||
selected={showAllMixins}
|
selected={showAllMixins}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
showAllMixins = !showAllMixins
|
showAllMixins = !showAllMixins
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<svelte:fragment slot="icon">
|
|
||||||
<IconMixin size={'small'} />
|
|
||||||
</svelte:fragment>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="attributes" let:direction={dir}>
|
<svelte:fragment slot="attributes" let:direction={dir}>
|
||||||
{#if !headerLoading}
|
{#if !headerLoading}
|
||||||
|
@ -38,7 +38,7 @@ test('create-issue-and-sub-issue', async ({ page }) => {
|
|||||||
await checkIssue(page, props)
|
await checkIssue(page, props)
|
||||||
props.name = `sub${props.name}`
|
props.name = `sub${props.name}`
|
||||||
await createSubissue(page, props)
|
await createSubissue(page, props)
|
||||||
await page.click(`span[title=${props.name}]`)
|
await page.click(`.antiList__row:has-text("${props.name}") .name a`)
|
||||||
await checkIssue(page, props)
|
await checkIssue(page, props)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -138,10 +138,10 @@ export async function checkIssue (page: Page, props: IssueProps): Promise<void>
|
|||||||
const { name, description, status, assignee, labels, priority, component, milestone } = props
|
const { name, description, status, assignee, labels, priority, component, milestone } = props
|
||||||
|
|
||||||
if (name !== undefined) {
|
if (name !== undefined) {
|
||||||
await expect(page.locator('.popupPanel')).toContainText(name)
|
await expect(page.locator('.popupPanel.panel')).toContainText(name)
|
||||||
}
|
}
|
||||||
if (description !== undefined) {
|
if (description !== undefined) {
|
||||||
await expect(page.locator('.popupPanel')).toContainText(description)
|
await expect(page.locator('.popupPanel.panel')).toContainText(description)
|
||||||
}
|
}
|
||||||
const asideLocator = page.locator('.popupPanel-body__aside')
|
const asideLocator = page.locator('.popupPanel-body__aside')
|
||||||
if (status !== undefined) {
|
if (status !== undefined) {
|
||||||
@ -170,7 +170,7 @@ export async function checkIssueFromList (page: Page, issueName: string): Promis
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function openIssue (page: Page, name: string): Promise<void> {
|
export async function openIssue (page: Page, name: string): Promise<void> {
|
||||||
await page.click(`.antiList__row:has-text("${name}") .issuePresenterRoot`, {
|
await page.click(`.antiList__row:has-text("${name}") .name a`, {
|
||||||
timeout: 15000
|
timeout: 15000
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user