mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
UBER-538: update ListView layout. Subissues, related issues. (#3467)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
f450e03bb7
commit
ee0754422d
@ -683,18 +683,30 @@ export function createModel (builder: Builder): void {
|
||||
descriptor: view.viewlet.List,
|
||||
config: [
|
||||
{ key: '', displayProps: { fixed: 'left', key: 'app' } },
|
||||
{
|
||||
key: 'state',
|
||||
props: { kind: 'list', size: 'small', shouldShowName: false },
|
||||
displayProps: { excludeByKey: 'state' }
|
||||
},
|
||||
{
|
||||
key: '$lookup.attachedTo',
|
||||
presenter: contact.component.PersonPresenter,
|
||||
label: recruit.string.Talent,
|
||||
sortingKey: '$lookup.attachedTo.name',
|
||||
displayProps: { fixed: 'left', key: 'talent' },
|
||||
props: {
|
||||
_class: recruit.mixin.Candidate,
|
||||
inline: true
|
||||
}
|
||||
},
|
||||
{ key: 'state', displayProps: { fixed: 'left', key: 'state' }, props: { inline: true, showLabel: false } },
|
||||
{ key: 'attachments', displayProps: { key: 'attachments', suffix: true } },
|
||||
{ key: 'comments', displayProps: { key: 'comments', suffix: true } },
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.RelatedIssueSelector,
|
||||
label: tracker.string.Issues,
|
||||
props: { size: 'small' }
|
||||
},
|
||||
{ key: '', displayProps: { grow: true } },
|
||||
{
|
||||
key: '$lookup.space.company',
|
||||
displayProps: { fixed: 'left', key: 'company' },
|
||||
@ -703,15 +715,6 @@ export function createModel (builder: Builder): void {
|
||||
maxWidth: '10rem'
|
||||
}
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.RelatedIssueSelector,
|
||||
label: tracker.string.Issues,
|
||||
displayProps: { fixed: 'left', key: 'issues' }
|
||||
},
|
||||
{ key: '', displayProps: { grow: true } },
|
||||
{ key: 'attachments', displayProps: { fixed: 'left', key: 'attachments' } },
|
||||
{ key: 'comments', displayProps: { fixed: 'left', key: 'comments' } },
|
||||
{
|
||||
key: '$lookup.attachedTo.$lookup.channels',
|
||||
label: contact.string.ContactInfo,
|
||||
@ -719,15 +722,14 @@ export function createModel (builder: Builder): void {
|
||||
props: {
|
||||
length: 'full',
|
||||
size: 'inline'
|
||||
},
|
||||
displayProps: {
|
||||
fixed: 'left',
|
||||
key: 'channels',
|
||||
dividerBefore: true
|
||||
}
|
||||
},
|
||||
{ key: 'modifiedOn', displayProps: { key: 'modified', fixed: 'left', dividerBefore: true } },
|
||||
{ key: 'assignee', displayProps: { key: 'assignee', fixed: 'right' }, props: { shouldShowLabel: false } }
|
||||
{ key: 'modifiedOn', displayProps: { key: 'modified', fixed: 'right', dividerBefore: true } },
|
||||
{
|
||||
key: 'assignee',
|
||||
props: { kind: 'list', shouldShowName: false, avatarSize: 'x-small' },
|
||||
displayProps: { key: 'assignee', fixed: 'right', excludeByKey: 'assignee' }
|
||||
}
|
||||
],
|
||||
options: {
|
||||
lookup: {
|
||||
|
@ -597,20 +597,22 @@ export function createModel (builder: Builder): void {
|
||||
key: '',
|
||||
label: tracker.string.Priority,
|
||||
presenter: tracker.component.PriorityEditor,
|
||||
props: { type: 'priority', kind: 'list', size: 'small' }
|
||||
props: { type: 'priority', kind: 'list', size: 'small' },
|
||||
displayProps: { key: 'subpriority' }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.Issue,
|
||||
presenter: tracker.component.IssuePresenter,
|
||||
props: { type: 'issue' },
|
||||
displayProps: { fixed: 'left' }
|
||||
displayProps: { key: 'subissue', fixed: 'left' }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.Status,
|
||||
presenter: tracker.component.StatusEditor,
|
||||
props: { kind: 'list', size: 'small', justify: 'center' }
|
||||
props: { kind: 'list', size: 'small', justify: 'center' },
|
||||
displayProps: { key: 'substatus' }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
@ -619,12 +621,7 @@ export function createModel (builder: Builder): void {
|
||||
props: { shouldUseMargin: true, showParent: false }
|
||||
},
|
||||
{ key: '', label: tracker.string.SubIssues, presenter: tracker.component.SubIssuesSelector, props: {} },
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.DueDate,
|
||||
presenter: tracker.component.DueDatePresenter,
|
||||
props: { kind: 'list', size: 'small' }
|
||||
},
|
||||
{ key: '', displayProps: { grow: true } },
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.Milestone,
|
||||
@ -640,22 +637,28 @@ export function createModel (builder: Builder): void {
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.DueDate,
|
||||
presenter: tracker.component.DueDatePresenter,
|
||||
displayProps: { key: 'dueDate', optional: true },
|
||||
props: { kind: 'list', size: 'small' }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.Estimation,
|
||||
presenter: tracker.component.EstimationEditor,
|
||||
props: { kind: 'list', size: 'small' },
|
||||
displayProps: { optional: true }
|
||||
props: { kind: 'list', size: 'small' }
|
||||
},
|
||||
{ key: '', displayProps: { grow: true } },
|
||||
{
|
||||
key: 'modifiedOn',
|
||||
presenter: tracker.component.ModificationDatePresenter,
|
||||
displayProps: { fixed: 'right', optional: true }
|
||||
displayProps: { key: 'submodified', fixed: 'right' }
|
||||
},
|
||||
{
|
||||
key: 'assignee',
|
||||
presenter: tracker.component.AssigneeEditor,
|
||||
displayProps: { key: 'assigee', fixed: 'right' },
|
||||
props: { kind: 'list', shouldShowName: false, avatarSize: 'x-small' }
|
||||
}
|
||||
]
|
||||
|
@ -365,6 +365,7 @@
|
||||
|
||||
&.dark { background-color: var(--body-accent); }
|
||||
&.noMargin { margin: 0; }
|
||||
& + & { display: none; }
|
||||
}
|
||||
|
||||
.antiHSpacer {
|
||||
@ -390,7 +391,7 @@
|
||||
align-items: center;
|
||||
height: 2.5rem;
|
||||
min-height: 2.5rem;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
border-bottom: 1px solid var(--theme-divider-color);
|
||||
|
||||
&.high {
|
||||
padding-right: 1rem;
|
||||
@ -403,7 +404,7 @@
|
||||
align-items: center;
|
||||
margin-right: .5rem;
|
||||
height: 2rem;
|
||||
color: var(--caption-color);
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
&__title {
|
||||
min-width: 0;
|
||||
@ -422,7 +423,7 @@
|
||||
min-width: 0;
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
color: var(--caption-color);
|
||||
color: var(--theme-caption-color);
|
||||
background: var(--header-bg-color);
|
||||
border-radius: .5rem .5rem 0 0;
|
||||
}
|
||||
|
@ -896,6 +896,7 @@
|
||||
border-radius: 1.625rem;
|
||||
transition: flex-shrink 0.25s cubic-bezier(0.38, 0.01, 0.33, 1) 0s;
|
||||
|
||||
&:not(:empty) { min-width: 7.5rem; }
|
||||
&:hover {
|
||||
flex-shrink: .5;
|
||||
min-width: initial;
|
||||
@ -903,7 +904,6 @@
|
||||
.label-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
& > *:last-child {
|
||||
|
@ -50,6 +50,7 @@
|
||||
export let iconSize: IconSize = size === 'inline' ? 'inline' : 'small'
|
||||
export let iconRightSize: IconSize = 'x-small'
|
||||
export let short: boolean = false
|
||||
export let shrink: number = 0
|
||||
export let accent: boolean = false
|
||||
export let noFocus: boolean = false
|
||||
|
||||
@ -112,6 +113,7 @@
|
||||
class:short
|
||||
style:width
|
||||
style:height
|
||||
style:flex-shrink={shrink}
|
||||
{title}
|
||||
type={kind === 'primary' ? 'submit' : 'button'}
|
||||
on:click|stopPropagation|preventDefault
|
||||
|
@ -30,6 +30,7 @@
|
||||
import { themeStore } from '@hcengineering/theme'
|
||||
|
||||
export let value: number | undefined
|
||||
export let kind: 'no-border' | 'list' = 'no-border'
|
||||
|
||||
let time: string = ''
|
||||
|
||||
@ -73,6 +74,11 @@
|
||||
: undefined
|
||||
</script>
|
||||
|
||||
<span use:tooltip={{ label: ui.string.TimeTooltip, props: { value: tooltipValue } }} class="overflow-label">
|
||||
<span
|
||||
use:tooltip={{ label: ui.string.TimeTooltip, props: { value: tooltipValue } }}
|
||||
class="overflow-label"
|
||||
class:text-sm={kind === 'list'}
|
||||
class:content-dark-color={kind === 'list'}
|
||||
>
|
||||
{time}
|
||||
</span>
|
||||
|
@ -28,7 +28,7 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div class="flex-between">
|
||||
<div class="flex-between w-full">
|
||||
<div class="caption">
|
||||
<Label {label} />
|
||||
{#if description}
|
||||
|
@ -63,7 +63,7 @@
|
||||
export let focusIndex = -1
|
||||
export let showTooltip: LabelAndProps | PersonLabelTooltip | undefined = undefined
|
||||
export let showNavigate = true
|
||||
export let shouldShowName = true
|
||||
export let shouldShowName: boolean = true
|
||||
export let id: string | undefined = undefined
|
||||
export let short: boolean = false
|
||||
|
||||
|
@ -253,6 +253,42 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if kind === 'list'}
|
||||
{#each displayItems as item, i}
|
||||
<div class="label-wrapper">
|
||||
<Button
|
||||
focusIndex={focusIndex === -1 ? focusIndex : focusIndex + 1 + i}
|
||||
id={item.label}
|
||||
bind:input={btns[i]}
|
||||
icon={item.icon}
|
||||
kind={highlighted.includes(item.provider) ? 'dangerous' : kind}
|
||||
{size}
|
||||
{shape}
|
||||
highlight={item.integration || item.notification}
|
||||
on:click={(ev) => {
|
||||
if (editable && !restricted.includes(item.provider)) {
|
||||
closeTooltip()
|
||||
editChannel(eventToHTMLElement(ev), i, item)
|
||||
} else {
|
||||
dispatch('open', item)
|
||||
}
|
||||
}}
|
||||
showTooltip={{
|
||||
component: opened !== i ? ChannelEditor : undefined,
|
||||
props: {
|
||||
value: item.value,
|
||||
placeholder: item.placeholder,
|
||||
editable: editable !== undefined ? false : undefined,
|
||||
openable: item.presenter ?? item.action ?? false
|
||||
},
|
||||
onUpdate: (result) => {
|
||||
updateTooltip(result, item, i)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<div
|
||||
class="{displayItems.length === 0 ? 'clear-mins' : 'buttons-group'} {kind === 'no-border'
|
||||
? 'xsmall-gap'
|
||||
@ -308,3 +344,4 @@
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { ButtonKind } from '@hcengineering/ui'
|
||||
import { ButtonKind, IconSize } from '@hcengineering/ui'
|
||||
import { PersonLabelTooltip } from '..'
|
||||
import contact from '../plugin'
|
||||
import { employeeByIdStore } from '../utils'
|
||||
@ -14,7 +14,9 @@
|
||||
export let onChange: ((value: Ref<Employee>) => void) | undefined = undefined
|
||||
export let colorInherit: boolean = false
|
||||
export let accent: boolean = false
|
||||
export let inline = false
|
||||
export let inline: boolean = false
|
||||
export let shouldShowName: boolean = true
|
||||
export let avatarSize: IconSize = kind === 'list-header' ? 'smaller' : 'x-small'
|
||||
|
||||
$: employee = value ? $employeeByIdStore.get(value) : undefined
|
||||
|
||||
@ -37,6 +39,8 @@
|
||||
kind={'link'}
|
||||
showNavigate={false}
|
||||
justify={'left'}
|
||||
{shouldShowName}
|
||||
{avatarSize}
|
||||
on:change={({ detail }) => onChange?.(detail)}
|
||||
on:accent-color
|
||||
/>
|
||||
@ -49,8 +53,8 @@
|
||||
shouldShowAvatar
|
||||
shouldShowPlaceholder
|
||||
defaultName={contact.string.NotSpecified}
|
||||
shouldShowName={kind !== 'list'}
|
||||
avatarSize={kind === 'list-header' ? 'smaller' : 'x-small'}
|
||||
shouldShowName={shouldShowName ? kind !== 'list' : false}
|
||||
{avatarSize}
|
||||
{colorInherit}
|
||||
{accent}
|
||||
on:accent-color
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { ButtonKind } from '@hcengineering/ui'
|
||||
import { ButtonKind, IconSize } from '@hcengineering/ui'
|
||||
import { PersonLabelTooltip } from '..'
|
||||
import EmployeeAttributePresenter from './EmployeeAttributePresenter.svelte'
|
||||
|
||||
@ -11,7 +11,9 @@
|
||||
export let onChange: ((value: Ref<Employee>) => void) | undefined = undefined
|
||||
export let colorInherit: boolean = false
|
||||
export let accent: boolean = false
|
||||
export let inline = false
|
||||
export let inline: boolean = false
|
||||
export let shouldShowName: boolean = true
|
||||
export let avatarSize: IconSize = kind === 'secondary' ? 'small' : 'card'
|
||||
</script>
|
||||
|
||||
{#if Array.isArray(value)}
|
||||
@ -25,6 +27,8 @@
|
||||
{inline}
|
||||
{colorInherit}
|
||||
{accent}
|
||||
{shouldShowName}
|
||||
{avatarSize}
|
||||
on:accent-color
|
||||
/>
|
||||
{/each}
|
||||
@ -38,6 +42,8 @@
|
||||
{inline}
|
||||
{colorInherit}
|
||||
{accent}
|
||||
{shouldShowName}
|
||||
{avatarSize}
|
||||
on:accent-color
|
||||
/>
|
||||
{/if}
|
||||
|
@ -29,6 +29,7 @@
|
||||
export let size: ButtonSize = 'small'
|
||||
export let width: string = 'min-content'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let shouldShowName: boolean = true
|
||||
|
||||
let state: State
|
||||
let opened: boolean = false
|
||||
@ -56,12 +57,16 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if kind === 'list' || kind === 'list-header'}
|
||||
<StatePresenter value={state} {shouldShowName} shouldShowTooltip on:click={handleClick} />
|
||||
{:else}
|
||||
<Button {kind} {size} {width} {justify} on:click={handleClick}>
|
||||
<svelte:fragment slot="content">
|
||||
{#if state}
|
||||
<div class="pointer-events-none clear-mins">
|
||||
<StatePresenter value={state} />
|
||||
<StatePresenter value={state} {shouldShowName} />
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
{/if}
|
||||
|
@ -29,6 +29,8 @@
|
||||
export let inline: boolean = false
|
||||
export let disabled: boolean = false
|
||||
export let oneLine: boolean = false
|
||||
export let shouldShowName: boolean = true
|
||||
export let shouldShowTooltip: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -43,22 +45,26 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<div class="flex-presenter" class:inline-presenter={inline}>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="flex-presenter" class:inline-presenter={inline} on:click>
|
||||
{#if shouldShowAvatar}
|
||||
<div
|
||||
class="state-container"
|
||||
class:inline
|
||||
class:mr-2={shouldShowName}
|
||||
style="background-color: {color?.color ?? defaultBackground($themeStore.dark)}"
|
||||
title={shouldShowTooltip ? value.name : undefined}
|
||||
/>
|
||||
{/if}
|
||||
{#if shouldShowName}
|
||||
<span class="overflow-label label" class:nowrap={oneLine} class:no-underline={disabled}>{value.name}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.state-container {
|
||||
flex-shrink: 0;
|
||||
margin-right: 0.5rem;
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, Status, StatusValue } from '@hcengineering/core'
|
||||
import type { ButtonSize } from '@hcengineering/ui'
|
||||
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import { State } from '@hcengineering/task'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
import StateEditor from './StateEditor.svelte'
|
||||
@ -23,14 +23,17 @@
|
||||
|
||||
export let value: Ref<State> | StatusValue
|
||||
export let onChange: ((value: Ref<State>) => void) | undefined = undefined
|
||||
export let kind: ButtonKind = 'link'
|
||||
export let size: ButtonSize = 'medium'
|
||||
export let shouldShowName: boolean = true
|
||||
|
||||
$: state = $statusStore.get(typeof value === 'string' ? value : (value?.values?.[0]?._id as Ref<Status>))
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
{#if onChange !== undefined && state !== undefined}
|
||||
<StateEditor value={state._id} space={state.space} {onChange} kind="link" {size} />
|
||||
<StateEditor value={state._id} space={state.space} {onChange} {kind} {size} {shouldShowName} />
|
||||
{:else}
|
||||
<StatePresenter value={state} on:accent-color />
|
||||
<StatePresenter value={state} {shouldShowName} on:accent-color />
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -39,6 +39,7 @@
|
||||
</script>
|
||||
|
||||
<div class="selectPopup" use:resizeObserver={() => dispatch('changeContent')}>
|
||||
<div class="menu-space" />
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
{#each states as state}
|
||||
@ -55,6 +56,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-space" />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -45,6 +45,6 @@
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 0.75rem;
|
||||
color: var(--content-color);
|
||||
color: var(--theme-dark-color);
|
||||
}
|
||||
</style>
|
||||
|
@ -53,16 +53,16 @@
|
||||
.names {
|
||||
display: inline-flex;
|
||||
min-width: 0;
|
||||
color: var(--content-color);
|
||||
color: var(--theme-dark-color);
|
||||
}
|
||||
|
||||
.name {
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
color: var(--theme-caption-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:active {
|
||||
color: var(--accent-color);
|
||||
color: var(--theme-content-color);
|
||||
}
|
||||
&::before {
|
||||
content: '›';
|
||||
|
@ -32,7 +32,6 @@
|
||||
class="name overflow-label select-text"
|
||||
class:with-margin={shouldUseMargin}
|
||||
class:list={kind === 'list'}
|
||||
style:max-width={showParent ? `${value.parents.length !== 0 ? 95 : 100}%` : '100%'}
|
||||
title={value.title}
|
||||
>
|
||||
<DocNavLink
|
||||
|
@ -166,7 +166,7 @@
|
||||
<ParentsNavigator element={issue} />
|
||||
{/if}
|
||||
|
||||
<span class="ml-4 fs-title select-text-i">
|
||||
<span class="ml-4 fs-title select-text-i overflow-label">
|
||||
{#if embedded}
|
||||
<DocNavLink object={issue}>
|
||||
{#if issueId}{issueId}{/if}
|
||||
|
@ -26,7 +26,8 @@
|
||||
export let issues: Issue[] | undefined = undefined
|
||||
export let viewlet: Viewlet
|
||||
export let viewOptions: ViewOptions
|
||||
export let disableHeader = false
|
||||
export let disableHeader: boolean = false
|
||||
export let compactMode: boolean = false
|
||||
|
||||
// Extra properties
|
||||
export let projects: Map<Ref<Project>, Project> | undefined
|
||||
@ -86,6 +87,7 @@
|
||||
{createItemDialogProps}
|
||||
{createItemLabel}
|
||||
selectedObjectIds={$selectionStore ?? []}
|
||||
{compactMode}
|
||||
on:row-focus={(event) => {
|
||||
listProvider.updateFocus(event.detail ?? undefined)
|
||||
}}
|
||||
|
@ -46,6 +46,7 @@
|
||||
export let shouldSaveDraft: boolean = false
|
||||
|
||||
let isCollapsed = false
|
||||
let listWidth: number
|
||||
|
||||
$: hasSubIssues = issue.subIssues > 0
|
||||
|
||||
@ -84,11 +85,10 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex-between">
|
||||
<div class="flex-between mb-1">
|
||||
{#if hasSubIssues}
|
||||
<Button
|
||||
width="min-content"
|
||||
size="small"
|
||||
kind="transparent"
|
||||
on:click={() => {
|
||||
isCollapsed = !isCollapsed
|
||||
@ -100,16 +100,14 @@
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
{/if}
|
||||
<div class="flex-row-center">
|
||||
<div class="flex-row-center gap-2">
|
||||
{#if viewlet && hasSubIssues && viewOptions}
|
||||
<ViewletSettingButton bind:viewOptions {viewlet} kind={'transparent'} />
|
||||
{/if}
|
||||
{#if hasSubIssues}
|
||||
<Button
|
||||
width="min-content"
|
||||
icon={IconScaleFull}
|
||||
kind={'transparent'}
|
||||
size={'small'}
|
||||
showTooltip={{ label: tracker.string.OpenSubIssues, direction: 'bottom' }}
|
||||
on:click={() => {
|
||||
const filter = createFilter(tracker.class.Issue, 'attachedTo', [issue._id])
|
||||
@ -129,12 +127,10 @@
|
||||
{/if}
|
||||
<Button
|
||||
id="add-sub-issue"
|
||||
width="min-content"
|
||||
icon={hasSubIssues ? IconAdd : undefined}
|
||||
label={hasSubIssues ? undefined : tracker.string.AddSubIssues}
|
||||
labelParams={{ subIssues: 0 }}
|
||||
kind={'transparent'}
|
||||
size={'small'}
|
||||
showTooltip={{ label: tracker.string.AddSubIssues, props: { subIssues: 1 }, direction: 'bottom' }}
|
||||
on:click={() => {
|
||||
isCollapsed = false
|
||||
@ -144,11 +140,10 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
{#if hasSubIssues && viewOptions && viewlet}
|
||||
{#if !isCollapsed}
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
<div class="list" class:collapsed={isCollapsed}>
|
||||
<div class="list" class:collapsed={isCollapsed} bind:clientWidth={listWidth}>
|
||||
<SubIssueList
|
||||
createItemDialog={CreateIssue}
|
||||
createItemLabel={tracker.string.AddIssueTooltip}
|
||||
@ -158,15 +153,16 @@
|
||||
{viewlet}
|
||||
{viewOptions}
|
||||
query={{ attachedTo: issue._id }}
|
||||
compactMode={listWidth <= 600}
|
||||
/>
|
||||
</div>
|
||||
</ExpandCollapse>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.list {
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid var(--divider-color);
|
||||
|
||||
&.collapsed {
|
||||
|
@ -39,6 +39,7 @@
|
||||
export let size: ButtonSize = 'inline'
|
||||
export let justify: 'left' | 'center' = 'left'
|
||||
export let width: string | undefined = 'min-contet'
|
||||
export let compactMode: boolean = false
|
||||
|
||||
let btn: HTMLElement
|
||||
|
||||
@ -130,9 +131,11 @@
|
||||
<svelte:fragment slot="content">
|
||||
{#if subIssues}
|
||||
<div class="flex-row-center content-color text-sm pointer-events-none">
|
||||
<div class="mr-1">
|
||||
<ProgressCircle bind:value={countComplete} bind:max={subIssues.length} size={'inline'} primary />
|
||||
{#if !compactMode}
|
||||
<div class="mr-1-5">
|
||||
<ProgressCircle bind:value={countComplete} bind:max={subIssues.length} size={'small'} primary />
|
||||
</div>
|
||||
{/if}
|
||||
{countComplete}/{subIssues.length}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -27,7 +27,8 @@
|
||||
export let object: Doc
|
||||
export let viewlet: Viewlet
|
||||
export let viewOptions: ViewOptions
|
||||
export let disableHeader = false
|
||||
export let disableHeader: boolean = false
|
||||
export let compactMode: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -59,12 +60,13 @@
|
||||
issues={subIssues}
|
||||
{projects}
|
||||
{disableHeader}
|
||||
{compactMode}
|
||||
createItemDialog={CreateIssue}
|
||||
createItemLabel={tracker.string.AddIssueTooltip}
|
||||
createItemDialogProps={{ relatedTo: object }}
|
||||
/>
|
||||
{:else}
|
||||
<div class="antiSection-empty solid flex-col mt-3">
|
||||
<div class="antiSection-empty solid flex-col">
|
||||
<div class="flex-center content-color">
|
||||
<AddIssueDuo size={'large'} />
|
||||
</div>
|
||||
@ -78,7 +80,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="flex-center pt-3">
|
||||
<div class="flex-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
const client = getClient()
|
||||
let viewlet: Viewlet | undefined
|
||||
let listWidth: number
|
||||
|
||||
const vquery = createQuery()
|
||||
$: vquery.query(view.class.Viewlet, { _id: tracker.viewlet.SubIssues }, (res) => {
|
||||
@ -41,12 +42,12 @@
|
||||
</script>
|
||||
|
||||
{#if $configurationStore.has(trackerId)}
|
||||
<div class="antiSection">
|
||||
<div class="antiSection-header">
|
||||
<div class="antiSection" bind:clientWidth={listWidth}>
|
||||
<div class="antiSection-header mb-3">
|
||||
<div class="antiSection-header__icon">
|
||||
<Icon icon={tracker.icon.Issue} size={'small'} />
|
||||
</div>
|
||||
<span class="antiSection-header__title short">
|
||||
<span class="antiSection-header__title short overflow-label">
|
||||
<Label {label} />
|
||||
</span>
|
||||
{#if headerRemoval}
|
||||
@ -64,7 +65,7 @@
|
||||
{:else}
|
||||
<span class="flex-grow" />
|
||||
{/if}
|
||||
<div class="buttons-group small-gap">
|
||||
<div class="flex-row-center gap-2">
|
||||
{#if viewlet && viewOptions}
|
||||
<ViewletSettingButton bind:viewOptions {viewlet} kind={'transparent'} />
|
||||
{/if}
|
||||
@ -74,13 +75,19 @@
|
||||
label={undefined}
|
||||
labelParams={{ subIssues: 0 }}
|
||||
kind={'transparent'}
|
||||
shape={'circle'}
|
||||
on:click={createIssue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if viewlet}
|
||||
<RelatedIssues {object} {viewOptions} {viewlet} on:add-issue={createIssue} disableHeader={headerRemoval} />
|
||||
<RelatedIssues
|
||||
{object}
|
||||
{viewOptions}
|
||||
{viewlet}
|
||||
on:add-issue={createIssue}
|
||||
disableHeader={headerRemoval}
|
||||
compactMode={listWidth <= 600}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -83,7 +83,6 @@
|
||||
{kind}
|
||||
{size}
|
||||
{shape}
|
||||
{width}
|
||||
{justify}
|
||||
{isEditable}
|
||||
{shouldShowLabel}
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { Button, ButtonSize, TimeSince } from '@hcengineering/ui'
|
||||
|
||||
export let value: number
|
||||
export let kind: 'no-border' | 'link' = 'no-border'
|
||||
export let kind: 'no-border' | 'link' | 'list' = 'no-border'
|
||||
export let readonly = false
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
@ -31,5 +31,5 @@
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
{:else}
|
||||
<TimeSince {value} />
|
||||
<TimeSince {value} {kind} />
|
||||
{/if}
|
||||
|
@ -312,18 +312,26 @@
|
||||
let selected: number | undefined
|
||||
</script>
|
||||
|
||||
<div class="selectPopup p-2">
|
||||
<div class="selectPopup">
|
||||
<div class="menu-space" />
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<div class="flex-row-reverse">
|
||||
<Button on:click={restoreDefault} label={view.string.RestoreDefaults} size={'x-small'} kind={'link'} noFocus />
|
||||
<div class="flex-row-reverse mb-2 mr-2">
|
||||
<Button
|
||||
on:click={restoreDefault}
|
||||
label={view.string.RestoreDefaults}
|
||||
size={'x-small'}
|
||||
kind={'link'}
|
||||
noFocus
|
||||
/>
|
||||
</div>
|
||||
{#each items as item, i}
|
||||
{#if isAttribute(item)}
|
||||
<div
|
||||
class="item"
|
||||
class="menu-item flex-row-center"
|
||||
bind:this={elements[i]}
|
||||
draggable={viewlet.configOptions?.sortable && item.enabled}
|
||||
on:dragstart={(ev) => {
|
||||
@ -353,6 +361,8 @@
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-space" />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.item {
|
||||
|
@ -31,12 +31,13 @@
|
||||
</script>
|
||||
|
||||
{#if viewlet}
|
||||
<div class="flex-row-center">
|
||||
<div class="mr-3"><ViewOptionsButton {viewlet} {kind} {viewOptions} /></div>
|
||||
<div class="flex-row-center gap-2 reverse">
|
||||
<ViewOptionsButton {viewlet} {kind} {viewOptions} />
|
||||
<Button
|
||||
icon={view.icon.Configure}
|
||||
label={view.string.Show}
|
||||
{kind}
|
||||
shrink={1}
|
||||
showTooltip={{ label: view.string.CustomizeView, direction: 'bottom' }}
|
||||
bind:input={btn}
|
||||
on:click={clickHandler}
|
||||
|
@ -1,12 +1,11 @@
|
||||
<span class="root" />
|
||||
<div class="grow-container" />
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
.grow-container {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
flex-shrink: 10;
|
||||
flex-shrink: initial;
|
||||
flex-basis: initial;
|
||||
}
|
||||
</style>
|
||||
|
@ -141,6 +141,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<GrowPresenter />
|
||||
{#each model.filter((p) => p.displayProps?.suffix === true) as attrModel}
|
||||
<ListPresenter
|
||||
{docObject}
|
||||
|
Loading…
Reference in New Issue
Block a user