mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
UBER-795: updated layout of pop-ups. There is always a Back in the Panel. (#3644)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
e80844e18e
commit
41650925c0
@ -18,7 +18,7 @@
|
||||
import { Button, IconClose, Label, Scroller } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import presentation from '..'
|
||||
import { deviceOptionsStore as deviceInfo, resizeObserver } from '@hcengineering/ui'
|
||||
import { deviceOptionsStore as deviceInfo, resizeObserver, IconBack } from '@hcengineering/ui'
|
||||
import IconForward from './icons/Forward.svelte'
|
||||
|
||||
export let label: IntlString
|
||||
@ -27,10 +27,16 @@
|
||||
export let canSave: boolean = false
|
||||
export let okLabel: IntlString = presentation.string.Create
|
||||
export let onCancel: Function | undefined = undefined
|
||||
export let backAction: () => Promise<void> | void = () => {}
|
||||
export let isBack: boolean = false
|
||||
export let fullSize: boolean = false
|
||||
export let hideAttachments: boolean = false
|
||||
export let hideSubheader: boolean = false
|
||||
export let numberOfBlocks: number = 0
|
||||
export let thinHeader: boolean = false
|
||||
export let accentHeader: boolean = false
|
||||
export let hideSubheader: boolean = false
|
||||
export let hideContent: boolean = false
|
||||
export let hideAttachments: boolean = false
|
||||
export let hideFooter: boolean = false
|
||||
export let gap: string | undefined = undefined
|
||||
export let width: 'large' | 'medium' | 'small' | 'x-small' | 'menu' = 'large'
|
||||
export let noFade = false
|
||||
@ -38,30 +44,40 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let okProcessing = false
|
||||
$: headerDivide = hideContent && numberOfBlocks > 1
|
||||
</script>
|
||||
|
||||
<form
|
||||
id={label}
|
||||
class="antiCard {$deviceInfo.isMobile ? 'mobile' : 'dialog'} {width}"
|
||||
class:full={fullSize}
|
||||
on:keydown
|
||||
on:submit|preventDefault={() => {}}
|
||||
use:resizeObserver={() => {
|
||||
dispatch('changeContent')
|
||||
}}
|
||||
>
|
||||
<div class="antiCard-header" class:withSub={$$slots.subheader && !hideSubheader}>
|
||||
<div
|
||||
class="antiCard-header"
|
||||
class:withSub={$$slots.subheader && !hideSubheader}
|
||||
class:thinHeader
|
||||
class:border-bottom-popup-divider={headerDivide}
|
||||
>
|
||||
<div class="antiCard-header__title-wrap">
|
||||
{#if $$slots.header}
|
||||
<slot name="header" />
|
||||
<span class="antiCard-header__divider"><IconForward size={'small'} /></span>
|
||||
{/if}
|
||||
<span class="antiCard-header__title" class:accentHeader>
|
||||
{#if isBack}
|
||||
<Button icon={IconBack} kind={'ghost'} size={'small'} on:click={backAction} />
|
||||
{/if}
|
||||
<div class="antiCard-header__title" class:accentHeader>
|
||||
{#if $$slots.title}
|
||||
<slot name="title" {label} labelProps={labelProps ?? {}} />
|
||||
{:else}
|
||||
<Label {label} params={labelProps ?? {}} />
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-4 buttons-group small-gap content-dark-color">
|
||||
<Button
|
||||
@ -86,62 +102,81 @@
|
||||
<slot name="subheader" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="antiCard-content">
|
||||
<Scroller padding={$$slots.pool ? '.5rem 1.5rem' : '.5rem 1.5rem 1.5rem'} {gap} {noFade}>
|
||||
<slot />
|
||||
</Scroller>
|
||||
</div>
|
||||
{#if !hideContent}
|
||||
<div class="antiCard-content">
|
||||
<Scroller padding={$$slots.pool ? '.5rem 1.5rem' : '.5rem 1.5rem 1.5rem'} {gap} {noFade}>
|
||||
<slot />
|
||||
</Scroller>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $$slots.pool}
|
||||
<div class="antiCard-pool">
|
||||
<slot name="pool" />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $$slots.blocks && numberOfBlocks}
|
||||
{#if numberOfBlocks === 1}
|
||||
<div class="antiCard-block">
|
||||
<slot name="blocks" block={0} />
|
||||
</div>
|
||||
{:else}
|
||||
<Scroller noFade={false}>
|
||||
{#each [...Array(numberOfBlocks).keys()] as block}
|
||||
<div class="antiCard-blocks" class:border-top-none={headerDivide && block === 0}>
|
||||
<slot name="blocks" {block} />
|
||||
</div>
|
||||
{/each}
|
||||
</Scroller>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if $$slots.attachments && !hideAttachments}
|
||||
<div class="antiCard-attachments">
|
||||
<Scroller horizontal contentDirection={'horizontal'} {gap}>
|
||||
<Scroller horizontal contentDirection={'horizontal'} {gap} noFade={false}>
|
||||
<div class="antiCard-attachments__container">
|
||||
<slot name="attachments" />
|
||||
</div>
|
||||
</Scroller>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="antiCard-footer divide reverse">
|
||||
<div class="buttons-group text-sm flex-no-shrink">
|
||||
{#if $$slots.buttons}
|
||||
<slot name="buttons" />
|
||||
{/if}
|
||||
<Button
|
||||
loading={okProcessing}
|
||||
focusIndex={10001}
|
||||
disabled={!canSave}
|
||||
label={okLabel}
|
||||
kind={'accented'}
|
||||
size={'large'}
|
||||
on:click={() => {
|
||||
if (okProcessing) {
|
||||
return
|
||||
}
|
||||
okProcessing = true
|
||||
const r = okAction()
|
||||
if (r instanceof Promise) {
|
||||
r.then(() => {
|
||||
{#if !hideFooter}
|
||||
<div class="antiCard-footer divide reverse">
|
||||
<div class="buttons-group text-sm flex-no-shrink">
|
||||
{#if $$slots.buttons}
|
||||
<slot name="buttons" />
|
||||
{/if}
|
||||
<Button
|
||||
loading={okProcessing}
|
||||
focusIndex={10001}
|
||||
disabled={!canSave}
|
||||
label={okLabel}
|
||||
kind={'accented'}
|
||||
size={'large'}
|
||||
on:click={() => {
|
||||
if (okProcessing) {
|
||||
return
|
||||
}
|
||||
okProcessing = true
|
||||
const r = okAction()
|
||||
if (r instanceof Promise) {
|
||||
r.then(() => {
|
||||
okProcessing = false
|
||||
dispatch('close')
|
||||
})
|
||||
} else {
|
||||
okProcessing = false
|
||||
dispatch('close')
|
||||
})
|
||||
} else {
|
||||
okProcessing = false
|
||||
dispatch('close')
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="buttons-group small-gap text-sm">
|
||||
<slot name="footer" />
|
||||
{#if $$slots.error}
|
||||
<div class="antiCard-footer__error">
|
||||
<slot name="error" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons-group small-gap text-sm">
|
||||
<slot name="footer" />
|
||||
{#if $$slots.error}
|
||||
<div class="antiCard-footer__error">
|
||||
<slot name="error" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</form>
|
||||
|
@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { DocIndexState } from '@hcengineering/core'
|
||||
|
||||
import { EditBox, Panel } from '@hcengineering/ui'
|
||||
import { EditBox, Dialog } from '@hcengineering/ui'
|
||||
import IndexedDocumentContent from './IndexedDocumentContent.svelte'
|
||||
|
||||
export let left: DocIndexState
|
||||
@ -10,46 +9,25 @@
|
||||
let search: string = ''
|
||||
</script>
|
||||
|
||||
<Panel on:changeContent on:close>
|
||||
<EditBox autoFocus bind:value={search} kind="search-style" />
|
||||
<div class="indexed-background">
|
||||
<div class="indexed-doc text-base max-h-125">
|
||||
<div class="flex">
|
||||
<div class="indexed-doc-part">
|
||||
<IndexedDocumentContent indexDoc={left} {search} />
|
||||
</div>
|
||||
{#if right !== undefined}
|
||||
<div class="indexed-doc-part">
|
||||
<IndexedDocumentContent indexDoc={right} {search} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<Dialog isFullSize on:changeContent on:close on:fullsize>
|
||||
<EditBox autoFocus bind:value={search} kind={'default-large'} fullSize />
|
||||
<div class="flex-row-top px-3 mt-4 text-base">
|
||||
<div class="indexed-doc-part flex-col">
|
||||
<IndexedDocumentContent indexDoc={left} {search} />
|
||||
</div>
|
||||
{#if right !== undefined}
|
||||
<div class="indexed-doc-part flex-col ml-4">
|
||||
<IndexedDocumentContent indexDoc={right} {search} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Panel>
|
||||
</Dialog>
|
||||
|
||||
<style lang="scss">
|
||||
.indexed-doc {
|
||||
padding: 2.5rem;
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
min-width: 50rem;
|
||||
max-width: 100rem;
|
||||
}
|
||||
.indexed-doc-part {
|
||||
padding: 0.5rem;
|
||||
display: grid;
|
||||
overflow: auto;
|
||||
min-width: 25rem;
|
||||
max-width: 50rem;
|
||||
}
|
||||
.indexed-background {
|
||||
background-color: white;
|
||||
color: black;
|
||||
user-select: text;
|
||||
// width: 200rem;
|
||||
.highlight {
|
||||
color: blue;
|
||||
}
|
||||
flex-basis: 100%;
|
||||
height: fit-content;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
{#if summary}
|
||||
{#if search.length > 0}
|
||||
Result:
|
||||
<span class="font-medium">Result:</span>
|
||||
{#each summary.split('\n').filter((line, idx, arr) => {
|
||||
return line.toLowerCase().includes(search.toLowerCase()) || arr[idx - 1]
|
||||
?.toLowerCase()
|
||||
@ -55,7 +55,7 @@
|
||||
{/each}
|
||||
<br />
|
||||
{/if}
|
||||
Summary:
|
||||
<span class="font-medium">Summary:</span>
|
||||
{#each summary.split('\n') as line}
|
||||
{@const hl = search.length > 0 && line.toLowerCase().includes(search.toLowerCase())}
|
||||
<span class:text-md={!hl} class:highlight={hl}>{line}</span>
|
||||
@ -75,7 +75,7 @@
|
||||
{#each attr[1] as doc}
|
||||
<div class="p-1" class:flex-col={doc.length > 1}>
|
||||
{#if search.length > 0}
|
||||
Result:
|
||||
<span class="font-medium">Result:</span>
|
||||
{#each doc.filter((line) => line.toLowerCase().includes(search.toLowerCase())) as line}
|
||||
<span class:highlight={true}>{line}</span>
|
||||
{/each}
|
||||
@ -93,6 +93,6 @@
|
||||
|
||||
<style lang="scss">
|
||||
.highlight {
|
||||
color: blue;
|
||||
color: var(--theme-link-color);
|
||||
}
|
||||
</style>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import core, { Doc, DocIndexState, Ref } from '@hcengineering/core'
|
||||
|
||||
import { EditBox, Label, Panel } from '@hcengineering/ui'
|
||||
import { EditBox, Label, Dialog } from '@hcengineering/ui'
|
||||
import { createQuery } from '../utils'
|
||||
import IndexedDocumentContent from './IndexedDocumentContent.svelte'
|
||||
import presentation from '../plugin'
|
||||
@ -25,39 +25,29 @@
|
||||
{#if noPanel}
|
||||
<div class="p-1 flex-col">
|
||||
<Label label={presentation.string.DocumentPreview} />
|
||||
<EditBox autoFocus bind:value={search} kind="search-style" />
|
||||
<EditBox autoFocus bind:value={search} kind={'default-large'} fullSize />
|
||||
</div>
|
||||
<div class="indexed-background">
|
||||
<div class="indexed-doc text-base max-h-125">
|
||||
<div class="indexed-doc flex-col px-3 mt-4 text-base">
|
||||
{#if indexDoc}
|
||||
<IndexedDocumentContent {indexDoc} {search} />
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<Dialog isFullSize on:changeContent on:close on:fullsize>
|
||||
<EditBox autoFocus bind:value={search} kind={'default-large'} fullSize />
|
||||
<div class="indexed-doc flex-col px-3 mt-4 text-base">
|
||||
{#if indexDoc}
|
||||
<IndexedDocumentContent {indexDoc} {search} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<Panel on:changeContent on:close>
|
||||
<EditBox autoFocus bind:value={search} kind="search-style" />
|
||||
<div class="indexed-background">
|
||||
<div class="indexed-doc text-base max-h-125">
|
||||
{#if indexDoc}
|
||||
<IndexedDocumentContent {indexDoc} {search} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
</Dialog>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.indexed-doc {
|
||||
padding: 2.5rem;
|
||||
display: grid;
|
||||
overflow: auto;
|
||||
min-width: 50rem;
|
||||
max-width: 80rem;
|
||||
}
|
||||
.indexed-background {
|
||||
background-color: white;
|
||||
color: black;
|
||||
user-select: text;
|
||||
flex-basis: 100%;
|
||||
height: fit-content;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import { Button, deviceOptionsStore as deviceInfo, Panel, PopupOptions } from '@hcengineering/ui'
|
||||
import { Button, Dialog, PopupOptions } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import presentation from '..'
|
||||
import { getFileUrl } from '../utils'
|
||||
@ -42,9 +42,7 @@
|
||||
</script>
|
||||
|
||||
<ActionContext context={{ mode: 'browser' }} />
|
||||
<Panel
|
||||
isHeader={false}
|
||||
isAside={popupOptions && popupOptions.fullSize}
|
||||
<Dialog
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
@ -76,19 +74,13 @@
|
||||
</svelte:fragment>
|
||||
|
||||
{#if contentType && contentType.startsWith('image/')}
|
||||
<div class="pdfviewer-content img" style:margin={$deviceInfo.minWidth ? '.5rem' : '1.5rem'}>
|
||||
<div class="pdfviewer-content img">
|
||||
<img class="img-fit" src={getFileUrl(file, 'full', name)} alt="" />
|
||||
</div>
|
||||
<div class="space" />
|
||||
{:else}
|
||||
<iframe
|
||||
class="pdfviewer-content"
|
||||
style:margin={$deviceInfo.minWidth ? '.5rem' : '1.5rem'}
|
||||
src={getFileUrl(file, 'full', name) + '#view=FitH&navpanes=0'}
|
||||
title=""
|
||||
/>
|
||||
<iframe class="pdfviewer-content" src={getFileUrl(file, 'full', name) + '#view=FitH&navpanes=0'} title="" />
|
||||
{/if}
|
||||
</Panel>
|
||||
</Dialog>
|
||||
|
||||
<style lang="scss">
|
||||
.icon {
|
||||
|
@ -22,6 +22,7 @@
|
||||
Button,
|
||||
ButtonKind,
|
||||
ButtonSize,
|
||||
ButtonShape,
|
||||
eventToHTMLElement,
|
||||
getEventPositionElement,
|
||||
getFocusManager,
|
||||
@ -50,6 +51,7 @@
|
||||
export let size: ButtonSize = 'large'
|
||||
export let itemSize: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let shape: ButtonShape = undefined
|
||||
export let width: string | undefined = undefined
|
||||
export let allowDeselect = false
|
||||
export let component: AnySvelteComponent | undefined = undefined
|
||||
@ -119,6 +121,7 @@
|
||||
<Button
|
||||
id="space.selector"
|
||||
{focus}
|
||||
{shape}
|
||||
disabled={readonly}
|
||||
{focusIndex}
|
||||
icon={selected?.icon === iconWithEmoji && iconWithEmoji ? IconWithEmoji : selected?.icon ?? defaultIcon}
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Class, DocumentQuery, FindOptions, Ref, Space } from '@hcengineering/core'
|
||||
import { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { AnySvelteComponent, ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import { AnySvelteComponent, ButtonKind, ButtonSize, ButtonShape } from '@hcengineering/ui'
|
||||
import { ObjectCreate } from '../types'
|
||||
import SpaceSelect from './SpaceSelect.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@ -28,6 +28,7 @@
|
||||
export let label: IntlString
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let shape: ButtonShape = undefined
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
export let allowDeselect = false
|
||||
@ -54,6 +55,7 @@
|
||||
{label}
|
||||
{size}
|
||||
{kind}
|
||||
{shape}
|
||||
{justify}
|
||||
{width}
|
||||
{component}
|
||||
|
@ -108,6 +108,7 @@
|
||||
// Be aware to update defineAlpha() function in colors.ts
|
||||
--theme-bg-color: #1A1A28;
|
||||
--theme-bg-accent-color: rgba(0, 0, 0, .08);
|
||||
--theme-bg-dark-color: rgba(0, 0, 0, .2);
|
||||
--theme-back-color: #0f0f18;
|
||||
--theme-overlay-color: rgba(0, 0, 0, .3);
|
||||
--theme-statusbar-color: #2C2C35;
|
||||
@ -303,6 +304,7 @@
|
||||
// Be aware to update defineAlpha() function in colors.ts
|
||||
--theme-bg-color: #F1F1F4;
|
||||
--theme-bg-accent-color: rgba(255, 255, 255, .08);
|
||||
--theme-bg-dark-color: rgba(255, 255, 255, .8);
|
||||
--theme-back-color: #D9D9DD;
|
||||
--theme-overlay-color: rgba(0, 0, 0, .2);
|
||||
--theme-statusbar-color: #bfbfc6;
|
||||
|
@ -225,6 +225,7 @@ input.search {
|
||||
align-items: flex-start;
|
||||
flex-wrap: nowrap;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
.flex-row-reverse {
|
||||
display: flex;
|
||||
@ -683,6 +684,8 @@ input.search {
|
||||
.min-h-5 { min-height: 1.25rem; }
|
||||
.min-h-7 { min-height: 1.75rem; }
|
||||
.min-h-8 { min-height: 2rem; }
|
||||
.min-h-9 { min-height: 2.25rem; }
|
||||
.min-h-11 { min-height: 2.75rem; }
|
||||
.min-h-30 { min-height: 7.5rem; }
|
||||
.min-h-60 { min-height: 15rem; }
|
||||
.max-w-2 { max-width: .5rem; }
|
||||
@ -819,6 +822,7 @@ a.no-line {
|
||||
.text-left { text-align: left; }
|
||||
.text-center { text-align: center; }
|
||||
.leading-3 { line-height: .75rem; }
|
||||
.tracking-1px { letter-spacing: 1px; }
|
||||
|
||||
.over-underline {
|
||||
cursor: pointer;
|
||||
@ -944,6 +948,9 @@ a.no-line {
|
||||
.border-divider-color {border: 1px solid var(--theme-divider-color);}
|
||||
.border-primary-button { border-color: var(--accented-button-border); }
|
||||
|
||||
.border-top-none { border-top: none !important; }
|
||||
.border-bottom-popup-divider { border-bottom: 1px solid var(--theme-popup-divider); }
|
||||
|
||||
.top-divider { border-top: 1px solid var(--theme-divider-color); }
|
||||
.bottom-divider { border-bottom: 1px solid var(--theme-divider-color); }
|
||||
.left-divider { border-left: 1px solid var(--theme-divider-color); }
|
||||
|
@ -40,8 +40,10 @@
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.withSub { padding: 1.5rem 1.5rem 0; }
|
||||
&:not(.withSub) { padding: 1.5rem 1.5rem 1rem; }
|
||||
&.withSub:not(.thinHeader) { padding: 1.5rem 1.5rem 0; }
|
||||
&.withSub.thinHeader { padding: 1rem 1.5rem 0; }
|
||||
&.thinHeader:not(.withSub) { padding: 1rem 1.5rem; }
|
||||
&:not(.withSub, .thinHeader) { padding: 1.5rem; }
|
||||
|
||||
&__title-wrap {
|
||||
display: flex;
|
||||
@ -81,7 +83,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: .5rem 1.5rem 1rem;
|
||||
padding: .5rem 1.5rem 1.5rem;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
@ -125,10 +127,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
.antiCard-attachments {
|
||||
background-color: var(--theme-bg-accent-color);
|
||||
border-top: 1px solid var(--theme-popup-divider);
|
||||
.antiCard-attachments,
|
||||
.antiCard-block,
|
||||
.antiCard-blocks { border-top: 1px solid var(--theme-popup-divider); }
|
||||
|
||||
.antiCard-block,
|
||||
.antiCard-blocks {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: fit-content;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.antiCard-attachments,
|
||||
.antiCard-blocks { background-color: var(--theme-bg-accent-color); }
|
||||
|
||||
.antiCard-attachments {
|
||||
&__container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -138,6 +153,8 @@
|
||||
& > *:last-child { margin-right: 1.5rem; }
|
||||
}
|
||||
}
|
||||
.antiCard-block { padding: 1.5rem; }
|
||||
.antiCard-blocks { padding: .75rem 1.5rem; }
|
||||
|
||||
.antiCard-footer {
|
||||
overflow: hidden;
|
||||
|
@ -178,7 +178,7 @@
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: calc(100% - 7.5rem);
|
||||
max-width: 860px;
|
||||
max-width: 54rem;
|
||||
|
||||
&.max {
|
||||
max-width: 100%;
|
||||
@ -337,10 +337,9 @@
|
||||
|
||||
.popupPanel-title,
|
||||
.popupPanel-body {
|
||||
border-radius: 0;
|
||||
// border-radius: 0;
|
||||
border: none;
|
||||
}
|
||||
// .popupPanel-title { border-bottom: 1px solid var(--divider-color); }
|
||||
}
|
||||
.popup.fullsize {
|
||||
transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19) !important;
|
||||
|
@ -15,111 +15,128 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { resizeObserver, Button, Label, IconClose, IconScale, IconScaleFull } from '..'
|
||||
|
||||
import Close from './icons/Close.svelte'
|
||||
import ScrollBox from './ScrollBox.svelte'
|
||||
import Button from './Button.svelte'
|
||||
import Label from './Label.svelte'
|
||||
|
||||
import ui from '../plugin'
|
||||
|
||||
export let label: IntlString
|
||||
export let okLabel: IntlString
|
||||
export let okAction: () => void
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let isFullSize: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let fullSize: boolean = false
|
||||
</script>
|
||||
|
||||
<div class="dialog-container">
|
||||
<form
|
||||
class="dialog"
|
||||
on:submit|preventDefault={() => {
|
||||
okAction()
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="flex-between header">
|
||||
<div class="title"><Label {label} /></div>
|
||||
<div
|
||||
class="tool"
|
||||
on:click={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<Close size={'small'} />
|
||||
<form
|
||||
class="dialog-container"
|
||||
class:fullsize={fullSize}
|
||||
on:submit|preventDefault={() => {}}
|
||||
use:resizeObserver={() => {
|
||||
dispatch('changeContent')
|
||||
}}
|
||||
>
|
||||
<div class="flex-between header">
|
||||
<div class="flex-row-center gap-1-5">
|
||||
<Button icon={IconClose} kind={'ghost'} size={'medium'} on:click={() => dispatch('close')} />
|
||||
<div class="title">
|
||||
{#if label}<Label {label} />{/if}
|
||||
{#if $$slots.title}<slot name="title" />{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<ScrollBox vertical stretch><slot /></ScrollBox>
|
||||
<div class="flex-row-center gap-1-5">
|
||||
{#if $$slots.utils}
|
||||
<slot name="utils" />
|
||||
{/if}
|
||||
{#if $$slots.utils && isFullSize}
|
||||
<div class="buttons-divider" />
|
||||
{/if}
|
||||
{#if isFullSize}
|
||||
<Button
|
||||
focusIndex={100010}
|
||||
icon={fullSize ? IconScale : IconScaleFull}
|
||||
kind={'ghost'}
|
||||
size={'medium'}
|
||||
selected={fullSize}
|
||||
on:click={() => {
|
||||
fullSize = !fullSize
|
||||
dispatch('fullsize')
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content" class:rounded={!($$slots.footerLeft || $$slots.footerRight)}>
|
||||
<slot />
|
||||
</div>
|
||||
{#if $$slots.footerLeft || $$slots.footerRight}
|
||||
<div class="footer">
|
||||
<Button label={okLabel} kind={'accented'} />
|
||||
<Button
|
||||
label={ui.string.Cancel}
|
||||
on:click={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
{#if $$slots.footerLeft}
|
||||
<div class="flex-row-center gap-2">
|
||||
<slot name="footerLeft" />
|
||||
</div>
|
||||
{:else}<div />{/if}
|
||||
{#if $$slots.footerRight}
|
||||
<div class="flex-row-center gap-2">
|
||||
<slot name="footerRight" />
|
||||
</div>
|
||||
{:else}<div />{/if}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{/if}
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
.dialog {
|
||||
.dialog-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 45rem;
|
||||
height: 100vh;
|
||||
min-height: 100vh;
|
||||
max-height: 100vh;
|
||||
background-color: var(--theme-bg-color);
|
||||
border-radius: 1.875rem 0 0 1.875rem;
|
||||
box-shadow: 0px 3.125rem 7.5rem rgba(0, 0, 0, 0.4);
|
||||
min-width: 25rem;
|
||||
max-width: calc(100vw - 2rem);
|
||||
min-height: 0;
|
||||
max-height: 80vh;
|
||||
background-color: var(--theme-popup-color);
|
||||
border-radius: 0.5rem;
|
||||
|
||||
&:not(.fullsize) {
|
||||
border: 1px solid var(--theme-popup-divider);
|
||||
box-shadow: var(--theme-popup-shadow);
|
||||
}
|
||||
&.fullsize {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-shrink: 0;
|
||||
padding: 0 2rem 0 2.5rem;
|
||||
height: 4.5rem;
|
||||
padding: 0.5rem;
|
||||
background-color: var(--theme-popup-header);
|
||||
border-bottom: 1px solid var(--theme-popup-divider);
|
||||
border-radius: 0.5rem 0.5rem 0 0;
|
||||
|
||||
.title {
|
||||
flex-grow: 1;
|
||||
font-weight: 500;
|
||||
font-size: 1.125rem;
|
||||
color: var(--caption-color);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tool {
|
||||
margin-left: 0.75rem;
|
||||
opacity: 0.4;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
font-size: 1rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
margin: 0 2.5rem;
|
||||
height: max-content;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
|
||||
&.rounded {
|
||||
border-radius: 0 0 0.5rem 0.5rem;
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
flex-shrink: 0;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
direction: rtl;
|
||||
justify-content: start;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
column-gap: 0.75rem;
|
||||
padding: 0 2.5rem;
|
||||
height: 6rem;
|
||||
mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 1.25rem, rgba(0, 0, 0, 1) 2.5rem);
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-top: 1px solid var(--theme-popup-divider);
|
||||
border-radius: 0 0 0.5rem 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { afterUpdate, createEventDispatcher, onMount } from 'svelte'
|
||||
import { deviceOptionsStore as deviceInfo, checkAdaptiveMatching, embeddedPlatform, IconBack } from '../../'
|
||||
import { deviceOptionsStore as deviceInfo, checkAdaptiveMatching, IconBack } from '../../'
|
||||
import { resizeObserver } from '../resize'
|
||||
import Button from './Button.svelte'
|
||||
import Scroller from './Scroller.svelte'
|
||||
@ -96,20 +96,19 @@
|
||||
class:embedded
|
||||
>
|
||||
<div class="popupPanel-title {twoRows && !withoutTitle ? 'row-top' : 'row'}">
|
||||
{#if allowClose && !embedded}
|
||||
{#if embeddedPlatform}
|
||||
<Button
|
||||
focusIndex={10000}
|
||||
icon={IconBack}
|
||||
kind={'ghost'}
|
||||
size={'medium'}
|
||||
on:click={() => {
|
||||
history.back()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<Button
|
||||
focusIndex={10000}
|
||||
icon={IconBack}
|
||||
kind={'ghost'}
|
||||
size={'medium'}
|
||||
on:click={() => {
|
||||
history.back()
|
||||
}}
|
||||
/>
|
||||
{#if allowClose}
|
||||
<div class="antiHSpacer" />
|
||||
<Button
|
||||
focusIndex={10000}
|
||||
focusIndex={10001}
|
||||
icon={IconClose}
|
||||
kind={'ghost'}
|
||||
size={'medium'}
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
export let fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
|
@ -1,16 +1,43 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
const fill: string = 'var(--caption-color)'
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'var(--theme-caption-color)'
|
||||
</script>
|
||||
|
||||
<svg {fill} viewBox="0 0 6 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="1" cy="1" r="1" />
|
||||
<circle cx="4.5" cy="1" r="1" />
|
||||
<circle cx="1" cy="4.5" r="1" />
|
||||
<circle cx="4.5" cy="4.5" r="1" />
|
||||
<circle cx="1" cy="8" r="1" />
|
||||
<circle cx="4.5" cy="8" r="1" />
|
||||
<circle cx="1" cy="11.5" r="1" />
|
||||
<circle cx="4.5" cy="11.5" r="1" />
|
||||
<circle cx="1" cy="15" r="1" />
|
||||
<circle cx="4.5" cy="15" r="1" />
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M9 5.99988C9 6.55216 9.44772 6.99988 10 6.99988C10.5523 6.99988 11 6.55216 11 5.99988C11 5.44759 10.5523 4.99988 10 4.99988C9.44772 4.99988 9 5.44759 9 5.99988Z"
|
||||
/>
|
||||
<path
|
||||
d="M9 9.99988C9 10.5522 9.44772 10.9999 10 10.9999C10.5523 10.9999 11 10.5522 11 9.99988C11 9.44759 10.5523 8.99988 10 8.99988C9.44772 8.99988 9 9.44759 9 9.99988Z"
|
||||
/>
|
||||
<path
|
||||
d="M10 14.9999C9.44772 14.9999 9 14.5522 9 13.9999C9 13.4476 9.44772 12.9999 10 12.9999C10.5523 12.9999 11 13.4476 11 13.9999C11 14.5522 10.5523 14.9999 10 14.9999Z"
|
||||
/>
|
||||
<path
|
||||
d="M9 2C9 2.55228 9.44772 3 10 3C10.5523 3 11 2.55228 11 2C11 1.44772 10.5523 1 10 1C9.44772 1 9 1.44772 9 2Z"
|
||||
/>
|
||||
<path
|
||||
d="M5 5.99988C5 6.55216 5.44772 6.99988 6 6.99988C6.55229 6.99988 7 6.55216 7 5.99988C7 5.44759 6.55229 4.99988 6 4.99988C5.44772 4.99988 5 5.44759 5 5.99988Z"
|
||||
/>
|
||||
<path
|
||||
d="M5 9.99988C5 10.5522 5.44772 10.9999 6 10.9999C6.55228 10.9999 7 10.5522 7 9.99988C7 9.44759 6.55229 8.99988 6 8.99988C5.44772 8.99988 5 9.44759 5 9.99988Z"
|
||||
/>
|
||||
<path
|
||||
d="M6 14.9999C5.44772 14.9999 5 14.5522 5 13.9999C5 13.4476 5.44772 12.9999 6 12.9999C6.55228 12.9999 7 13.4476 7 13.9999C7 14.5522 6.55228 14.9999 6 14.9999Z"
|
||||
/>
|
||||
<path d="M5 2C5 2.55228 5.44772 3 6 3C6.55229 3 7 2.55228 7 2C7 1.44772 6.55229 1 6 1C5.44772 1 5 1.44772 5 2Z" />
|
||||
</svg>
|
||||
|
@ -296,7 +296,7 @@ export function fitPopupElement (
|
||||
show = false
|
||||
} else if (element === 'full' && contentPanel !== undefined) {
|
||||
const rect = contentPanel.getBoundingClientRect()
|
||||
newProps.top = `${rect.top + 1}px`
|
||||
newProps.top = `${rect.top + 4}px`
|
||||
newProps.bottom = '4px'
|
||||
newProps.left = '4px'
|
||||
newProps.right = '4px'
|
||||
|
@ -140,7 +140,7 @@
|
||||
)
|
||||
}
|
||||
async function showSummary (left: DocIndexState, right?: DocIndexState): Promise<void> {
|
||||
showPopup(IndexedDocumentCompare, { left, right }, 'top')
|
||||
showPopup(IndexedDocumentCompare, { left, right }, 'centered')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -173,13 +173,11 @@
|
||||
}}
|
||||
on:changeContent
|
||||
>
|
||||
<div on:keydown={onKeydown}>
|
||||
<div class="mb-2">
|
||||
<EditBox bind:value={name} placeholder={core.string.Name} />
|
||||
</div>
|
||||
<div class="flex-between mb-2">
|
||||
<EditBox placeholder={presentation.string.Search} kind="large-style" bind:value={newValue} />
|
||||
<div class="flex gap-2">
|
||||
<div class="flex-col" on:keydown={onKeydown}>
|
||||
<EditBox bind:value={name} placeholder={core.string.Name} kind={'large-style'} fullSize />
|
||||
<div class="flex-between my-4">
|
||||
<EditBox placeholder={presentation.string.Search} kind={'large-style'} bind:value={newValue} fullSize />
|
||||
<div class="flex-row-center flex-no-shrink gap-2 ml-4">
|
||||
<ActionIcon icon={IconAdd} label={presentation.string.Add} action={add} size={'small'} />
|
||||
<ActionIcon
|
||||
icon={Copy}
|
||||
@ -207,7 +205,7 @@
|
||||
</div>
|
||||
<svelte:fragment slot="footer">
|
||||
<div
|
||||
class="resume flex gap-2"
|
||||
class="resume flex-center"
|
||||
class:solid={dragover}
|
||||
on:dragover|preventDefault={() => {
|
||||
dragover = true
|
||||
|
@ -123,7 +123,7 @@
|
||||
kind="large-style"
|
||||
bind:value={newValue}
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<div class="flex-row-center gap-2">
|
||||
<ActionIcon
|
||||
icon={IconAdd}
|
||||
label={setting.string.Add}
|
||||
@ -131,7 +131,6 @@
|
||||
size={'small'}
|
||||
disabled={value.enumValues.includes(newValue.trim())}
|
||||
/>
|
||||
|
||||
<ActionIcon
|
||||
icon={IconAttachment}
|
||||
label={setting.string.ImportEnum}
|
||||
|
@ -57,7 +57,7 @@
|
||||
<Scroller>
|
||||
{#each filtered as item, i}
|
||||
<div
|
||||
class="flex-between flex-nowrap item mb-2"
|
||||
class="flex-between flex-nowrap item step-tb25"
|
||||
draggable={true}
|
||||
bind:this={elements[i]}
|
||||
on:dragover|preventDefault={(ev) => {
|
||||
@ -71,9 +71,9 @@
|
||||
selected = undefined
|
||||
}}
|
||||
>
|
||||
<div class="flex">
|
||||
<div class="circles-mark"><IconCircles /></div>
|
||||
<span class="overflow-label">{item}</span>
|
||||
<div class="flex-row-center">
|
||||
<div class="circles-mark"><IconCircles size={'small'} /></div>
|
||||
<span class="overflow-label mx-2">{item}</span>
|
||||
</div>
|
||||
<ActionIcon
|
||||
icon={IconDelete}
|
||||
@ -85,6 +85,7 @@
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{#if filtered.length}<div class="antiVSpacer x4" />{/if}
|
||||
</Scroller>
|
||||
{#if filtered.length === 0}
|
||||
<Label label={presentation.string.NoMatchesFound} />
|
||||
@ -92,26 +93,35 @@
|
||||
|
||||
<style lang="scss">
|
||||
.item {
|
||||
padding: 0.5rem 0.5rem 0.5rem 0.125rem;
|
||||
height: 2.25rem;
|
||||
background-color: var(--theme-button-default);
|
||||
border-radius: 0.25rem;
|
||||
|
||||
.circles-mark {
|
||||
position: relative;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
opacity: 0.4;
|
||||
transition: opacity 0.1s;
|
||||
cursor: grab;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: -0.5rem;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--theme-button-hovered);
|
||||
|
||||
.circles-mark {
|
||||
cursor: grab;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.circles-mark {
|
||||
position: relative;
|
||||
opacity: 0.4;
|
||||
width: 0.375rem;
|
||||
height: 1rem;
|
||||
transition: opacity 0.1s;
|
||||
margin-right: 0.5rem;
|
||||
cursor: grab;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: -0.5rem;
|
||||
&:active {
|
||||
background-color: var(--theme-button-pressed);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -110,7 +110,7 @@
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class="flex-col mt-3">
|
||||
{#each states as state, i}
|
||||
{@const color = getPlatformColorDef(state.color ?? getColorNumberByText(state.name), $themeStore.dark)}
|
||||
{#if state}
|
||||
@ -133,7 +133,7 @@
|
||||
selected = undefined
|
||||
}}
|
||||
>
|
||||
<div class="bar"><IconCircles /></div>
|
||||
<div class="bar"><IconCircles size={'small'} /></div>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="color"
|
||||
@ -169,7 +169,7 @@
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="mt-9">
|
||||
<div class="flex-col mt-9">
|
||||
<div class="flex-no-shrink flex-between trans-title uppercase">
|
||||
<Label label={task.string.DoneStatesWon} />
|
||||
<CircleButton
|
||||
@ -188,7 +188,7 @@
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="flex-col mt-4">
|
||||
{#each wonStates as state}
|
||||
{@const color = getPlatformColorDef(PaletteColorIndexes.Crocodile, $themeStore.dark)}
|
||||
{#if state}
|
||||
@ -294,7 +294,7 @@
|
||||
|
||||
<style lang="scss">
|
||||
.states {
|
||||
padding: 0.5rem 1rem;
|
||||
padding: 0.5rem 1rem 0.5rem 0.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
background-color: var(--theme-button-default);
|
||||
border: 1px solid var(--theme-button-border);
|
||||
@ -302,8 +302,8 @@
|
||||
user-select: none;
|
||||
|
||||
.bar {
|
||||
margin-right: 0.375rem;
|
||||
width: 0.375rem;
|
||||
margin-right: 0.25rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
opacity: 0.4;
|
||||
cursor: grabbing;
|
||||
|
@ -151,6 +151,7 @@
|
||||
"CreatedOne": "Created",
|
||||
"MoveIssues": "Move issues",
|
||||
"MoveIssuesDescription": "Select the project you want to move issues to",
|
||||
"ManageAttributes": "Manage attributes",
|
||||
"KeepOriginalAttributes": "Keep original attributes",
|
||||
"KeepOriginalAttributesTooltip": "Original issue statuses and components will be kept in the new project",
|
||||
"SelectReplacement": "The following items are not available in the new project. Select a replacement.",
|
||||
|
@ -151,6 +151,7 @@
|
||||
"CreatedOne": "Создана",
|
||||
"MoveIssues": "Переместить задачи",
|
||||
"MoveIssuesDescription": "Выберите проект, в который вы хотите переместить задачи",
|
||||
"ManageAttributes": "Управление атрибутами",
|
||||
"KeepOriginalAttributes": "Оставить оригинальные аттрибуты",
|
||||
"KeepOriginalAttributesTooltip": "Оригинальные статусы и компоненты будут сохранены в новом проекте",
|
||||
"SelectReplacement": "Следующие элементы не доступны в новом проекте. Выберите замену.",
|
||||
|
@ -25,7 +25,7 @@
|
||||
export let disabled = false
|
||||
export let onClick: (() => void) | undefined = undefined
|
||||
export let shouldShowAvatar: boolean = false
|
||||
export let noUnderline = false
|
||||
export let noUnderline = disabled
|
||||
export let inline = false
|
||||
export let kind: 'list' | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
@ -58,7 +58,7 @@
|
||||
component={tracker.component.EditIssue}
|
||||
shrink={0}
|
||||
>
|
||||
<span class="issuePresenterRoot" class:inline class:list={kind === 'list'}>
|
||||
<span class="issuePresenterRoot" class:inline class:list={kind === 'list'} class:cursor-pointer={!disabled}>
|
||||
{#if !inline && shouldShowAvatar}
|
||||
<div class="icon" use:tooltip={{ label: tracker.string.Issue }}>
|
||||
<Icon icon={icon ?? tracker.icon.Issues} size={'small'} />
|
||||
@ -81,7 +81,6 @@
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
|
||||
&:not(.list) {
|
||||
color: var(--theme-content-color);
|
||||
|
@ -17,12 +17,11 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import { Ref, Status } from '@hcengineering/core'
|
||||
import { SpaceSelector, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { SpaceSelector, createQuery, getClient, Card } from '@hcengineering/presentation'
|
||||
import { Component, Issue, IssueStatus, Milestone, Project } from '@hcengineering/tracker'
|
||||
import ui, { Button, IconClose, Label, Spinner, Toggle, tooltip } from '@hcengineering/ui'
|
||||
import ui, { Button, IconForward, Label, Spinner, Toggle, tooltip } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
|
||||
import tracker from '../../plugin'
|
||||
import {
|
||||
@ -265,198 +264,140 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
{#if !showManageAttributes}
|
||||
<div class="space-between">
|
||||
<span class="fs-title aligned-text">
|
||||
<Label label={tracker.string.MoveIssues} />
|
||||
<Card
|
||||
label={showManageAttributes ? tracker.string.ManageAttributes : tracker.string.MoveIssues}
|
||||
okLabel={view.string.Move}
|
||||
okAction={moveAll}
|
||||
canSave={docs[0]?.space !== currentSpace?._id}
|
||||
onCancel={() => dispatch('close')}
|
||||
backAction={() => {
|
||||
showManageAttributes = !showManageAttributes
|
||||
}}
|
||||
isBack={showManageAttributes}
|
||||
thinHeader
|
||||
accentHeader
|
||||
hideSubheader={showManageAttributes}
|
||||
hideContent={showManageAttributes}
|
||||
hideAttachments
|
||||
numberOfBlocks={showManageAttributes
|
||||
? toMove.length
|
||||
: currentSpace !== undefined && !keepOriginalAttribytes && docs[0]?.space !== currentSpace?._id
|
||||
? 1
|
||||
: 0}
|
||||
on:changeContent
|
||||
>
|
||||
<svelte:fragment slot="title" let:label>
|
||||
{#if !showManageAttributes}
|
||||
<Label {label} />
|
||||
{#if Array.isArray(selected) && selected.length}
|
||||
<span class="content-dark-color ml-1-5">{selected.length}</span>
|
||||
{/if}
|
||||
{:else}
|
||||
<Label {label} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="subheader">
|
||||
{#if !showManageAttributes}
|
||||
<span class="content-halfcontent-color overflow-label" style:margin-top={'-.5rem'}>
|
||||
<Label label={tracker.string.MoveIssuesDescription} />
|
||||
</span>
|
||||
<Button icon={IconClose} iconProps={{ size: 'medium' }} kind="ghost" on:click={() => dispatch('close')} />
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<div>
|
||||
<Label label={tracker.string.MoveIssuesDescription} />
|
||||
</div>
|
||||
|
||||
<div class="space-between mt-6 mb-4">
|
||||
{#if !showManageAttributes}
|
||||
<div class="flex-between">
|
||||
{#if currentSpace !== undefined}
|
||||
<SpaceSelector
|
||||
_class={currentSpace._class}
|
||||
label={hierarchy.getClass(tracker.class.Project).label}
|
||||
bind:space
|
||||
kind={'regular'}
|
||||
size={'small'}
|
||||
size={'large'}
|
||||
component={ProjectPresenter}
|
||||
iconWithEmoji={tracker.component.IconWithEmoji}
|
||||
defaultIcon={tracker.icon.Home}
|
||||
/>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span
|
||||
class="aligned-text"
|
||||
class:disabled={!isManageAttributesAvailable}
|
||||
on:click|stopPropagation={() => {
|
||||
<Button
|
||||
label={tracker.string.ManageAttributes}
|
||||
iconRight={IconForward}
|
||||
kind={'ghost'}
|
||||
size={'small'}
|
||||
padding={'0 1rem'}
|
||||
disabled={!isManageAttributesAvailable}
|
||||
on:click={() => {
|
||||
if (!isManageAttributesAvailable) {
|
||||
return
|
||||
}
|
||||
showManageAttributes = !showManageAttributes
|
||||
}}
|
||||
>
|
||||
Manage attributes >
|
||||
</span>
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if loading}<Spinner />{/if}
|
||||
|
||||
<div class="divider" />
|
||||
{#if currentSpace !== undefined && !keepOriginalAttribytes}
|
||||
<SelectReplacement
|
||||
{statuses}
|
||||
{components}
|
||||
targetProject={currentSpace}
|
||||
issues={toMove}
|
||||
bind:statusToUpdate
|
||||
bind:componentToUpdate
|
||||
/>
|
||||
<div class="divider" />
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="space-between pb-4">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span
|
||||
class="fs-title aligned-text"
|
||||
on:click|stopPropagation={() => (showManageAttributes = !showManageAttributes)}
|
||||
>
|
||||
<Label label={getEmbeddedLabel('< Manage attributes')} />
|
||||
</span>
|
||||
<Button icon={IconClose} iconProps={{ size: 'medium' }} kind="ghost" on:click={() => dispatch('close')} />
|
||||
</div>
|
||||
<div class="divider" />
|
||||
|
||||
<div class="issues-move flex-col">
|
||||
{#if loading}
|
||||
<Spinner />
|
||||
{:else if toMove.length > 0 && currentSpace}
|
||||
{#each toMove as issue}
|
||||
{@const upd = issueToUpdate.get(issue._id) ?? {}}
|
||||
{@const originalComponent = components.find((it) => it._id === issue.component)}
|
||||
{@const targetComponent = components.find(
|
||||
(it) => it.space === currentSpace?._id && it.label === originalComponent?.label
|
||||
)}
|
||||
{#key keepOriginalAttribytes}
|
||||
{#if issue.space !== currentSpace._id && (upd.status !== undefined || upd.component !== undefined)}
|
||||
<div class="issue-move pb-2">
|
||||
<div class="flex-row-center pl-1">
|
||||
<PriorityEditor value={issue} isEditable={false} />
|
||||
<IssuePresenter value={issue} disabled kind={'list'} />
|
||||
<div class="ml-2 max-w-30">
|
||||
<TitlePresenter disabled value={issue} showParent={false} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="pl-4">
|
||||
{#key upd.status}
|
||||
<StatusMovePresenter {issue} bind:issueToUpdate targetProject={currentSpace} {statuses} />
|
||||
{/key}
|
||||
{#if targetComponent === undefined}
|
||||
{#key upd.component}
|
||||
<ComponentMovePresenter {issue} bind:issueToUpdate targetProject={currentSpace} {components} />
|
||||
{/key}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<svelte:fragment slot="blocks" let:block>
|
||||
{#if !showManageAttributes}
|
||||
{#if currentSpace !== undefined && !keepOriginalAttribytes}
|
||||
<SelectReplacement
|
||||
{statuses}
|
||||
{components}
|
||||
targetProject={currentSpace}
|
||||
issues={toMove}
|
||||
bind:statusToUpdate
|
||||
bind:componentToUpdate
|
||||
/>
|
||||
{/if}
|
||||
{:else if toMove.length > 0 && currentSpace}
|
||||
{@const issue = toMove[block]}
|
||||
{@const upd = issueToUpdate.get(issue._id) ?? {}}
|
||||
{@const originalComponent = components.find((it) => it._id === issue.component)}
|
||||
{@const targetComponent = components.find(
|
||||
(it) => it.space === currentSpace?._id && it.label === originalComponent?.label
|
||||
)}
|
||||
{#key keepOriginalAttribytes}
|
||||
{#if issue.space !== currentSpace._id && (upd.status !== undefined || upd.component !== undefined)}
|
||||
<div class="flex-row-center min-h-9 gap-1-5 content-color">
|
||||
<PriorityEditor value={issue} isEditable={false} kind={'list'} size={'small'} shouldShowLabel={false} />
|
||||
<IssuePresenter value={issue} disabled kind={'list'} />
|
||||
<TitlePresenter disabled value={issue} showParent={false} maxWidth={'7.5rem'} />
|
||||
</div>
|
||||
{#key upd.status}
|
||||
<StatusMovePresenter {issue} bind:issueToUpdate targetProject={currentSpace} {statuses} />
|
||||
{/key}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if targetComponent === undefined}
|
||||
{#key upd.component}
|
||||
<ComponentMovePresenter {issue} bind:issueToUpdate targetProject={currentSpace} {components} />
|
||||
{/key}
|
||||
{/if}
|
||||
{/if}
|
||||
{/key}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="space-between mt-4">
|
||||
<svelte:fragment slot="footer">
|
||||
<div
|
||||
class="aligned-text"
|
||||
class="flex-row-center gap-2"
|
||||
use:tooltip={{
|
||||
component: Label,
|
||||
props: { label: tracker.string.KeepOriginalAttributesTooltip }
|
||||
}}
|
||||
>
|
||||
<div class="mr-2">
|
||||
<Toggle
|
||||
disabled={!isManageAttributesAvailable}
|
||||
on:change={() => {
|
||||
keepOriginalAttribytes = !keepOriginalAttribytes
|
||||
if (!keepOriginalAttribytes) {
|
||||
statusToUpdate = {}
|
||||
componentToUpdate = {}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Label label={tracker.string.KeepOriginalAttributes} />
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<Button
|
||||
label={view.string.Move}
|
||||
size={'small'}
|
||||
disabled={docs[0]?.space === currentSpace?._id}
|
||||
kind={'accented'}
|
||||
on:click={moveAll}
|
||||
loading={processing}
|
||||
/>
|
||||
<Button
|
||||
size={'small'}
|
||||
label={ui.string.Cancel}
|
||||
on:click={() => {
|
||||
dispatch('close')
|
||||
<Toggle
|
||||
disabled={!isManageAttributesAvailable}
|
||||
on:change={() => {
|
||||
keepOriginalAttribytes = !keepOriginalAttribytes
|
||||
if (!keepOriginalAttribytes) {
|
||||
statusToUpdate = {}
|
||||
componentToUpdate = {}
|
||||
}
|
||||
}}
|
||||
disabled={processing}
|
||||
/>
|
||||
<span class="lines-limit-2">
|
||||
<Label label={tracker.string.KeepOriginalAttributes} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1.25rem 1.5rem 1rem;
|
||||
width: 480px;
|
||||
max-width: 40rem;
|
||||
background: var(--popup-bg-color);
|
||||
border-radius: 8px;
|
||||
user-select: none;
|
||||
|
||||
.aligned-text {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.space-between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
flex-shrink: 0;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
direction: rtl;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
column-gap: 0.5rem;
|
||||
}
|
||||
.issues-move {
|
||||
overflow: auto;
|
||||
}
|
||||
.issue-move {
|
||||
border-bottom: 1px solid var(--popup-divider);
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
border-bottom: 1px solid var(--theme-divider-color);
|
||||
}
|
||||
|
||||
.disabled {
|
||||
cursor: not-allowed;
|
||||
color: var(--dark-color);
|
||||
}
|
||||
</style>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="buttons">
|
||||
<Button label={ui.string.Cancel} size={'large'} disabled={processing} on:click={() => dispatch('close')} />
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
@ -84,7 +84,7 @@
|
||||
{#if value}
|
||||
{#if kind === 'list' || kind === 'list-header'}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="priority-container" on:click={handlePriorityEditorOpened}>
|
||||
<div class="priority-container" class:cursor-pointer={isEditable} on:click={handlePriorityEditorOpened}>
|
||||
<div class="icon">
|
||||
{#if issuePriorities[value.priority]?.icon}<Icon icon={issuePriorities[value.priority]?.icon} {size} />{/if}
|
||||
</div>
|
||||
@ -116,7 +116,6 @@
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
min-width: 0;
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
|
@ -21,10 +21,11 @@
|
||||
|
||||
export let value: WithLookup<Issue>
|
||||
export let shouldUseMargin: boolean = false
|
||||
export let showParent = true
|
||||
export let showParent: boolean = true
|
||||
export let kind: 'list' | undefined = undefined
|
||||
export let onClick: (() => void) | undefined = undefined
|
||||
export let disabled = false
|
||||
export let disabled: boolean = false
|
||||
export let maxWidth: string | undefined = undefined
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
@ -32,6 +33,7 @@
|
||||
class="name overflow-label select-text"
|
||||
class:with-margin={shouldUseMargin}
|
||||
class:list={kind === 'list'}
|
||||
style:max-width={maxWidth}
|
||||
title={value.title}
|
||||
>
|
||||
<DocNavLink
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Button, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { Button, Grid, IconArrowRight, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { Component, Issue, Project } from '@hcengineering/tracker'
|
||||
|
||||
import { IssueToUpdate } from '../../../utils'
|
||||
@ -31,13 +31,16 @@
|
||||
</script>
|
||||
|
||||
{#if current !== undefined}
|
||||
<div class="flex-row-center p-1">
|
||||
<div class="side-columns aligned-text">
|
||||
<Grid rowGap={0.25} topGap>
|
||||
<div class="flex-between min-h-11">
|
||||
<ComponentPresenter value={current} disabled />
|
||||
<IconArrowRight size={'small'} fill={'var(--theme-halfcontent-color)'} />
|
||||
</div>
|
||||
<span class="middle-column aligned-text">-></span>
|
||||
<div class="side-columns">
|
||||
<div class="flex-row-center min-h-11">
|
||||
<Button
|
||||
size={'large'}
|
||||
shape={'round-sm'}
|
||||
width={'min-content'}
|
||||
on:click={(event) => {
|
||||
showPopup(
|
||||
ComponentReplacementPopup,
|
||||
@ -67,7 +70,7 @@
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, Status } from '@hcengineering/core'
|
||||
import { Button, Label, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { Button, Label, Grid, IconArrowRight, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { Component, Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
|
||||
@ -81,114 +81,91 @@
|
||||
</script>
|
||||
|
||||
{#if issues[0]?.space !== targetProject._id && (Object.keys(statusToUpdate).length > 0 || missingComponents.length > 0)}
|
||||
<div class="mt-4 mb-4">
|
||||
<span class="caption-color">
|
||||
<Label label={tracker.string.SelectReplacement} />
|
||||
</span>
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="missing-items">
|
||||
<span class="side-columns">
|
||||
<Label label={tracker.string.MissingItem} />
|
||||
</span>
|
||||
<span class="middle-column" />
|
||||
<span class="side-columns">
|
||||
<Label label={tracker.string.Replacement} />
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
{#each Object.keys(statusToUpdate) as status}
|
||||
{@const newStatus = statusToUpdate[status]}
|
||||
<div class="missing-items mt-4">
|
||||
<div class="side-columns aligned-text">
|
||||
<StatusRefPresenter value={getStatusRef(status)} kind={'list-header'} />
|
||||
</div>
|
||||
<span class="middle-column aligned-text">-></span>
|
||||
<div class="side-columns">
|
||||
<Button
|
||||
on:click={(event) => {
|
||||
showPopup(
|
||||
StatusReplacementPopup,
|
||||
{
|
||||
statuses,
|
||||
original: $statusStore.get(getStatusRef(status)),
|
||||
selected: getStatusRef(newStatus.ref)
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
(value) => {
|
||||
if (value) {
|
||||
const createStatus = typeof value === 'object'
|
||||
const s = createStatus ? value.create : value
|
||||
statusToUpdate = { ...statusToUpdate, [status]: { ref: s, create: createStatus } }
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
>
|
||||
<span slot="content" class="flex-row-center pointer-events-none">
|
||||
<StatusRefPresenter value={getStatusRef(newStatus.ref)} />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
{#each missingComponents as component}
|
||||
{@const componentRef = componentToUpdate[component._id]?.ref}
|
||||
<div class="missing-items mt-4">
|
||||
<div class="side-columns aligned-text">
|
||||
<ComponentPresenter value={component} disabled />
|
||||
</div>
|
||||
<span class="middle-column aligned-text">-></span>
|
||||
<div class="side-columns aligned-text">
|
||||
<Button
|
||||
on:click={(event) => {
|
||||
showPopup(
|
||||
ComponentReplacementPopup,
|
||||
{
|
||||
components: components.filter((it) => it.space === targetProject._id),
|
||||
original: component,
|
||||
selected: componentRef
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
(value) => {
|
||||
if (value !== undefined) {
|
||||
const createComponent = typeof value === 'object'
|
||||
const c = createComponent ? value.create : value
|
||||
componentToUpdate = {
|
||||
...componentToUpdate,
|
||||
[component._id]: { ref: c, create: createComponent }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
>
|
||||
<span slot="content" class="flex-row-center pointer-events-none">
|
||||
<ComponentRefPresenter value={componentRef} />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="caption-color mb-4">
|
||||
<Label label={tracker.string.SelectReplacement} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.missing-items {
|
||||
display: flex;
|
||||
}
|
||||
.side-columns {
|
||||
width: 40%;
|
||||
}
|
||||
.middle-column {
|
||||
width: 10%;
|
||||
}
|
||||
.aligned-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
<Grid rowGap={0.25} columnGap={2}>
|
||||
<div class="flex-row-center min-h-8 content-dark-color text-xs font-medium tracking-1px uppercase">
|
||||
<Label label={tracker.string.MissingItem} />
|
||||
</div>
|
||||
<div class="flex-row-center min-h-8 content-dark-color text-xs font-medium tracking-1px uppercase">
|
||||
<Label label={tracker.string.Replacement} />
|
||||
</div>
|
||||
{#each Object.keys(statusToUpdate) as status}
|
||||
{@const newStatus = statusToUpdate[status]}
|
||||
<div class="flex-between min-h-11">
|
||||
<StatusRefPresenter value={getStatusRef(status)} kind={'list-header'} />
|
||||
<IconArrowRight size={'small'} fill={'var(--theme-halfcontent-color)'} />
|
||||
</div>
|
||||
<div class="flex-row-center min-h-11">
|
||||
<Button
|
||||
size={'large'}
|
||||
shape={'round-sm'}
|
||||
width={'min-content'}
|
||||
on:click={(event) => {
|
||||
showPopup(
|
||||
StatusReplacementPopup,
|
||||
{
|
||||
statuses,
|
||||
original: $statusStore.get(getStatusRef(status)),
|
||||
selected: getStatusRef(newStatus.ref)
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
(value) => {
|
||||
if (value) {
|
||||
const createStatus = typeof value === 'object'
|
||||
const s = createStatus ? value.create : value
|
||||
statusToUpdate = { ...statusToUpdate, [status]: { ref: s, create: createStatus } }
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
>
|
||||
<span slot="content" class="flex-row-center pointer-events-none">
|
||||
<StatusRefPresenter value={getStatusRef(newStatus.ref)} />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
{#each missingComponents as component}
|
||||
{@const componentRef = componentToUpdate[component._id]?.ref}
|
||||
<div class="flex-between min-h-11">
|
||||
<ComponentPresenter value={component} disabled />
|
||||
<IconArrowRight size={'small'} fill={'var(--theme-halfcontent-color)'} />
|
||||
</div>
|
||||
<div class="flex-row-center min-h-11">
|
||||
<Button
|
||||
size={'large'}
|
||||
shape={'round-sm'}
|
||||
width={'min-content'}
|
||||
on:click={(event) => {
|
||||
showPopup(
|
||||
ComponentReplacementPopup,
|
||||
{
|
||||
components: components.filter((it) => it.space === targetProject._id),
|
||||
original: component,
|
||||
selected: componentRef
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
(value) => {
|
||||
if (value !== undefined) {
|
||||
const createComponent = typeof value === 'object'
|
||||
const c = createComponent ? value.create : value
|
||||
componentToUpdate = {
|
||||
...componentToUpdate,
|
||||
[component._id]: { ref: c, create: createComponent }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
>
|
||||
<span slot="content" class="flex-row-center pointer-events-none">
|
||||
<ComponentRefPresenter value={componentRef} />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
</Grid>
|
||||
{/if}
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Button, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { Button, Grid, IconArrowRight, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
|
||||
import { IssueToUpdate } from '../../../utils'
|
||||
import StatusRefPresenter from '../StatusRefPresenter.svelte'
|
||||
@ -31,13 +31,16 @@
|
||||
$: original = $statusStore.get(issue.status)
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center p-1">
|
||||
<div class="side-columns aligned-text">
|
||||
<Grid rowGap={0.25} topGap>
|
||||
<div class="flex-between min-h-11">
|
||||
<StatusRefPresenter value={issue.status} size={'small'} />
|
||||
<IconArrowRight size={'small'} fill={'var(--theme-halfcontent-color)'} />
|
||||
</div>
|
||||
<span class="middle-column aligned-text">-></span>
|
||||
<div class="side-columns">
|
||||
<div class="flex-row-center min-h-11">
|
||||
<Button
|
||||
size={'large'}
|
||||
shape={'round-sm'}
|
||||
width={'min-content'}
|
||||
on:click={(event) => {
|
||||
showPopup(
|
||||
StatusReplacementPopup,
|
||||
@ -63,17 +66,4 @@
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.side-columns {
|
||||
width: 45%;
|
||||
}
|
||||
.middle-column {
|
||||
width: 10%;
|
||||
}
|
||||
.aligned-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</Grid>
|
||||
|
@ -121,7 +121,7 @@
|
||||
on:dragend={resetDrag}
|
||||
>
|
||||
<div class="draggable-container">
|
||||
<div class="draggable-mark"><IconCircles /></div>
|
||||
<IconCircles size={'small'} />
|
||||
</div>
|
||||
<div class="flex-row-center ml-6 clear-mins gap-2">
|
||||
<StatusEditor
|
||||
@ -209,23 +209,18 @@
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 1.5rem;
|
||||
top: 50%;
|
||||
left: 0.125rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
opacity: 0;
|
||||
transform: translateY(-50%);
|
||||
transition: opacity 0.1s;
|
||||
cursor: grabbing;
|
||||
|
||||
.draggable-mark {
|
||||
opacity: 0;
|
||||
width: 0.375rem;
|
||||
height: 1rem;
|
||||
margin-left: 0.75rem;
|
||||
transition: opacity 0.1s;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.draggable-mark {
|
||||
opacity: 0.4;
|
||||
}
|
||||
&:hover .draggable-container {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
&.is-dragging::before {
|
||||
|
@ -123,7 +123,7 @@
|
||||
on:dragend={resetDrag}
|
||||
>
|
||||
<div class="draggable-container">
|
||||
<div class="draggable-mark"><IconCircles /></div>
|
||||
<IconCircles size={'small'} />
|
||||
</div>
|
||||
<div class="flex-row-center ml-6 clear-mins gap-2">
|
||||
<PriorityEditor
|
||||
@ -204,23 +204,18 @@
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 1.5rem;
|
||||
top: 50%;
|
||||
left: 0.125rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
opacity: 0;
|
||||
transform: translateY(-50%);
|
||||
transition: opacity 0.1s;
|
||||
cursor: grabbing;
|
||||
|
||||
.draggable-mark {
|
||||
opacity: 0;
|
||||
width: 0.375rem;
|
||||
height: 1rem;
|
||||
margin-left: 0.75rem;
|
||||
transition: opacity 0.1s;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.draggable-mark {
|
||||
opacity: 0.4;
|
||||
}
|
||||
&:hover .draggable-container {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
&.is-dragging::before {
|
||||
|
@ -51,13 +51,13 @@
|
||||
$: canSave = !isSaving && (value.name ?? '').length > 0
|
||||
</script>
|
||||
|
||||
<div class="flex-between background-button-bg-color border-radius-1 p-2 root">
|
||||
<div class="flex-between border-radius-1 statusEditor-container">
|
||||
<div class="flex flex-grow items-center clear-mins inputs">
|
||||
<div class="flex-no-shrink draggable-mark">
|
||||
{#if !isSingle}<IconCircles />{/if}
|
||||
{#if !isSingle}<IconCircles size={'small'} />{/if}
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="flex-no-shrink ml-2 color" on:click={pickColor}>
|
||||
<div class="flex-no-shrink color" on:click={pickColor}>
|
||||
<div class="dot" style="background-color: {getPlatformColorDef(value.color ?? 0, $themeStore.dark).color}" />
|
||||
</div>
|
||||
<div class="ml-2 w-full name">
|
||||
@ -67,61 +67,74 @@
|
||||
<StatusInput bind:value={value.description} placeholder={tracker.string.Description} fill />
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons-group small-gap flex-no-shrink ml-2 mr-1">
|
||||
<Button label={presentation.string.Cancel} kind="regular" on:click={() => dispatch('cancel')} />
|
||||
<Button label={presentation.string.Save} kind="accented" disabled={!canSave} on:click={() => dispatch('save')} />
|
||||
<div class="buttons-group small-gap flex-no-shrink ml-2">
|
||||
<Button label={presentation.string.Cancel} size={'small'} kind={'regular'} on:click={() => dispatch('cancel')} />
|
||||
<Button
|
||||
label={presentation.string.Save}
|
||||
size={'small'}
|
||||
kind={'accented'}
|
||||
disabled={!canSave}
|
||||
on:click={() => dispatch('save')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
.statusEditor-container {
|
||||
padding: 0.75rem 1.125rem;
|
||||
line-height: 1.125rem;
|
||||
background-color: var(--theme-button-default);
|
||||
|
||||
.draggable-mark {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0.125rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
transform: translateY(-50%);
|
||||
opacity: 0;
|
||||
|
||||
&.draggable {
|
||||
transition: opacity 0.15s;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: -0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.color {
|
||||
position: relative;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
background-color: var(--theme-bg-dark-color);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
|
||||
.dot {
|
||||
position: absolute;
|
||||
content: '';
|
||||
border-radius: 50%;
|
||||
inset: 30%;
|
||||
}
|
||||
}
|
||||
.inputs {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
.name {
|
||||
max-width: 10rem;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--theme-button-hovered);
|
||||
|
||||
.draggable-mark {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.inputs {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
.name {
|
||||
max-width: 10rem;
|
||||
}
|
||||
}
|
||||
|
||||
.draggable-mark {
|
||||
position: relative;
|
||||
opacity: 0;
|
||||
width: 0.375rem;
|
||||
height: 1rem;
|
||||
margin-left: 0.25rem;
|
||||
transition: opacity 0.1s;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: -0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.color {
|
||||
position: relative;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
background-color: var(--accent-bg-color);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
|
||||
.dot {
|
||||
position: absolute;
|
||||
content: '';
|
||||
border-radius: 50%;
|
||||
inset: 30%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -57,8 +57,6 @@
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
font-weight: 500;
|
||||
|
||||
&.fill {
|
||||
width: 100%;
|
||||
|
||||
@ -69,7 +67,7 @@
|
||||
|
||||
input {
|
||||
padding: 0.25rem 0.5rem;
|
||||
background-color: var(--accent-bg-color);
|
||||
background-color: var(--theme-bg-dark-color);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem;
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { IssueStatus } from '@hcengineering/tracker'
|
||||
import { Icon, IconCircles, IconClose, IconEdit, Label, tooltip } from '@hcengineering/ui'
|
||||
import { Icon, IconCircles, IconDelete, IconEdit, Label, tooltip } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import IssueStatusIcon from '../issues/IssueStatusIcon.svelte'
|
||||
@ -30,12 +30,12 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-between background-button-bg-color border-radius-1 p-2 root" on:dblclick|preventDefault={edit}>
|
||||
<div class="flex flex-grow items-center">
|
||||
<div class="flex-no-shrink draggable-mark" class:draggable={!isSingle}>
|
||||
<IconCircles />
|
||||
<div class="flex-between border-radius-1 px-6 h-8 status-container" on:dblclick|preventDefault={edit}>
|
||||
<div class="flex-row-center flex-grow">
|
||||
<div class="draggable-mark" class:draggable={!isSingle}>
|
||||
<IconCircles size={'small'} />
|
||||
</div>
|
||||
<div class="flex-no-shrink ml-2">
|
||||
<div class="flex-no-shrink">
|
||||
<IssueStatusIcon {value} size="small" />
|
||||
</div>
|
||||
<span class="caption-color ml-2">{value.name}</span>
|
||||
@ -43,7 +43,7 @@
|
||||
<span> · {value.description}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="buttons-group flex-no-shrink mr-1">
|
||||
<div class="buttons-group flex-no-shrink">
|
||||
{#if isDefault}
|
||||
<Label label={tracker.string.Default} />
|
||||
{:else if value.category === tracker.issueStatusCategory.Backlog || value.category === tracker.issueStatusCategory.Unstarted}
|
||||
@ -58,7 +58,7 @@
|
||||
use:tooltip={{ label: tracker.string.EditWorkflowStatus, direction: 'bottom' }}
|
||||
on:click|preventDefault={edit}
|
||||
>
|
||||
<Icon icon={IconEdit} size="small" />
|
||||
<Icon icon={IconEdit} size={'small'} />
|
||||
</div>
|
||||
{#if !isDefault}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
@ -67,54 +67,45 @@
|
||||
use:tooltip={{ label: tracker.string.DeleteWorkflowStatus, direction: 'bottom' }}
|
||||
on:click|preventDefault={() => dispatch('delete', value)}
|
||||
>
|
||||
<Icon icon={IconClose} size="small" />
|
||||
<Icon icon={IconDelete} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
&:hover {
|
||||
.btn {
|
||||
opacity: 1;
|
||||
}
|
||||
.status-container {
|
||||
background-color: var(--theme-button-default);
|
||||
|
||||
.draggable-mark.draggable {
|
||||
cursor: grab;
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
position: relative;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
color: var(--content-color);
|
||||
transition: color 0.15s;
|
||||
transition: opacity 0.15s;
|
||||
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
}
|
||||
|
||||
&::before {
|
||||
.draggable-mark {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: -0.5rem;
|
||||
top: 0.5rem;
|
||||
left: 0.125rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
opacity: 0;
|
||||
|
||||
&.draggable {
|
||||
transition: opacity 0.15s;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: -0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.draggable-mark {
|
||||
opacity: 0;
|
||||
position: relative;
|
||||
width: 0.375rem;
|
||||
height: 1rem;
|
||||
margin-left: 0.25rem;
|
||||
|
||||
&.draggable {
|
||||
.btn {
|
||||
position: relative;
|
||||
color: var(--theme-dark-color);
|
||||
transition: color 0.15s;
|
||||
transition: opacity 0.15s;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--theme-content-color);
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
@ -122,5 +113,22 @@
|
||||
inset: -0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
.btn {
|
||||
opacity: 1;
|
||||
}
|
||||
.draggable-mark.draggable {
|
||||
opacity: 0.4;
|
||||
cursor: grab;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--theme-button-hovered);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--theme-button-pressed);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -14,20 +14,9 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { Class, Data, Ref, SortingOrder, StatusCategory } from '@hcengineering/core'
|
||||
import { createQuery, getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient, MessageBox, Card } from '@hcengineering/presentation'
|
||||
import { calcRank, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
closeTooltip,
|
||||
ExpandCollapse,
|
||||
Icon,
|
||||
IconAdd,
|
||||
Label,
|
||||
Loading,
|
||||
Panel,
|
||||
Scroller,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { Button, closeTooltip, ExpandCollapse, IconAdd, Label, Loading, Scroller, showPopup } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { flip } from 'svelte/animate'
|
||||
import tracker from '../../plugin'
|
||||
@ -241,28 +230,25 @@
|
||||
$: projectStatuses = $statusStore.getDocs().filter((status) => status.space === projectId)
|
||||
</script>
|
||||
|
||||
<Panel isHeader={false} isAside={false} on:fullsize on:close={() => dispatch('close')}>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="antiTitle icon-wrapper">
|
||||
<div class="wrapped-icon">
|
||||
<Icon icon={tracker.icon.Issue} size="small" />
|
||||
</div>
|
||||
<div class="title-wrapper">
|
||||
<span class="wrapped-title">
|
||||
<Label label={tracker.string.ManageWorkflowStatuses} />
|
||||
</span>
|
||||
{#if project}
|
||||
<span class="wrapped-subtitle">{project.name}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<Card
|
||||
label={tracker.string.ManageWorkflowStatuses}
|
||||
onCancel={() => dispatch('close')}
|
||||
okAction={() => {}}
|
||||
accentHeader
|
||||
hideAttachments
|
||||
hideFooter
|
||||
>
|
||||
<svelte:fragment slot="subheader">
|
||||
{#if project}
|
||||
<span class="content-halfcontent-color overflow-label" style:margin-top={'-.5rem'}>{project.name}</span>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
{#if project === undefined || statusCategories === undefined || projectStatuses.length === 0}
|
||||
<Loading />
|
||||
{:else}
|
||||
<Scroller>
|
||||
<div class="popupPanel-body__main-content py-10 clear-mins flex-no-shrink">
|
||||
<div class="content">
|
||||
{#each statusCategories as category}
|
||||
{@const statuses = projectStatuses.filter((s) => s.category === category._id) ?? []}
|
||||
{@const isSingle = statuses.length === 1}
|
||||
@ -270,10 +256,9 @@
|
||||
<Label label={category.label} />
|
||||
<Button
|
||||
showTooltip={{ label: tracker.string.AddWorkflowStatus }}
|
||||
width="min-content"
|
||||
icon={IconAdd}
|
||||
size="small"
|
||||
kind="ghost"
|
||||
size={'medium'}
|
||||
kind={'ghost'}
|
||||
on:click={() => {
|
||||
closeTooltip()
|
||||
editingStatus = { category: category._id, color: category.color }
|
||||
@ -320,43 +305,60 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
<ExpandCollapse isExpanded>
|
||||
{#if editingStatus && !('_id' in editingStatus) && editingStatus.category === category._id}
|
||||
{#if editingStatus && !('_id' in editingStatus) && editingStatus.category === category._id}
|
||||
<ExpandCollapse isExpanded>
|
||||
<StatusEditor value={editingStatus} on:cancel={cancelEditing} on:save={addStatus} {isSaving} isSingle />
|
||||
{/if}
|
||||
</ExpandCollapse>
|
||||
</ExpandCollapse>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Scroller>
|
||||
{/if}
|
||||
</Panel>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.row {
|
||||
position: relative;
|
||||
margin-bottom: 0.25rem;
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 100%;
|
||||
max-width: 54rem;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
|
||||
&.is-dragged-over-up::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: 0;
|
||||
border-top: 1px solid var(--theme-caret-color);
|
||||
.category-name {
|
||||
margin: 1rem 0 0.25rem 0;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
font-size: 0.625rem;
|
||||
letter-spacing: 1px;
|
||||
color: var(--theme-dark-color);
|
||||
|
||||
&:first-child {
|
||||
margin: 0 0 0.25rem 0;
|
||||
}
|
||||
}
|
||||
.row {
|
||||
position: relative;
|
||||
|
||||
&.is-dragged-over-down::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: 0;
|
||||
border-bottom: 1px solid var(--theme-caret-color);
|
||||
&.is-dragged-over-up::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: 0;
|
||||
border-top: 1px solid var(--theme-caret-color);
|
||||
}
|
||||
&.is-dragged-over-down::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: 0;
|
||||
border-bottom: 1px solid var(--theme-caret-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.category-name {
|
||||
margin: 1rem 0 0.5rem 0;
|
||||
|
||||
&:first-child {
|
||||
margin: 0 0 0.5rem 0;
|
||||
.row + .row {
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -202,12 +202,12 @@ export async function queryIssue<D extends Issue> (
|
||||
}
|
||||
|
||||
async function move (issues: Issue | Issue[]): Promise<void> {
|
||||
showPopup(MoveIssues, { selected: issues })
|
||||
showPopup(MoveIssues, { selected: issues }, 'top')
|
||||
}
|
||||
|
||||
async function editWorkflowStatuses (project: Project | undefined): Promise<void> {
|
||||
if (project !== undefined) {
|
||||
showPopup(Statuses, { projectId: project._id, projectClass: project._class }, 'float')
|
||||
showPopup(Statuses, { projectId: project._id, projectClass: project._class }, 'top')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,6 +180,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
Duplicate: '' as IntlString,
|
||||
MoveIssues: '' as IntlString,
|
||||
MoveIssuesDescription: '' as IntlString,
|
||||
ManageAttributes: '' as IntlString,
|
||||
KeepOriginalAttributes: '' as IntlString,
|
||||
KeepOriginalAttributesTooltip: '' as IntlString,
|
||||
SelectReplacement: '' as IntlString,
|
||||
|
@ -22,7 +22,7 @@
|
||||
export let object: Doc | undefined
|
||||
export let disabled = false
|
||||
export let onClick: ((event: MouseEvent) => void) | undefined = undefined
|
||||
export let noUnderline = false
|
||||
export let noUnderline = disabled
|
||||
export let inline = false
|
||||
export let colorInherit: boolean = false
|
||||
export let component: AnyComponent = view.component.EditDoc
|
||||
|
@ -30,18 +30,18 @@
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2rem 1.75rem 1.75rem;
|
||||
border-radius: 0.75rem;
|
||||
background: var(--popup-bg-color);
|
||||
box-shadow: var(--popup-shadow);
|
||||
padding: 1rem 1.5rem 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--theme-popup-color);
|
||||
border: 1px solid var(--theme-popup-divider);
|
||||
box-shadow: var(--theme-popup-shadow);
|
||||
|
||||
.label {
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 1.25rem;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
color: var(--dark-color);
|
||||
margin-bottom: 1rem;
|
||||
min-height: 1.75rem;
|
||||
font-size: 1rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
|
||||
.content {
|
||||
|
@ -26,7 +26,7 @@
|
||||
<span
|
||||
use:tooltip={{ label: plugin.string.ShowPreviewOnClick }}
|
||||
on:click={() => {
|
||||
showPopup(IndexedDocumentPreview, { objectId: value._id, search })
|
||||
showPopup(IndexedDocumentPreview, { objectId: value._id, search }, 'centered')
|
||||
}}
|
||||
>
|
||||
{#if value.$source?.$score}
|
||||
|
@ -91,9 +91,7 @@
|
||||
>
|
||||
<div class="draggable-container">
|
||||
<div class="draggable-mark">
|
||||
<IconCircles />
|
||||
<div class="space" />
|
||||
<IconCircles />
|
||||
<IconCircles size={'small'} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-center relative mr-1" use:tooltip={{ label: view.string.Select, direction: 'bottom' }}>
|
||||
@ -168,16 +166,12 @@
|
||||
{/each}
|
||||
{#if compactMode}
|
||||
<div class="panel-trigger" tabindex="-1">
|
||||
<IconCircles />
|
||||
<div class="space" />
|
||||
<IconCircles />
|
||||
<IconCircles size={'small'} />
|
||||
</div>
|
||||
<div class="hidden-panel" tabindex="-1">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="header" on:click={(ev) => ev.currentTarget.blur()}>
|
||||
<IconCircles />
|
||||
<div class="space" />
|
||||
<IconCircles />
|
||||
<IconCircles size={'small'} />
|
||||
</div>
|
||||
<div class="scroll-box gap-2">
|
||||
{#if mobile}
|
||||
|
@ -33,10 +33,12 @@
|
||||
<div
|
||||
class="root flex background-button-bg-color border-radius-1"
|
||||
{style}
|
||||
style:background={'red'}
|
||||
on:dblclick|preventDefault={isEditable && !isEditing ? () => dispatch('edit') : undefined}
|
||||
>
|
||||
<div class="flex-center ml-2">
|
||||
<div class="flex-no-shrink circles-mark" class:isDraggable><IconCircles /></div>
|
||||
<div class="flex-no-shrink circles-mark" class:isDraggable><IconCircles size={'small'} /></div>
|
||||
!!!
|
||||
</div>
|
||||
|
||||
<div class="root flex flex-between items-center w-full p-2">
|
||||
|
@ -238,7 +238,7 @@ test('report-time-from-main-view', async ({ page }) => {
|
||||
await page.waitForSelector('text="Estimation"')
|
||||
|
||||
await page.click('button:has-text("Add time report")')
|
||||
await page.waitForSelector('.antiCard-header >> .antiCard-header__title-wrap >> span:has-text("Add time report")')
|
||||
await page.waitForSelector('[id="tracker\\:string\\:TimeSpendReportAdd"] >> text=Add time report')
|
||||
await expect(page.locator('button:has-text("Create")')).toBeDisabled()
|
||||
await page.fill('[placeholder="Reported\\ days"]', `${time}`)
|
||||
await expect(page.locator('button:has-text("Create")')).toBeEnabled()
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user