Update Tracker board layout. Removed context menu. (#2016)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2022-06-07 06:23:59 +03:00 committed by GitHub
parent f029789957
commit 922c06d5f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 126 additions and 133 deletions

View File

@ -15,7 +15,7 @@
<script lang="ts">
import core, { AttachedDoc, Class, Doc, DocumentQuery, DocumentUpdate, FindOptions, Ref, Space } from '@anticrm/core'
import { createQuery, getClient } from '@anticrm/presentation'
import { getPlatformColor, ScrollBox } from '@anticrm/ui'
import { getPlatformColor, ScrollBox, Scroller } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import { slide } from 'svelte/transition'
import { DocWithRank, StateType, TypeState } from '../types'
@ -309,41 +309,39 @@
</div>
</div>
{/if}
<div class="scroll" on:dragover on:drop>
<ScrollBox vertical>
<slot name="beforeCard" {state} />
{#each stateObjects as object}
{@const dragged = isDragging && object.it._id === dragCard?._id}
<Scroller padding={'.5rem 0'} on:dragover on:drop>
<slot name="beforeCard" {state} />
{#each stateObjects as object}
{@const dragged = isDragging && object.it._id === dragCard?._id}
<div
transition:slideD|local={{ isDragging }}
class="step-tb75"
on:dragover|preventDefault={(evt) => cardDragOver(evt, object)}
on:drop|preventDefault={(evt) => cardDrop(evt, object)}
>
<div
transition:slideD|local={{ isDragging }}
class="step-tb75"
on:dragover|preventDefault={(evt) => cardDragOver(evt, object)}
on:drop|preventDefault={(evt) => cardDrop(evt, object)}
class="card-container"
class:selection={selection !== undefined ? objects[selection]?._id === object.it._id : false}
class:checked={checkedSet.has(object.it._id)}
on:mouseover={() => dispatch('obj-focus', object.it)}
on:focus={() => {}}
on:contextmenu={(evt) => showMenu(evt, object)}
draggable={true}
class:draggable={true}
on:dragstart
on:dragend
class:dragged
on:dragstart={() => onDragStart(object, state)}
on:dragend={() => {
isDragging = false
}}
>
<div
class="card-container"
class:selection={selection !== undefined ? objects[selection]?._id === object.it._id : false}
class:checked={checkedSet.has(object.it._id)}
on:mouseover={() => dispatch('obj-focus', object.it)}
on:focus={() => {}}
on:contextmenu={(evt) => showMenu(evt, object)}
draggable={true}
class:draggable={true}
on:dragstart
on:dragend
class:dragged
on:dragstart={() => onDragStart(object, state)}
on:dragend={() => {
isDragging = false
}}
>
<slot name="card" object={toAny(object.it)} {dragged} />
</div>
<slot name="card" object={toAny(object.it)} {dragged} />
</div>
{/each}
<slot name="afterCard" {space} {state} />
</ScrollBox>
</div>
</div>
{/each}
<slot name="afterCard" {space} {state} />
</Scroller>
</div>
{/each}
<slot name="afterPanel" />
@ -363,7 +361,7 @@
}
.kanban-content {
display: flex;
margin: 0.5rem 2rem;
padding: 1.5rem 2rem 0;
height: 100%;
}
@ -371,10 +369,13 @@
height: 100%;
}
.card-container {
background-color: var(--body-color);
background-color: var(--board-card-bg-color);
border-radius: 0.25rem;
// transition: box-shadow .15s ease-in-out;
&:hover {
background-color: var(--board-card-bg-hover);
}
&.checked {
background-color: var(--highlight-select);
box-shadow: inset 0 0 1px 1px var(--highlight-select-border);
@ -440,10 +441,5 @@
color: var(--theme-caption-color);
}
}
.scroll {
min-height: 0;
height: 100%;
}
}
</style>

View File

