UBER-538: update ListView layout. Subissues, related issues. (#3467)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2023-06-29 07:29:12 +03:00 committed by GitHub
parent f450e03bb7
commit ee0754422d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 301 additions and 205 deletions

View File

@ -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: {

View File

@ -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' }
}
]

View File

@ -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;
}

View File

@ -895,7 +895,8 @@
flex-grow: 1;
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 {

View File

@ -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

View File

@ -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>

View File

@ -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}

View File

@ -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

View File

@ -253,58 +253,95 @@
}
</script>
<div
class="{displayItems.length === 0 ? 'clear-mins' : 'buttons-group'} {kind === 'no-border'
? 'xsmall-gap'
: 'xxsmall-gap'}"
class:short={displayItems.length > 4 && length === 'short'}
class:tiny={displayItems.length > 2 && length === 'tiny'}
>
{#if kind === 'list'}
{#each displayItems as item, i}
<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 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}
{#if actions.length > 0 && editable}
<Button
focusIndex={focusIndex === -1 ? focusIndex : focusIndex + 2 + displayItems.length}
id={presentation.string.AddSocialLinks}
bind:input={addBtn}
icon={contact.icon.SocialEdit}
label={displayItems.length === 0 ? presentation.string.AddSocialLinks : undefined}
notSelected={displayItems.length === 0}
{kind}
{size}
{shape}
showTooltip={{ label: presentation.string.AddSocialLinks }}
on:click={showMenu}
/>
{/if}
</div>
{:else}
<div
class="{displayItems.length === 0 ? 'clear-mins' : 'buttons-group'} {kind === 'no-border'
? 'xsmall-gap'
: 'xxsmall-gap'}"
class:short={displayItems.length > 4 && length === 'short'}
class:tiny={displayItems.length > 2 && length === 'tiny'}
>
{#each displayItems as item, i}
<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)
}
}}
/>
{/each}
{#if actions.length > 0 && editable}
<Button
focusIndex={focusIndex === -1 ? focusIndex : focusIndex + 2 + displayItems.length}
id={presentation.string.AddSocialLinks}
bind:input={addBtn}
icon={contact.icon.SocialEdit}
label={displayItems.length === 0 ? presentation.string.AddSocialLinks : undefined}
notSelected={displayItems.length === 0}
{kind}
{size}
{shape}
showTooltip={{ label: presentation.string.AddSocialLinks }}
on:click={showMenu}
/>
{/if}
</div>
{/if}

View File

@ -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

View File

@ -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}

View File

@ -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>
<Button {kind} {size} {width} {justify} on:click={handleClick}>
<svelte:fragment slot="content">
{#if state}
<div class="pointer-events-none clear-mins">
<StatePresenter value={state} />
</div>
{/if}
</svelte:fragment>
</Button>
{#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} {shouldShowName} />
</div>
{/if}
</svelte:fragment>
</Button>
{/if}

View File

@ -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}
<span class="overflow-label label" class:nowrap={oneLine} class:no-underline={disabled}>{value.name}</span>
{#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);

View File

@ -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}

View File

@ -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">

View File

@ -45,6 +45,6 @@
white-space: nowrap;
text-overflow: ellipsis;
font-size: 0.75rem;
color: var(--content-color);
color: var(--theme-dark-color);
}
</style>

View File

@ -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: '';

View File

@ -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

View File

@ -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}

View File

@ -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)
}}

View File

@ -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,29 +140,29 @@
/>
</div>
</div>
<div class="mt-1">
{#if hasSubIssues && viewOptions && viewlet}
{#if !isCollapsed}
<ExpandCollapse isExpanded={!isCollapsed}>
<div class="list" class:collapsed={isCollapsed}>
<SubIssueList
createItemDialog={CreateIssue}
createItemLabel={tracker.string.AddIssueTooltip}
createItemDialogProps={{ space: issue.space, parentIssue: issue, shouldSaveDraft }}
focusIndex={focusIndex === -1 ? -1 : focusIndex + 1}
projects={_projects}
{viewlet}
{viewOptions}
query={{ attachedTo: issue._id }}
/>
</div>
</ExpandCollapse>
{/if}
{#if hasSubIssues && viewOptions && viewlet}
{#if !isCollapsed}
<ExpandCollapse isExpanded={!isCollapsed}>
<div class="list" class:collapsed={isCollapsed} bind:clientWidth={listWidth}>
<SubIssueList
createItemDialog={CreateIssue}
createItemLabel={tracker.string.AddIssueTooltip}
createItemDialogProps={{ space: issue.space, parentIssue: issue, shouldSaveDraft }}
focusIndex={focusIndex === -1 ? -1 : focusIndex + 1}
projects={_projects}
{viewlet}
{viewOptions}
query={{ attachedTo: issue._id }}
compactMode={listWidth <= 600}
/>
</div>
</ExpandCollapse>
{/if}
</div>
{/if}
<style lang="scss">
.list {
padding-top: 0.75rem;
border-top: 1px solid var(--divider-color);
&.collapsed {

View File

@ -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 />
</div>
{#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}

View File

@ -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}

View File

@ -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}

View File

@ -83,7 +83,6 @@
{kind}
{size}
{shape}
{width}
{justify}
{isEditable}
{shouldShowLabel}

View File

@ -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}

View File

@ -312,46 +312,56 @@
let selected: number | undefined
</script>
<div class="selectPopup p-2">
<div class="selectPopup">
<div class="menu-space" />
<div class="scroll">
{#if loading}
<Loading />
{:else}
<div class="flex-row-reverse">
<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"
bind:this={elements[i]}
draggable={viewlet.configOptions?.sortable && item.enabled}
on:dragstart={(ev) => {
if (ev.dataTransfer) {
ev.dataTransfer.effectAllowed = 'move'
ev.dataTransfer.dropEffect = 'move'
}
// ev.preventDefault()
ev.stopPropagation()
selected = i
}}
on:dragover|preventDefault={(e) => dragOver(e, i)}
on:dragend={dragEnd}
>
<ToggleWithLabel
on={item.enabled}
label={item.label}
on:change={(e) => {
change(item, e.detail)
<div class="box">
{#if loading}
<Loading />
{:else}
<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="menu-item flex-row-center"
bind:this={elements[i]}
draggable={viewlet.configOptions?.sortable && item.enabled}
on:dragstart={(ev) => {
if (ev.dataTransfer) {
ev.dataTransfer.effectAllowed = 'move'
ev.dataTransfer.dropEffect = 'move'
}
// ev.preventDefault()
ev.stopPropagation()
selected = i
}}
/>
</div>
{:else}
<div class="antiDivider" />
{/if}
{/each}
{/if}
on:dragover|preventDefault={(e) => dragOver(e, i)}
on:dragend={dragEnd}
>
<ToggleWithLabel
on={item.enabled}
label={item.label}
on:change={(e) => {
change(item, e.detail)
}}
/>
</div>
{:else}
<div class="antiDivider" />
{/if}
{/each}
{/if}
</div>
</div>
<div class="menu-space" />
</div>
<style lang="scss">

View File

@ -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}

View File

@ -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>

View File

@ -141,6 +141,7 @@
{/each}
</div>
{:else}
<GrowPresenter />
{#each model.filter((p) => p.displayProps?.suffix === true) as attrModel}
<ListPresenter
{docObject}