mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-08 21:27:45 +03:00
Update Tracker board layout. Removed context menu. (#2016)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
f029789957
commit
922c06d5f1
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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)}
|
||||
|
Loading…
Reference in New Issue
Block a user