@ -1,4 +1,5 @@
import { Doc } from '@anticrm/core'
import { Asset } from '@anticrm/platform'
/**
* @public
@ -16,4 +17,5 @@ export interface TypeState {
_id: StateType
title: string
color: number
icon?: Asset
}

View File

@ -63,8 +63,8 @@
--highlight-hover: #28292b;
--highlight-select: #252b3a;
--highlight-select-border: #2f3544;
--highlight-select-hover: #323a4e;
--highlight-select-border: #44506b;
--highlight-select-hover: #2c3346;
--scrollbar-bar-color: #303236;
--scrollbar-bar-hover: #8a8f98;

View File

@ -86,7 +86,7 @@ table {
/* Common */
* {
--modal-padding: 1.5rem;
--modal-padding: 1rem;
}
p { user-select: text; }
@ -347,6 +347,7 @@ input.search {
.mt-0-5 { margin-top: .125rem; }
.mt-1 { margin-top: .25rem; }
.mt-2 { margin-top: .5rem; }
.mt-10px { margin-top: .625rem; }
.mt-3 { margin-top: .75rem; }
.mt-4 { margin-top: 1rem; }
.mt-5 { margin-top: 1.25rem; }
@ -473,6 +474,10 @@ input.search {
.square-36 { width: 2.25rem; height: 2.25rem; }
/* --------- */
.svg-inline {
width: 1em;
height: 1em;
}
.svg-x-small {
width: .857em;
height: .857em;

View File

@ -259,3 +259,14 @@
height: 1px;
background-color: var(--divider-color);
}
// Button on selected card in Kanban
.card-container.checked .button.inline.link-bordered {
background-color: var(--highlight-select);
border-color: var(--highlight-select-border);
&:hover {
background-color: var(--highlight-select-hover);
border-color: var(--highlight-select-border);
}
}

View File

@ -106,27 +106,35 @@
{#if icon && !loading}
<div
class="btn-icon pointer-events-none"
class:mr-1={!iconOnly && (kind === 'no-border' || shape === 'circle')}
class:mr-2={!iconOnly && kind !== 'no-border' && shape !== 'circle'}
class:mr-1={!iconOnly && (kind === 'no-border' || kind === 'link-bordered' || shape === 'circle')}
class:mr-2={!iconOnly && kind !== 'no-border' && kind !== 'link-bordered' && shape !== 'circle'}
class:resetIconSize
>
<Icon bind:icon size={'small'} />
<Icon bind:icon size={size === 'inline' ? 'inline' : 'small'} />
</div>
{/if}
{#if loading}
<Spinner />
{:else}
{:else if label}
<span class="overflow-label pointer-events-none">
{#if label}
<Label {label} params={labelParams} />
{:else if $$slots.content}
<slot name="content" />
{/if}
<Label {label} params={labelParams} />
</span>
{:else if $$slots.content}
<span class="overflow-label pointer-events-none">
<slot name="content" />
</span>
{/if}
</button>
<style lang="scss">
.inline {
height: 1.375rem;
font-size: 0.75rem;
line-height: 0.75rem;
&.only-icon {
width: 1.375rem;
}
}
.small {
height: 1.5rem;
font-size: 0.75rem;
@ -160,7 +168,7 @@
flex-shrink: 0;
padding: 0 0.5rem;
font-weight: 500;
min-width: 1.5rem;
min-width: 1.375rem;
white-space: nowrap;
color: var(--accent-color);
background-color: transparent;
@ -289,6 +297,7 @@
border-color: var(--button-border-color);
&:hover {
color: var(--accent-color);
background-color: var(--button-bg-hover);
border-color: var(--button-border-hover);
.btn-icon {
color: var(--accent-color);

View File

@ -15,13 +15,13 @@
<script lang="ts">
import { onDestroy, onMount } from 'svelte'
export let padding: boolean = false
export let padding: string | undefined = undefined
export let autoscroll: boolean = false
// export let correctPadding: number = 0
export let bottomStart: boolean = false
export let tableFade: boolean = false
let mask: 'top' | 'bottom' | 'both' | 'none' = 'bottom'
let mask: 'top' | 'bottom' | 'both' | 'none' = 'none'
let divScroll: HTMLElement
let divBox: HTMLElement
@ -103,9 +103,9 @@
if (divScroll) {
beforeContent = divScroll.scrollTop
belowContent = divScroll.scrollHeight - divScroll.clientHeight - beforeContent
if (beforeContent > 1 && belowContent > 1) mask = 'both'
else if (beforeContent > 1) mask = 'bottom'
else if (belowContent > 1) mask = 'top'
if (beforeContent > 2 && belowContent > 2) mask = 'both'
else if (beforeContent > 2) mask = 'bottom'
else if (belowContent > 2) mask = 'top'
else mask = 'none'
if (autoscroll) {
@ -141,6 +141,9 @@
let divHeight: number
const _resize = (): void => checkFade()
let boxHeight: number
$: if (boxHeight) checkFade()
</script>
<svelte:window on:resize={_resize} />
@ -155,7 +158,7 @@
class:antiNav-bothFade={mask === 'both'}
class:antiNav-noneFade={mask === 'none'}
>
<div bind:this={divBox} class="box" class:p-10={padding}>
<div bind:this={divBox} class="box" style:padding bind:clientHeight={boxHeight} on:dragover on:drop>
<slot />
</div>
</div>

View File

@ -73,7 +73,7 @@ export interface TabItem {
}
export type ButtonKind = 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'link-bordered' | 'dangerous'
export type ButtonSize = 'small' | 'medium' | 'large' | 'x-large'
export type ButtonSize = 'inline' | 'small' | 'medium' | 'large' | 'x-large'
export type ButtonShape = 'rectangle' | 'rectangle-left' | 'rectangle-right' | 'circle' | 'round' | undefined
export type EditStyle = 'editbox' | 'large-style' | 'small-style' | 'search-style'
export interface PopupPositionElement {

View File

@ -65,7 +65,7 @@
: `#${getPanelURI(view.component.EditDoc, value._id, Hierarchy.mixinOrClass(value), 'content')}`}
>
{#if shouldShowAvatar}
<div class="eContentPresenterIcon">
<div class="eContentPresenterIcon" class:mr-1={shouldShowName}>
<Avatar size={avatarSize} avatar={value?.avatar} />
</div>
{/if}
@ -100,7 +100,6 @@
}
}
.eContentPresenterIcon {
margin-right: 0.25rem;
color: var(--theme-content-dark-color);
}
.eContentPresenterLabel {

View File

@ -18,7 +18,7 @@
import { Kanban, TypeState } from '@anticrm/kanban'
import { createQuery } from '@anticrm/presentation'
import { Issue, Team } from '@anticrm/tracker'
import { Button, eventToHTMLElement, Icon, IconAdd, showPopup, Tooltip } from '@anticrm/ui'
import { Button, Icon, IconAdd, showPopup, Tooltip } from '@anticrm/ui'
import { focusStore, ListSelectionProvider, SelectDirection, selectionStore } from '@anticrm/view-resources'
import ActionContext from '@anticrm/view-resources/src/components/ActionContext.svelte'
import Menu from '@anticrm/view-resources/src/components/Menu.svelte'
@ -50,7 +50,8 @@
states = issueStatuses.map((status) => ({
_id: status._id,
title: status.name,
color: status.color ?? status.$lookup?.category?.color ?? 0
color: status.color ?? status.$lookup?.category?.color ?? 0,
icon: status.$lookup?.category?.icon ?? undefined
}))
},
{
@ -128,7 +129,7 @@
>
<svelte:fragment slot="header" let:state let:count>
<div class="header flex-col">
<div class="flex-between label font-medium w-full h-full mb-4">
<div class="flex-between label font-medium w-full h-full">
<div class="flex-row-center gap-2">
<Icon icon={state.icon} size={'small'} />
<span class="lines-limit-2 ml-2">{state.title}</span>
@ -139,8 +140,8 @@
<Button
icon={IconAdd}
kind={'transparent'}
on:click={(evt) => {
showPopup(CreateIssue, { space: currentSpace, status: state._id }, eventToHTMLElement(evt))
on:click={() => {
showPopup(CreateIssue, { space: currentSpace, status: state._id }, 'top')
}}
/>
</Tooltip>
@ -150,18 +151,27 @@
</svelte:fragment>
<svelte:fragment slot="card" let:object>
{@const issue = toIssue(object)}
<div class="flex-row pt-2 pb-2 pr-4 pl-4">
<div class="flex-between mb-2">
<div class="tracker-card">
<div class="flex-col mr-6">
<IssuePresenter value={object} {currentTeam} />
{#if issue.$lookup?.assignee}
<AssigneePresenter value={issue.$lookup.assignee} issueId={issue._id} {currentSpace} isEditable={true} />
{/if}
<span class="fs-bold caption-color mt-1 lines-limit-2">
{object.title}
</span>
</div>
<span class="fs-bold title">
{object.title}
</span>
<div class="flex gap-2 mt-2 mb-2">
<PriorityEditor value={issue} isEditable={true} />
{#if issue.$lookup?.assignee}
<div class="abs-rt-content">
<AssigneePresenter value={issue.$lookup.assignee} issueId={issue._id} {currentSpace} isEditable={true} />
</div>
{/if}
<div class="buttons-group xsmall-gap mt-10px">
<PriorityEditor
value={issue}
isEditable={true}
kind={'link-bordered'}
size={'inline'}
justify={'center'}
width={''}
/>
</div>
</div>
</svelte:fragment>
@ -170,19 +180,22 @@
<style lang="scss">
.header {
height: 6rem;
min-height: 6rem;
user-select: none;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--divider-color);
.label {
color: var(--theme-caption-color);
border-bottom: 1px solid var(--divider-color);
color: var(--caption-color);
.counter {
color: rgba(var(--theme-caption-color), 0.8);
color: rgba(var(--caption-color), 0.8);
}
}
}
.title {
color: var(--theme-caption-color);
.tracker-card {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
padding: 0.5rem 1rem;
min-height: 6.5rem;
}
</style>

View File

@ -17,17 +17,7 @@
import { Class, Doc, FindOptions, getObjectValue, Ref, WithLookup } from '@anticrm/core'
import { getClient } from '@anticrm/presentation'
import { Issue, IssueStatus, Team } from '@anticrm/tracker'
import {
Button,
CheckBox,
Component,
eventToHTMLElement,
IconAdd,
IconMoreV,
showPopup,
Spinner,
Tooltip
} from '@anticrm/ui'
import { Button, CheckBox, Component, eventToHTMLElement, IconAdd, showPopup, Spinner, Tooltip } from '@anticrm/ui'
import { AttributeModel, BuildModelKey } from '@anticrm/view'
import { buildModel, getObjectPresenter, LoadingProps, Menu } from '@anticrm/view-resources'
import { createEventDispatcher } from 'svelte'
@ -233,18 +223,6 @@
value={getObjectValue(attributeModel.key, docObject) ?? ''}
{...attributeModel.props}
/>
<div
id="context-menu"
class="eIssuePresenterContextMenu"
on:click={(event) =>
handleMenuOpened(
event,
docObject,
combinedGroupedIssues.findIndex((x) => x === docObject)
)}
>
<IconMoreV size={'small'} />
</div>
</div>
{:else if attributeModelIndex === 3}
<svelte:component
@ -329,12 +307,6 @@
}
}
&.mListGridFixed {
.eIssuePresenterContextMenu {
visibility: visible;
}
}
&.mListGridSelected {
background-color: var(--menu-bg-select);
}
@ -375,23 +347,6 @@
display: flex;
align-items: center;
flex-shrink: 0;
width: 5.5rem;
margin-left: 0.5rem;
.eIssuePresenterContextMenu {
visibility: hidden;
opacity: 0.6;
cursor: pointer;
&:hover {
opacity: 1;
}
}
&:hover {
.eIssuePresenterContextMenu {
visibility: visible;
}
}
// width: 5.5rem;
}
</style>

View File

@ -101,7 +101,7 @@
{/if}
</div>
<div class="ml-8 mr-8 mt-4"><SearchEdit bind:value={search} /></div>
<Scroller padding>
<Scroller padding={'2.5rem'}>
<div class="flex-col">
{#each spaces as space (space._id)}
{@const icon = classIcon(client, space._class)}