Update IssuesList layout (#2317)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov
2022-10-24 17:26:56 +07:00
committed by GitHub
parent bcceb8b5b4
commit 5568eeba3c
28 changed files with 321 additions and 137 deletions
@@ -116,7 +116,11 @@
{loading}
on:click={handleProjectEditorOpened}
><svelte:fragment slot="content">
<span class="{enlargedText ? 'ml-1 text-base fs-bold' : 'text-md'} overflow-label content-accent-color">
<span
class="{enlargedText
? 'ml-1 text-base fs-bold'
: 'text-md'} overflow-label content-accent-color pointer-events-none"
>
<Label label={getEmbeddedLabel(projectText)} />
</span>
</svelte:fragment></Button
@@ -1,5 +1,5 @@
<script lang="ts">
const fill: string = 'var(--theme-caption-color)'
const fill: string = 'var(--caption-color)'
</script>
<svg {fill} viewBox="0 0 6 16" xmlns="http://www.w3.org/2000/svg">
@@ -20,8 +20,7 @@
}
})
let twoRows: boolean
$: twoRows = $deviceInfo.docWidth <= 680
$: twoRows = $deviceInfo.twoRows
</script>
<div class="ac-header withSettings" class:full={!twoRows} class:mini={twoRows}>
@@ -30,7 +30,8 @@
IconMoreH,
showPopup,
Spinner,
tooltip
tooltip,
deviceOptionsStore as deviceInfo
} from '@hcengineering/ui'
import { AttributeModel, BuildModelKey } from '@hcengineering/view'
import { buildModel, FixedColumn, getObjectPresenter, LoadingProps, Menu } from '@hcengineering/view-resources'
@@ -38,6 +39,7 @@
import tracker from '../../plugin'
import { IssuesGroupByKeys, issuesGroupEditorMap, IssuesOrderByKeys, issuesSortOrderMap } from '../../utils'
import CreateIssue from '../CreateIssue.svelte'
import Circles from '../icons/Circles.svelte'
export let _class: Ref<Class<Doc>>
export let currentSpace: Ref<Team> | undefined = undefined
@@ -237,7 +239,7 @@
value: groupByKey ? { [groupByKey]: category } : {},
statuses: groupByKey === 'status' ? statuses : undefined,
issues: groupedIssues[category],
size: 'medium',
width: 'min-content',
kind: 'list-header',
enlargedText: true,
currentSpace
@@ -245,7 +247,11 @@
/>
{/if}
{#if limited.length < items.length}
<span class="text-base content-dark-color ml-4"> {limited.length} / {items.length}</span>
<div class="counter">
{limited.length}
<div class="text-xs mx-1">/</div>
{items.length}
</div>
<ActionIcon
size={'small'}
icon={IconMoreH}
@@ -255,12 +261,15 @@
}}
/>
{:else}
<span class="text-base content-dark-color ml-4">{items.length}</span>
<span class="counter">{items.length}</span>
{/if}
</div>
<div class="clear-mins" use:tooltip={{ label: tracker.string.AddIssueTooltip }}>
<Button icon={IconAdd} kind={'transparent'} on:click={(event) => handleNewIssueAdded(event, category)} />
</div>
<Button
icon={IconAdd}
kind={'transparent'}
showTooltip={{ label: tracker.string.AddIssueTooltip }}
on:click={(event) => handleNewIssueAdded(event, category)}
/>
</div>
{/if}
<ExpandCollapse isExpanded={!isCollapsedMap[toCat(category)]} duration={400}>
@@ -340,23 +349,25 @@
{...attributeModel.props}
/>
{:else if attributeModel.props?.fixed}
<FixedColumn
width={propsWidth[attributeModel.key]}
key={attributeModel.key}
justify={attributeModel.props.fixed}
on:update={(result) => checkWidth(attributeModel.key, result)}
>
<svelte:component
this={attributeModel.presenter}
value={getObjectValue(attributeModel.key, docObject) ?? ''}
groupBy={groupByKey}
{...attributeModel.props}
{statuses}
{currentTeam}
/>
</FixedColumn>
{:else}
<div class="gridElement">
{#if !(attributeModel.props?.optional && $deviceInfo.minWidth)}
<FixedColumn
width={propsWidth[attributeModel.key]}
key={attributeModel.key}
justify={attributeModel.props.fixed}
on:update={(result) => checkWidth(attributeModel.key, result)}
>
<svelte:component
this={attributeModel.presenter}
value={getObjectValue(attributeModel.key, docObject) ?? ''}
groupBy={groupByKey}
{...attributeModel.props}
{statuses}
{currentTeam}
/>
</FixedColumn>
{/if}
{:else if attributeModel.props?.excludeByKey !== groupByKey}
{#if !(attributeModel.props?.optional && $deviceInfo.minWidth)}
<svelte:component
this={attributeModel.presenter}
value={getObjectValue(attributeModel.key, docObject) ?? ''}
@@ -366,9 +377,36 @@
{statuses}
{currentTeam}
/>
</div>
{/if}
{/if}
{/each}
{#if $deviceInfo.minWidth}
<div class="panel-trigger" tabindex="-1">
<Circles />
<div class="space" />
<Circles />
</div>
<div class="hidden-panel gap-2" tabindex="-1">
<div class="header">
<Circles />
<div class="space" />
<Circles />
</div>
{#each itemModels as attributeModel}
{#if attributeModel.props?.optional && attributeModel.props?.excludeByKey !== groupByKey}
<svelte:component
this={attributeModel.presenter}
value={getObjectValue(attributeModel.key, docObject) ?? ''}
issueId={docObject._id}
groupBy={groupByKey}
{...attributeModel.props}
{statuses}
{currentTeam}
/>
{/if}
{/each}
</div>
{/if}
</div>
{/each}
{:else if loadingProps !== undefined}
@@ -403,11 +441,11 @@
.categoryHeader {
position: sticky;
top: 0;
padding: 0 1.5rem 0 2.25rem;
padding: 0 0.75rem 0 2.25rem;
height: 3rem;
min-height: 3rem;
min-width: 0;
background-color: var(--accent-bg-color);
background: var(--header-bg-color);
z-index: 5;
}
@@ -415,10 +453,29 @@
border-bottom: 1px solid var(--accent-bg-color);
}
.listGrid {
.counter {
display: flex;
align-items: center;
padding: 0 1.5rem 0 0.875rem;
flex-wrap: nowrap;
flex-shrink: 0;
margin-left: 1rem;
padding: 0.25rem 0.5rem;
min-width: 1.325rem;
text-align: center;
font-weight: 500;
font-size: 1rem;
line-height: 1rem;
color: var(--accent-color);
background-color: var(--body-color);
border: 1px solid var(--divider-color);
border-radius: 1rem;
}
.listGrid {
position: relative;
display: flex;
align-items: center;
padding: 0 0.75rem 0 0.875rem;
width: 100%;
height: 2.75rem;
min-height: 2.75rem;
@@ -437,6 +494,68 @@
&.mListGridSelected {
background-color: var(--highlight-hover);
}
.hidden-panel,
.panel-trigger {
position: absolute;
display: flex;
align-items: center;
top: 0;
bottom: 0;
height: 100%;
}
.hidden-panel {
overflow: hidden;
right: 0;
width: 80%;
background-color: var(--accent-bg-color);
opacity: 0;
pointer-events: none;
z-index: 2;
transition-property: opacity, width;
transition-duration: 0.15s;
transition-timing-function: var(--timing-main);
.header {
display: flex;
flex-direction: column;
justify-content: center;
margin: 0 0.25rem;
width: 0.375rem;
min-width: 0.375rem;
height: 100%;
opacity: 0.25;
}
}
.panel-trigger {
flex-direction: column;
justify-content: center;
padding: 0 0.125rem;
right: 2.5rem;
width: 0.75rem;
border: 1px solid transparent;
border-radius: 0.25rem;
opacity: 0.1;
z-index: 1;
transition: opacity 0.15s var(--timing-main);
&:focus {
border-color: var(--primary-edit-border-color);
opacity: 0.25;
}
& > * {
pointer-events: none;
}
}
.hidden-panel:focus-within,
.panel-trigger:focus + .hidden-panel {
width: 100%;
opacity: 1;
pointer-events: all;
}
.space {
min-height: 0.1075rem;
}
}
.priorityPresenter,
@@ -27,20 +27,18 @@
</script>
{#if value}
<span class="root" class:with-margin={shouldUseMargin} title={value.title}>
<span class="name cursor-pointer" on:click={handleIssueEditorOpened}>{value.title}</span>
<span class="titlePresenter-container" class:with-margin={shouldUseMargin} title={value.title}>
<span class="name overflow-label cursor-pointer" on:click={handleIssueEditorOpened}>{value.title}</span>
<ParentNamesPresenter {value} />
</span>
{/if}
<style lang="scss">
.root {
.titlePresenter-container {
display: flex;
flex-grow: 0;
min-width: 7rem;
white-space: nowrap;
overflow: hidden;
flex-shrink: 10;
min-width: 1.5rem;
// flex-shrink: 10;
.name {
&:hover {
@@ -99,7 +99,7 @@
</script>
{#if hasSubIssues}
<div class="flex-center clear-mins" bind:this={btn}>
<div class="flex-center flex-no-shrink" bind:this={btn}>
<Button
{width}
{kind}
@@ -49,7 +49,8 @@
{#if (value.project && value.project !== $activeProject && groupBy !== 'project') || shouldShowPlaceholder}
<div
class="clear-mins"
class="min-w-8"
class:minus-margin={kind === 'list-header'}
use:tooltip={{ label: value.project ? tracker.string.MoveToProject : tracker.string.AddToProject }}
>
<ProjectSelector
@@ -68,3 +69,9 @@
/>
</div>
{/if}
<style lang="scss">
.minus-margin {
margin-left: -0.5rem;
}
</style>
@@ -17,7 +17,8 @@
import { IntlString } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Issue, IssueStatus, IssueTemplate, Sprint } from '@hcengineering/tracker'
import { ButtonKind, ButtonShape, ButtonSize, Label, tooltip } from '@hcengineering/ui'
import type { ButtonKind, ButtonSize, ButtonShape } from '@hcengineering/ui'
import { Label, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
import DatePresenter from '@hcengineering/ui/src/components/calendar/DatePresenter.svelte'
import { activeSprint } from '../../issues'
import tracker from '../../plugin'
@@ -30,15 +31,15 @@
export let shouldShowLabel: boolean = true
export let popupPlaceholder: IntlString = tracker.string.MoveToSprint
export let shouldShowPlaceholder = true
export let kind: ButtonKind = 'link'
export let size: ButtonSize = 'large'
export let kind: ButtonKind = 'link'
export let shape: ButtonShape = undefined
export let justify: 'left' | 'center' = 'left'
export let width: string | undefined = '100%'
export let onlyIcon: boolean = false
export let issues: Issue[] | undefined = undefined
export let groupBy: string | undefined = undefined
export let enlargedText = false
export let enlargedText: boolean = false
const client = getClient()
@@ -119,71 +120,92 @@
sprint = res.shift()
})
}
$: twoRows = $deviceInfo.twoRows
</script>
{#if (value.sprint && value.sprint !== $activeSprint && groupBy !== 'sprint') || shouldShowPlaceholder}
<div
class="clear-mins"
use:tooltip={{ label: value.sprint ? tracker.string.MoveToSprint : tracker.string.AddToSprint }}
>
<SprintSelector
{kind}
{size}
{shape}
{width}
{justify}
{isEditable}
{shouldShowLabel}
{popupPlaceholder}
{onlyIcon}
{enlargedText}
value={value.sprint}
onChange={handleSprintIdChanged}
/>
</div>
{/if}
{#if sprint}
{@const now = Date.now()}
<div class="flex-row-center">
<DatePresenter value={sprint.startDate} kind={'transparent'} />
<span class="p-1"> / </span><DatePresenter value={sprint.targetDate} kind={'transparent'} />
</div>
<div class="flex-row-center ml-2">
<!-- Active sprint in time -->
<Label
label={tracker.string.SprintPassed}
params={{
from:
now < sprint.startDate
? 0
: now > sprint.targetDate
? getDayOfSprint(sprint.startDate, sprint.targetDate)
: getDayOfSprint(sprint.startDate, now),
to: getDayOfSprint(sprint.startDate, sprint.targetDate)
}}
/>
</div>
{/if}
{#if issues}
<!-- <Label label={tracker.string.SprintDay} value={}/> -->
<div class="ml-4 flex-row-center" class:showWarning={totalEstimation > (sprint?.capacity ?? 0)}>
<div class="mr-2">
<EstimationProgressCircle value={totalReported} max={totalEstimation} />
<div
class="flex flex-wrap min-w-8"
class:minus-margin={kind === 'list-header'}
style:flex-direction={twoRows ? 'column' : 'row'}
>
{#if (value.sprint && value.sprint !== $activeSprint && groupBy !== 'sprint') || shouldShowPlaceholder}
<div class="flex-row-center" class:minus-margin-vSpace={kind === 'list-header'} style:width>
<SprintSelector
{kind}
{size}
{shape}
{width}
{justify}
{isEditable}
{shouldShowLabel}
{popupPlaceholder}
{onlyIcon}
{enlargedText}
showTooltip={{ label: value.sprint ? tracker.string.MoveToSprint : tracker.string.AddToSprint }}
value={value.sprint}
onChange={handleSprintIdChanged}
/>
</div>
{#if totalReported > 0}
<Label label={tracker.string.TimeSpendValue} params={{ value: totalReported }} />
/
{/if}
<Label label={tracker.string.TimeSpendValue} params={{ value: totalEstimation }} />
{#if sprint?.capacity}
<Label label={tracker.string.CapacityValue} params={{ value: sprint?.capacity }} />
{/if}
</div>
{/if}
{/if}
{#if sprint || issues}
<div class="flex-row-center" class:minus-margin-space={kind === 'list-header'} class:text-sm={twoRows}>
{#if sprint}
{@const now = Date.now()}
<DatePresenter value={sprint.startDate} kind={'transparent'} />
<span class="p-1"> / </span>
<DatePresenter value={sprint.targetDate} kind={'transparent'} />
<div class="w-2 min-w-2" />
<!-- Active sprint in time -->
<Label
label={tracker.string.SprintPassed}
params={{
from:
now < sprint.startDate
? 0
: now > sprint.targetDate
? getDayOfSprint(sprint.startDate, sprint.targetDate)
: getDayOfSprint(sprint.startDate, now),
to: getDayOfSprint(sprint.startDate, sprint.targetDate)
}}
/>
{/if}
{#if issues}
<!-- <Label label={tracker.string.SprintDay} value={}/> -->
<div
class="flex-row-center flex-no-shrink h-6"
class:ml-2={sprint}
class:ml-0-5={!sprint}
class:showWarning={totalEstimation > (sprint?.capacity ?? 0)}
>
<EstimationProgressCircle value={totalReported} max={totalEstimation} />
<div class="w-2 min-w-2" />
{#if totalReported > 0}
<Label label={tracker.string.TimeSpendValue} params={{ value: totalReported }} />
/
{/if}
<Label label={tracker.string.TimeSpendValue} params={{ value: totalEstimation }} />
{#if sprint?.capacity}
<Label label={tracker.string.CapacityValue} params={{ value: sprint?.capacity }} />
{/if}
</div>
{/if}
</div>
{/if}
</div>
<style lang="scss">
.showWarning {
color: var(--warning-color) !important;
}
.minus-margin {
margin-left: -0.5rem;
&-vSpace {
margin: -0.25rem 0;
}
&-space {
margin: -0.25rem 0 -0.25rem 0.5rem;
}
}
</style>
@@ -17,7 +17,7 @@
import { getEmbeddedLabel, IntlString, translate } from '@hcengineering/platform'
import { createQuery } from '@hcengineering/presentation'
import { Project, Sprint } from '@hcengineering/tracker'
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
import type { ButtonKind, ButtonSize, LabelAndProps } from '@hcengineering/ui'
import { Button, ButtonShape, eventToHTMLElement, SelectPopup, showPopup, Label } from '@hcengineering/ui'
import tracker from '../../plugin'
import { sprintStatusAssets } from '../../types'
@@ -36,6 +36,7 @@
export let enlargedText = false
export let useProject: Ref<Project> | undefined = undefined
export let showTooltip: LabelAndProps | undefined = undefined
let selectedSprint: Sprint | undefined
let defaultSprintLabel = ''
@@ -101,6 +102,7 @@
{shape}
{width}
{justify}
{showTooltip}
icon={sprintIcon}
disabled={!isEditable}
on:click={handleSprintEditorOpened}
@@ -112,13 +114,17 @@
{shape}
{width}
{justify}
{showTooltip}
icon={sprintIcon}
disabled={!isEditable}
on:click={handleSprintEditorOpened}
><svelte:fragment slot="content">
<span class="{enlargedText ? 'ml-1 text-base fs-bold' : 'text-md'} overflow-label content-accent-color">
>
<svelte:fragment slot="content">
<span
class="{enlargedText ? 'text-base' : 'text-md'} fs-bold overflow-label content-accent-color pointer-events-none"
>
<Label label={getEmbeddedLabel(sprintText)} />
</span>
</svelte:fragment></Button
>
</svelte:fragment>
</Button>
{/if}