UBER-219: updated CreateIssue layout (#3244)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2023-05-24 13:27:57 +03:00 committed by GitHub
parent d8e69eb6bc
commit edfc99ccd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 454 additions and 303 deletions

View File

@ -14,17 +14,16 @@
"AddSocialLinks": "Add social links",
"EditSocialLinks": "Edit social links",
"Change": "Change",
"Remove": "Remove",
"Remove": "Remove",
"Search": "Search...",
"Spaces": "Spaces",
"CreateMore": "Create more",
"Spaces": "Spaces",
"NumberSpaces": "{count, plural, =0 {In} =1 {In 1 place} other {In # places}}",
"InThis": "In this {space}",
"NoMatchesInThis": "No matches in this {space}",
"NoMatchesFound": "No matches found",
"NotInThis": "Not in this {space}",
"Add": "Add",
"Edit": "Edit",
"Edit": "Edit",
"DocumentPreview": "Preview",
"MakePrivate": "Make private",
"MakePrivateDescription": "Only members can see it"

View File

@ -16,8 +16,7 @@
"Change": "Изменить",
"Remove": "Удалить",
"Search": "Поиск...",
"Spaces": "Пространства",
"CreateMore": "Создать еще",
"Spaces": "Пространства",
"NumberSpaces": "{count, plural, =0 {В} =1 {В 1 месте} other {В # местах}}",
"InThis": "В этом {space}",
"NoMatchesInThis": "В этом {space} совпадения не обнаружены",

View File

@ -15,7 +15,7 @@
-->
<script lang="ts">
import type { IntlString } from '@hcengineering/platform'
import { Button, IconClose, Label, MiniToggle, Scroller } from '@hcengineering/ui'
import { Button, IconClose, Label, Scroller } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import presentation from '..'
import { deviceOptionsStore as deviceInfo, resizeObserver } from '@hcengineering/ui'
@ -25,10 +25,12 @@
export let labelProps: any | undefined = undefined
export let okAction: () => Promise<void> | void
export let canSave: boolean = false
export let createMore: boolean | undefined = undefined
export let okLabel: IntlString = presentation.string.Create
export let onCancel: Function | undefined = undefined
export let fullSize = false
export let fullSize: boolean = false
export let hideAttachments: boolean = false
export let hideSubheader: boolean = false
export let gap: string | undefined = undefined
const dispatch = createEventDispatcher()
@ -44,7 +46,7 @@
dispatch('changeContent')
}}
>
<div class="antiCard-header">
<div class="antiCard-header" class:withSub={$$slots.subheader && !hideSubheader}>
<div class="antiCard-header__title-wrap">
{#if $$slots.header}
<slot name="header" />
@ -58,12 +60,15 @@
{/if}
</span>
</div>
<div class="buttons-group small-gap">
<div class="buttons-group small-gap content-dark-color">
<Button
id="card-close"
focusIndex={10002}
icon={IconClose}
iconSize={'medium'}
iconProps={{ fill: 'var(--theme-dark-color)' }}
kind={'transparent'}
size={'small'}
on:click={() => {
if (onCancel) {
onCancel()
@ -74,25 +79,35 @@
/>
</div>
</div>
<Scroller horizontal>
<div class="antiCard-content">
<slot />
{#if $$slots.subheader && !hideSubheader}
<div class="antiCard-subheader">
<slot name="subheader" />
</div>
</Scroller>
{/if}
<div class="antiCard-content">
<Scroller padding={$$slots.pool ? '.5rem 1.5rem' : '.5rem 1.5rem 1.5rem'} {gap}>
<slot />
</Scroller>
</div>
{#if $$slots.pool}
<div class="antiCard-pool">
<slot name="pool" />
</div>
{/if}
<div class="antiCard-pool__separator" />
<div class="antiCard-footer reverse">
{#if $$slots.attachments && !hideAttachments}
<div class="antiCard-attachments">
<Scroller horizontal contentDirection={'horizontal'} {gap}>
<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}
{#if createMore !== undefined}
<MiniToggle label={presentation.string.CreateMore} bind:on={createMore} />
{/if}
<Button
loading={okProcessing}
focusIndex={10001}
@ -109,11 +124,9 @@
if (r instanceof Promise) {
r.then(() => {
okProcessing = false
if (!createMore) {
dispatch('close')
}
dispatch('close')
})
} else if (!createMore) {
} else {
okProcessing = false
dispatch('close')
}

View File

@ -36,7 +36,7 @@
</div>
{/if}
</div>
<div class="antiCard-content"><slot /></div>
<div class="antiCard-content px-6"><slot /></div>
<div class="antiCard-footer">
<Button
disabled={!canSave}

View File

@ -18,7 +18,5 @@
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path
d="M11.4,7.7L5.7,2.1C5.5,1.9,5.2,1.9,5,2.1S4.8,2.6,5,2.8L10.3,8L5,13.3c-0.2,0.2-0.2,0.5,0,0.7c0.1,0.1,0.2,0.1,0.4,0.1s0.3,0,0.4-0.1l5.6-5.6c0.1-0.1,0.1-0.2,0.1-0.4S11.4,7.8,11.4,7.7z"
/>
<path d="M11.0001 8L6.00008 3L5.29297 3.70711L9.58586 8L5.29297 12.2929L6.00008 13L11.0001 8Z" />
</svg>

View File

@ -46,7 +46,6 @@ export default plugin(presentationId, {
Remove: '' as IntlString,
Search: '' as IntlString,
Spaces: '' as IntlString,
CreateMore: '' as IntlString,
NumberMembers: '' as IntlString,
NumberSpaces: '' as IntlString,
InThis: '' as IntlString,

View File

@ -130,7 +130,7 @@
max-height: inherit !important;
outline: none;
line-height: 150%;
color: var(--accent-color);
color: var(--theme-caption-color);
p:not(:last-child) {
margin-block-end: 1em;
@ -148,10 +148,13 @@
p.is-editor-empty:first-child::before {
content: attr(data-placeholder);
float: left;
color: var(--dark-color);
color: var(--theme-halfcontent-color);
pointer-events: none;
height: 0;
}
&:focus-within p.is-editor-empty:first-child::before {
color: var(--theme-trans-color);
}
&::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-bar-color);
@ -166,14 +169,6 @@
margin: 0;
}
}
/* Placeholder (at the top) */
.ProseMirror p.is-editor-empty:first-child::before {
color: #adb5bd;
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
.lint-icon {
display: inline-block;
@ -227,7 +222,7 @@
}
.code-block {
border: 1px solid var(--divider-color);
border: 1px solid var(--theme-divider-color);
border-radius: 4px;
padding: 0.5rem;
}

View File

@ -12,7 +12,7 @@
export let showButtons = true
export let buttonSize: IconSize = 'small'
export let focus = false
export let emphasized: boolean = false
export let kind: 'normal' | 'emphasized' | 'indented' = 'normal'
export let isScrollable: boolean = false
export let maxHeight: 'max' | 'card' | 'limited' | string | undefined = undefined
export let required = false
@ -49,8 +49,9 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="antiComponent styled-box clear-mins"
class:antiEmphasized={emphasized}
class="antiComponent styled-box focusable clear-mins"
class:antiEmphasized={kind === 'emphasized'}
class:antiIndented={kind === 'indented'}
on:click={() => {
textEditor?.focus()
}}

View File

@ -22,7 +22,7 @@
export let content: string
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
export let emphasized: boolean = false
export let kind: 'normal' | 'emphasized' | 'indented' = 'normal'
export let alwaysEdit: boolean = false
export let showButtons: boolean = true
export let hideAttachments: boolean = false
@ -133,8 +133,9 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="antiComponent styled-box clear-mins"
class:antiEmphasized={emphasized}
class:antiEmphasized-focus={(mode === Mode.Edit || alwaysEdit) && focused}
class:antiEmphasized={kind === 'emphasized'}
class:antiIndented={kind === 'indented'}
class:focusable={(mode === Mode.Edit || alwaysEdit) && focused}
on:click={() => {
if (alwaysEdit && focused) {
textEditor?.focus()
@ -230,9 +231,7 @@
.label {
padding-bottom: 0.25rem;
font-size: 0.75rem;
color: var(--caption-color);
opacity: 0.3;
color: var(--theme-halfcontent-color);
transition: top 200ms;
pointer-events: none;
user-select: none;

View File

@ -80,6 +80,7 @@
let textEditor: TextEditor
let isEmpty = true
let contentHeight: number
export function submit (): void {
textEditor.submit()
@ -551,7 +552,13 @@
</div>
{/if}
<div class="textInput" class:focusable>
<div class="inputMsg" class:scrollable={isScrollable} style="--texteditor-maxheight: {varsStyle};">
<div
bind:clientHeight={contentHeight}
class="inputMsg"
class:scrollable={isScrollable}
class:showScroll={contentHeight > 32}
style="--texteditor-maxheight: {varsStyle};"
>
{#if isScrollable}
<Scroller>
<TextEditor
@ -617,21 +624,21 @@
flex-grow: 1;
display: flex;
flex-direction: column;
min-height: 4.5rem;
min-height: 1.25rem;
.textInput {
flex-grow: 1;
display: flex;
justify-content: space-between;
align-items: flex-end;
min-height: 2.75rem;
min-height: 1.25rem;
background-color: transparent;
.inputMsg {
align-self: stretch;
width: 100%;
min-height: 0;
color: var(--content-color);
color: var(--theme-caption-color);
background-color: transparent;
:global(.ProseMirror) {
@ -639,6 +646,9 @@
// max-height: 100%;
height: 100%;
}
&:not(.showScroll)::-webkit-scrollbar-thumb {
background-color: transparent;
}
&.scrollable {
overflow: auto;

View File

@ -248,7 +248,7 @@
max-height: inherit !important;
outline: none;
line-height: 150%;
color: var(--accent-color);
color: var(--theme-caption-color);
p:not(:last-child) {
margin-block-end: 1em;
@ -262,10 +262,13 @@
p.is-editor-empty:first-child::before {
content: attr(data-placeholder);
float: left;
color: var(--dark-color);
color: var(--theme-halfcontent-color);
pointer-events: none;
height: 0;
}
&:focus-within p.is-editor-empty:first-child::before {
color: var(--theme-trans-color);
}
&::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-bar-color);

View File

@ -69,13 +69,14 @@
--theme-button-pressed: rgba(255, 255, 255, .08);
--theme-button-focused: rgba(255, 255, 255, .04);
--theme-button-disabled: rgba(255, 255, 255, .02);
--theme-button-border: rgba(255, 255, 255, .06);
--theme-button-border: rgba(255, 255, 255, .09);
--theme-refinput-color: rgba(255, 255, 255, .03);
--theme-refinput-divider: rgba(255, 255, 255, .07);
--theme-refinput-border: rgba(255, 255, 255, .03);
--theme-bg-color: #1A1A28;
--theme-bg-accent-color: rgba(0, 0, 0, .08);
--theme-back-color: #0f0f18;
--theme-overlay-color: rgba(0, 0, 0, .3);
--theme-statusbar-color: #2C2C35;
@ -239,13 +240,14 @@
--theme-button-pressed: rgba(0, 0, 0, .08);
--theme-button-focused: rgba(0, 0, 0, .04);
--theme-button-disabled: rgba(0, 0, 0, .02);
--theme-button-border: rgba(0, 0, 0, .06);
--theme-button-border: rgba(0, 0, 0, .09);
--theme-refinput-color: rgba(0, 0, 0, .03);
--theme-refinput-divider: rgba(0, 0, 0, .07);
--theme-refinput-border: rgba(0, 0, 0, .03);
--theme-bg-color: #F1F1F4;
--theme-bg-accent-color: rgba(255, 255, 255, .08);
--theme-back-color: #D9D9DD;
--theme-overlay-color: rgba(0, 0, 0, .2);
--theme-statusbar-color: #bfbfc6;

View File

@ -59,8 +59,9 @@ input {
font: inherit;
background-color: transparent;
outline: none;
color: var(--caption-color);
&::placeholder { color: var(--dark-color); }
color: var(--theme-caption-color);
&::placeholder { color: var(--theme-halfcontent-color); }
&:focus::placeholder { color: var(--theme-trans-color); }
&.wrong-input { background-color: var(--system-error-color) !important; }
}
audio, canvas, embed, iframe, img, object, svg, video {
@ -77,7 +78,7 @@ textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
-webkit-text-fill-color: var(--caption-color);
-webkit-text-fill-color: var(--theme-caption-color);
transition: background-color 5000s ease-in-out 0s;
background: transparent;
}
@ -278,9 +279,9 @@ input.search {
background-color: var(--avatar-bg-color);
border-radius: 50%;
}
&:not(.small-gap, .medium-gap) { margin-right: .5rem; }
&:not(.small-gap, .large-gap) { margin-right: .375rem; }
&.small-gap { margin-right: .25rem; }
&.medium-gap { margin-right: .375rem; }
&.large-gap { margin-right: .5rem; }
}
.label {
min-width: 0;
@ -407,6 +408,11 @@ input.search {
&:not(.reverse) > *:not(:first-child) { margin-left: .75rem; }
&.reverse > *:not(:last-child) { margin-right: .75rem; }
}
.gap-4 {
&:not(.reverse) > *:not(:first-child) { margin-left: 1rem; }
&.reverse > *:not(:last-child) { margin-right: 1rem; }
}
.gapV-4 > *:not(:last-child) { margin-bottom: 1rem; }
.gap-around-2 > * { margin: .25rem; }
.gap-around-4 > * { margin: .5rem; }
@ -491,6 +497,7 @@ input.search {
.m--1 { margin: -.25rem; }
.m-0-5 { margin: .125rem; }
.m-1 { margin: .25rem; }
.m-3 { margin: .75rem; }
.pl-1 { padding-left: .25rem; }
.pl-2 { padding-left: .5rem; }
@ -613,6 +620,7 @@ input.search {
.w-full { width: 100%; }
.w-2 { width: .5rem; }
.w-4 { width: 1rem; }
.w-6 { width: 1.5rem; }
.w-9 { width: 2.25rem; }
.w-14 { width: 3.5rem; }
.w-16 { width: 4rem; }
@ -641,6 +649,7 @@ input.search {
.max-w-2 { max-width: .5rem; }
.max-w-4 { max-width: 1rem; }
.max-w-9 { max-width: 2.25rem; }
.max-w-20 { max-width: 5rem; }
.max-w-30 { max-width: 7.5rem; }
.max-w-40 { max-width: 10rem; }
.max-w-60 { max-width: 15rem; }
@ -843,9 +852,9 @@ a.no-line {
&:hover { background-color: var(--scrollbar-bar-hover); }
}
.scroll-divider-color::-webkit-scrollbar-thumb {
background-color: var(--divider-color);
background-color: var(--theme-divider-color);
&:horizontal { border-radius: .25rem .25rem 0 0; }
&:hover { background-color: var(--popup-bg-hover); }
&:hover { background-color: var(--theme-popup-hover); }
}
/* Backgrounds & Colors */

View File

@ -2,7 +2,7 @@
display: flex;
align-items: center;
flex-shrink: 0;
padding: 0 0.625rem;
padding: 0 0.75rem;
min-width: 1.375rem;
white-space: nowrap;
color: var(--theme-caption-color);
@ -11,6 +11,12 @@
transition-property: border, background-color, color, box-shadow;
transition-duration: 0.15s;
&.iconL:not(.iconR) {
padding: 0 1rem 0 0.75rem;
}
&.iconR:not(.iconL) {
padding: 0 0.75rem 0 1rem;
}
&.accent {
font-weight: 500;
}
@ -26,10 +32,10 @@
pointer-events: none;
}
&:not(.only-icon) .btn-icon:not(.spinner) {
margin-right: 0.5rem;
margin-right: 0.375rem;
}
&:not(.only-icon) .btn-right-icon {
margin-left: 0.5rem;
margin-left: 0.375rem;
}
&.no-border:not(.only-icon) .btn-icon,
&.link-bordered:not(.only-icon) .btn-icon,
@ -39,7 +45,7 @@
}
&.short {
max-width: 7rem;
max-width: 8.5rem;
}
&.sh-no-shape {
border-radius: 0.25rem;

View File

@ -424,6 +424,17 @@
// Replacing the background of a text editor in Activity
.activity-content .ref-container .textInput { background-color: var(--body-color) !important; }
// Indented
.antiIndented {
margin: .75rem;
border-radius: .25rem;
&:hover,
&.focusable:focus-within {
border-color: var(--theme-divider-color);
}
}
// Emphasized
.antiEmphasized {
padding: .75rem;
@ -435,7 +446,7 @@
transition-timing-function: var(--timing-main);
&:hover,
&:focus-within {
&.focusable:focus-within {
// background-color: var(--body-color);
border-color: var(--theme-list-divider-color);
}

View File

@ -39,8 +39,9 @@
justify-content: space-between;
align-items: center;
flex-shrink: 0;
padding: 1.5rem 1.5rem .5rem 1.75rem;
font-size: .8125rem;
&.withSub { padding: 1.5rem 1.5rem 0; }
&:not(.withSub) { padding: 1.5rem 1.5rem 1rem; }
&__title-wrap {
display: flex;
@ -58,7 +59,7 @@
line-height: 150%;
color: var(--theme-caption-color);
}
&__divider { color: var(--theme-content-color); }
&__divider { color: var(--theme-dark-color); }
&__error {
min-width: 0;
flex-grow: 1;
@ -74,17 +75,24 @@
}
}
.antiCard-subheader {
display: flex;
align-items: center;
flex-shrink: 0;
padding: .5rem 1.5rem 1rem;
min-width: 0;
min-height: 0;
}
.antiCard-content {
display: flex;
flex-direction: column;
flex-grow: 1;
flex-shrink: 1;
padding: 1rem 1.75rem;
// margin: 1.5rem 1.5rem .5rem;
height: fit-content;
min-width: 0;
min-height: 0;
& > *:not(:last-child) { margin-bottom: 1rem; }
}
.antiCard-pool {
@ -92,18 +100,41 @@
display: flex;
align-items: center;
flex-wrap: wrap;
margin: .5rem 1.25rem 0 1.75rem;
margin: .5rem 1.5rem 1.5rem;
min-width: 0;
font-size: .8125rem;
color: var(--theme-caption-color);
&::after {
content: '';
width: 100%;
order: 0;
}
&__separator {
flex-shrink: 0;
margin-top: 1.25rem;
margin-top: 1.5rem;
height: 1px;
background-color: var(--theme-popup-divider);
}
& > * { margin: .5rem .5rem 0 0; }
.new-line {
min-width: 0;
order: 1;
}
}
.antiCard-attachments {
background-color: var(--theme-bg-accent-color);
border-top: 1px solid var(--theme-popup-divider);
&__container {
display: flex;
align-items: center;
margin: 1rem 1.5rem;
& > * { margin-right: 1rem; }
& > *:last-child { margin-right: 1.5rem; }
}
}
.antiCard-footer {
@ -113,10 +144,11 @@
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 1.75rem;
padding: 1rem 1.5rem;
height: 4.25rem;
border-radius: 0 0 .5rem .5rem;
&.divide { border-top: 1px solid var(--theme-popup-divider); }
&.reverse { flex-direction: row-reverse; }
&__error {
flex-grow: 1;

View File

@ -53,9 +53,11 @@
export let accent: boolean = false
// $: iconSize = size === 'inline' ? 'inline' : 'small'
let iconOnly: boolean = false
$: iconOnly =
label === undefined &&
($$slots.content === undefined || $$slots.icon !== undefined || $$slots.iconRight !== undefined)
$$slots.content === undefined &&
(icon !== undefined || iconRight !== undefined || $$slots.icon || $$slots.iconRight)
onMount(() => {
if (focus && input) {
@ -102,6 +104,8 @@
class:highlight
class:selected
class:notSelected
class:iconL={(icon || $$slots.icon) && (label || $$slots.content)}
class:iconR={(iconRight || $$slots.iconRight) && (label || $$slots.content)}
disabled={disabled || loading}
class:short
style:width
@ -149,13 +153,20 @@
width: 1.375rem;
}
}
.small {
.x-small {
height: 1.5rem;
font-size: 0.75rem;
&.only-icon {
width: 1.5rem;
}
}
.small {
height: 1.75rem;
font-size: 0.8125rem;
&.only-icon {
width: 1.75rem;
}
}
.medium {
height: 2rem;
&.only-icon {

View File

@ -34,6 +34,7 @@
export let maxDigitsAfterPoint: number | undefined = undefined
export let kind: EditStyle = 'editbox'
export let focus: boolean = false
export let select: boolean = false
export let focusable: boolean = false
export let disabled: boolean = false
export let fullSize = false
@ -89,6 +90,10 @@
input.focus()
focus = false
}
if (select) {
input.select()
select = false
}
computeSize(input)
})
@ -99,6 +104,9 @@
export function focusInput () {
input?.focus()
}
export function selectInput () {
input?.select()
}
// Focusable control with index
export let focusIndex = -1
@ -218,12 +226,11 @@
input {
padding: 0.25rem 0.5rem;
background-color: var(--accent-bg-color);
border: 1px solid transparent;
background-color: var(--theme-editbox-focus-color);
border-radius: 0.25rem;
&:focus {
border: 1px solid var(--primary-edit-border-color);
box-shadow: 0 0 0 1px var(--theme-editbox-focus-border);
}
}
}
@ -231,12 +238,11 @@
margin: 0 -0.75rem;
padding: 0.625rem 0.75rem;
width: calc(100% + 1.5rem);
border: 1px solid transparent;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out;
&:focus-within {
border-color: var(--primary-edit-border-color);
box-shadow: 0 0 0 1px var(--theme-editbox-focus-border);
}
}
@ -244,7 +250,7 @@
margin: 0;
padding: 0;
min-width: 0;
color: var(--caption-color);
color: var(--theme-caption-color);
border: none;
border-radius: 2px;

View File

@ -37,7 +37,7 @@
</script>
{#if isAsset(icon)}
<svg class="svg-{size}" fill={_fill}>
<svg class="svg-{size}" fill={_fill} {...iconProps}>
<use href={url} />
</svg>
{:else if typeof icon !== 'string'}

View File

@ -475,7 +475,7 @@
divHeight = element.clientHeight
}}
class="scroll relative flex-shrink"
class:overflow-x={horizontal ? 'auto' : 'hidden'}
style:overflow-x={horizontal ? 'auto' : 'hidden'}
on:scroll={(evt) => {
if ($tooltipstore.label !== undefined) closeTooltip()
const newPos = divScroll?.scrollTop ?? 0

View File

@ -23,7 +23,7 @@
export let title: IntlString
export let value: number | null | undefined = null
export let withTime: boolean = false
export let icon: 'normal' | 'warning' | 'overdue' = 'normal'
export let iconModifier: 'normal' | 'warning' | 'overdue' = 'normal'
export let labelNull: IntlString = ui.string.NoDate
const dispatch = createEventDispatcher()
@ -41,6 +41,6 @@
<div class="antiSelect antiWrapper cursor-default">
<div class="flex-col">
<span class="label mb-1"><Label label={title} /></span>
<DatePresenter {value} {mode} {icon} {labelNull} editable on:change={changeValue} />
<DatePresenter {value} {mode} {iconModifier} {labelNull} editable on:change={changeValue} />
</div>
</div>

View File

@ -13,24 +13,26 @@
// limitations under the License.
-->
<script lang="ts">
import type { IntlString } from '@hcengineering/platform'
import type { IntlString, Asset } from '@hcengineering/platform'
import { createEventDispatcher } from 'svelte'
import { DateRangeMode } from '@hcengineering/core'
import ui from '../../plugin'
import { showPopup } from '../../popups'
import { ButtonKind, ButtonSize } from '../../types'
import { ButtonKind, ButtonSize, AnySvelteComponent } from '../../types'
import Icon from '../Icon.svelte'
import Label from '../Label.svelte'
import DatePopup from './DatePopup.svelte'
import DPCalendar from './icons/DPCalendar.svelte'
import { getMonthName } from './internal/DateUtils'
import { ComponentType } from 'svelte'
export let value: number | null | undefined
export let mode: DateRangeMode = DateRangeMode.DATE
export let mondayStart: boolean = true
export let editable: boolean = false
export let icon: 'normal' | 'warning' | 'critical' | 'overdue' = 'normal'
export let icon: Asset | AnySvelteComponent | ComponentType | undefined = undefined
export let iconModifier: 'normal' | 'warning' | 'critical' | 'overdue' = 'normal'
export let labelNull: IntlString = ui.string.NoDate
export let showIcon = true
export let shouldShowLabel: boolean = true
@ -68,6 +70,7 @@
class:editable
class:dateTimeButtonNoLabel={!shouldShowLabel}
class:text-xs={size === 'x-small'}
class:noDate={!value}
style:width
on:click={(e) => {
if (editable && !opened) {
@ -89,8 +92,8 @@
}}
>
{#if showIcon}
<div class="btn-icon {icon}" class:buttonIconNoLabel={!shouldShowLabel}>
<Icon icon={DPCalendar} size="full" />
<div class="btn-icon {iconModifier}" class:buttonIconNoLabel={!shouldShowLabel}>
<Icon icon={icon ?? DPCalendar} size="full" />
</div>
{/if}
{#if value !== null && value !== undefined}
@ -185,7 +188,7 @@
transition-duration: 0;
.not-selected {
color: var(--theme-caption-color);
color: var(--theme-content-color);
}
}
&.editable {
@ -324,6 +327,13 @@
}
}
&.noDate .btn-icon {
color: var(--theme-dark-color);
}
&.noDate:hover .btn-icon {
color: var(--theme-content-color);
}
.time-divider {
flex-shrink: 0;
margin: 0 0.25rem;

View File

@ -23,7 +23,7 @@
export let title: IntlString
export let value: number | null | undefined = null
export let withTime: boolean = false
export let icon: 'normal' | 'warning' | 'overdue' = 'normal'
export let iconModifier: 'normal' | 'warning' | 'overdue' = 'normal'
export let labelNull: IntlString = ui.string.NoDate
const dispatch = createEventDispatcher()
@ -39,6 +39,13 @@
<div class="antiSelect antiWrapper cursor-default">
<div class="flex-col">
<span class="label mb-1"><Label label={title} /></span>
<DateRangePresenter {value} mode={DateRangeMode.DATETIME} {icon} {labelNull} editable on:change={changeValue} />
<DateRangePresenter
{value}
mode={DateRangeMode.DATETIME}
{iconModifier}
{labelNull}
editable
on:change={changeValue}
/>
</div>
</div>

View File

@ -28,7 +28,7 @@
export let value: number | null | undefined = null
export let mode: DateRangeMode = DateRangeMode.DATE
export let editable: boolean = false
export let icon: 'normal' | 'warning' | 'overdue' = 'normal'
export let iconModifier: 'normal' | 'warning' | 'overdue' = 'normal'
export let labelNull: IntlString = ui.string.NoDate
export let kind: 'default' | 'no-border' | 'link' | 'secondary' = 'default'
export let size: 'small' | 'medium' | 'large' = 'small'
@ -392,7 +392,7 @@
</div>
{/if}
{:else}
<div class="btn-icon {icon}">
<div class="btn-icon {iconModifier}">
<Icon icon={DPCalendar} size={'full'} />
</div>
{#if value !== undefined && value !== null && value.toString() !== ''}

View File

@ -20,9 +20,9 @@
export let value: number | null | undefined
export let editable: boolean = false
export let icon: 'normal' | 'warning' | 'overdue' = 'normal'
export let iconModifier: 'normal' | 'warning' | 'overdue' = 'normal'
export let labelNull: IntlString = ui.string.NoDate
export let noShift: boolean = false
</script>
<DateRangePresenter bind:value mode={DateRangeMode.DATETIME} {editable} {icon} {labelNull} {noShift} />
<DateRangePresenter bind:value mode={DateRangeMode.DATETIME} {editable} {iconModifier} {labelNull} {noShift} />

View File

@ -93,6 +93,6 @@
}
: undefined}
>
<DatePresenter {value} {editable} icon={iconModifier} {kind} {size} {width} on:change={handleDueDateChanged} />
<DatePresenter {value} {editable} {iconModifier} {kind} {size} {width} on:change={handleDueDateChanged} />
</div>
{/if}

View File

@ -15,11 +15,13 @@
-->
<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">
<svg class="svg-{size}" {fill} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path
d="M3,8.5L8.1,3c1.1-1.2,2.9-1.2,4,0c1.1,1.2,1.1,3,0,4.2l-5.9,6.3c-0.4,0.5-1.1,0.5-1.6,0c-0.4-0.5-0.4-1.2,0-1.7l5.9-6.3c0.2-0.2,0.2-0.6,0-0.8c-0.2-0.2-0.6-0.2-0.8,0L3.7,11c-0.9,0.9-0.9,2.4,0,3.3c0.9,0.9,2.3,0.9,3.2,0l5.9-6.3c1.5-1.6,1.5-4.2,0-5.8c-1.5-1.6-4-1.6-5.5,0L2.2,7.6c-0.2,0.2-0.2,0.6,0,0.8C2.4,8.7,2.7,8.7,3,8.5z"
fill-rule="evenodd"
clip-rule="evenodd"
d="M26.682 5.31802C24.9246 3.56066 22.0754 3.56066 20.318 5.31802L5.31802 20.318C3.56066 22.0754 3.56066 24.9246 5.31802 26.682C7.07538 28.4393 9.92462 28.4393 11.682 26.682L11.6875 26.6765L14.1575 24.282C14.5541 23.8976 15.1871 23.9074 15.5716 24.304C15.956 24.7005 15.9461 25.3336 15.5496 25.718L13.0905 28.1019C10.5516 30.6346 6.44031 30.6327 3.90381 28.0962C1.3654 25.5578 1.3654 21.4422 3.90381 18.9038L18.9038 3.90381C21.4422 1.3654 25.5578 1.3654 28.0962 3.90381C30.6346 6.44221 30.6346 10.5578 28.0962 13.0962L20.5104 20.682C18.753 22.4393 15.9038 22.4393 14.1464 20.682C12.3891 18.9246 12.3891 16.0754 14.1464 14.318L17.7929 10.6716C18.1834 10.281 18.8166 10.281 19.2071 10.6716C19.5976 11.0621 19.5976 11.6953 19.2071 12.0858L15.5607 15.7322C14.5843 16.7085 14.5843 18.2915 15.5607 19.2678C16.537 20.2441 18.1199 20.2441 19.0962 19.2678L26.682 11.682C28.4393 9.92462 28.4393 7.07538 26.682 5.31802Z"
/>
</svg>

View File

@ -3,8 +3,8 @@
export let fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="-7 -7 38 38" xmlns="http://www.w3.org/2000/svg">
<svg class="svg-{size}" {fill} viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path
d="M0.439127 21.44C0.157865 21.7214 -9.37008e-05 22.103 7.33302e-08 22.5008C9.38474e-05 22.8987 0.158232 23.2802 0.439627 23.5615C0.721022 23.8427 1.10262 24.0007 1.50048 24.0006C1.89834 24.0005 2.27987 23.8424 2.56113 23.561L11.8231 14.3C11.8463 14.2767 11.8739 14.2582 11.9043 14.2456C11.9347 14.233 11.9672 14.2265 12.0001 14.2265C12.033 14.2265 12.0656 14.233 12.0959 14.2456C12.1263 14.2582 12.1539 14.2767 12.1771 14.3L21.4391 23.563C21.5784 23.7023 21.7437 23.8128 21.9257 23.8883C22.1077 23.9637 22.3028 24.0025 22.4998 24.0026C22.6968 24.0026 22.8919 23.9639 23.0739 23.8885C23.2559 23.8132 23.4213 23.7027 23.5606 23.5635C23.7 23.4242 23.8105 23.2589 23.8859 23.0769C23.9614 22.8949 24.0002 22.6998 24.0003 22.5028C24.0003 22.3058 23.9615 22.1107 23.8862 21.9287C23.8109 21.7467 23.7004 21.5813 23.5611 21.442L14.3001 12.177C14.2768 12.1537 14.2584 12.1262 14.2458 12.0958C14.2332 12.0654 14.2267 12.0329 14.2267 12C14.2267 11.9671 14.2332 11.9345 14.2458 11.9042C14.2584 11.8738 14.2768 11.8462 14.3001 11.823L23.5631 2.56097C23.8444 2.27931 24.0022 1.89745 24.002 1.49941C24.0017 1.10136 23.8433 0.71973 23.5616 0.438468C23.28 0.157206 22.8981 -0.000647135 22.5001 -0.000365836C22.102 -8.45362e-05 21.7204 0.158308 21.4391 0.439968L12.1771 9.69997C12.1539 9.72325 12.1263 9.74172 12.0959 9.75432C12.0656 9.76693 12.033 9.77342 12.0001 9.77342C11.9672 9.77342 11.9347 9.76693 11.9043 9.75432C11.8739 9.74172 11.8463 9.72325 11.8231 9.69997L2.56113 0.439968C2.42186 0.300636 2.25651 0.190098 2.07453 0.114667C1.89254 0.0392356 1.69748 0.000387673 1.50048 0.000341244C1.10262 0.000247476 0.721022 0.158206 0.439627 0.439468C0.158232 0.72073 9.38099e-05 1.10226 4.17235e-08 1.50011C-9.37265e-05 1.89797 0.157865 2.27957 0.439127 2.56097L9.70013 11.823C9.72341 11.8462 9.74188 11.8738 9.75448 11.9042C9.76709 11.9345 9.77357 11.9671 9.77357 12C9.77357 12.0329 9.76709 12.0654 9.75448 12.0958C9.74188 12.1262 9.72341 12.1537 9.70013 12.177L0.439127 21.44Z"
d="M5.18306 5.18306C4.93898 5.42714 4.93898 5.82286 5.18306 6.06694L9.11612 10L5.18306 13.9331C4.93898 14.1771 4.93898 14.5729 5.18306 14.8169C5.42714 15.061 5.82286 15.061 6.06694 14.8169L10 10.8839L13.9331 14.8169C14.1771 15.061 14.5729 15.061 14.8169 14.8169C15.061 14.5729 15.061 14.1771 14.8169 13.9331L10.8839 10L14.8169 6.06694C15.061 5.82286 15.061 5.42714 14.8169 5.18306C14.5729 4.93898 14.1771 4.93898 13.9331 5.18306L10 9.11612L6.06694 5.18306C5.82286 4.93898 5.42714 4.93898 5.18306 5.18306Z"
/>
</svg>

View File

@ -125,7 +125,7 @@ export type ButtonKind =
| 'dangerous'
| 'list'
| 'list-header'
export type ButtonSize = 'inline' | 'small' | 'medium' | 'large' | 'x-large'
export type ButtonSize = 'inline' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large'
export type ButtonShape = 'rectangle' | 'rectangle-left' | 'rectangle-right' | 'circle' | 'round' | 'filter' | undefined
export type EditStyle = 'editbox' | 'large-style' | 'small-style' | 'search-style' | 'underline'
export interface PopupPositionElement {

View File

@ -81,6 +81,7 @@
>
<div
class="flex-center icon"
class:svg={value.type === 'image/svg+xml'}
class:image={isImage(value.type)}
style:background-image={isImage(value.type) ? `url(${getFileUrl(value.file)})` : 'none'}
>
@ -106,7 +107,7 @@
on:click={(ev) => {
ev.stopPropagation()
ev.preventDefault()
dispatch('remove')
dispatch('remove', value)
}}
>
<Label label={presentation.string.Delete} />
@ -118,33 +119,43 @@
<style lang="scss">
.attachment-container {
padding: 0.375rem 0.75rem 0.375rem 0.375rem;
flex-shrink: 0;
width: auto;
height: 3rem;
min-width: 14rem;
max-width: 19rem;
background-color: var(--theme-button-enabled);
border: 1px solid var(--theme-button-border);
background-color: var(--theme-button-hovered);
border-radius: 0.25rem;
.icon {
flex-shrink: 0;
margin-right: 0.75rem;
width: 3.25rem;
height: 3.25rem;
border: 1px solid var(--primary-button-border);
border-radius: 0.25rem;
width: 3rem;
height: 3rem;
border: 1px solid var(--theme-button-border);
border-radius: 0.25rem 0 0 0.25rem;
cursor: pointer;
&:not(.image) {
color: var(--primary-button-color);
background-color: var(--primary-button-enabled);
}
&.svg {
background-color: #fff;
}
&.image {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
}
.info-container {
padding: 0.5rem 0.75rem;
width: 100%;
height: 100%;
border: 1px solid var(--theme-button-border);
border-left: none;
border-radius: 0 0.25rem 0.25rem 0;
}
.name {
white-space: nowrap;
font-size: 0.8125rem;
@ -152,7 +163,6 @@
cursor: pointer;
}
.info-content {
margin-top: 0.125rem;
white-space: nowrap;
font-size: 0.6875rem;
color: var(--theme-darker-color);

View File

@ -32,7 +32,7 @@
export let placeholder: IntlString | undefined = undefined
export let alwaysEdit = false
export let showButtons = false
export let emphasized: boolean = false
export let kind: 'normal' | 'emphasized' | 'indented' = 'normal'
export let buttonSize: IconSize = 'medium'
export let formatButtonSize: IconSize = 'small'
export let maxHeight: 'max' | 'card' | 'limited' | string = 'max'
@ -302,6 +302,11 @@
export function isEmptyContent (): boolean {
return refInput.isEmptyContent()
}
$: dispatch('attachments', {
size: attachments.size,
values: attachments.size === 0 ? true : attachments
})
</script>
<input
@ -337,7 +342,7 @@
{formatButtonSize}
{maxHeight}
{focusable}
{emphasized}
{kind}
{enableBackReferences}
on:changeSize
on:changeContent

View File

@ -36,7 +36,7 @@
<Icon icon={board.icon.Card} {size} />
&nbsp;{done}/{total}
{#if item.dueTo !== null}
&nbsp;<DatePresenter value={item.dueTo} size="x-small" icon={getDateIcon(item)} kind="transparent" />
&nbsp;<DatePresenter value={item.dueTo} size="x-small" iconModifier={getDateIcon(item)} kind="transparent" />
{/if}
</div>
{/if}

View File

@ -11,6 +11,7 @@
{#if value}
<div class="flex-presenter flex-gap-1 h-full">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="flex-center h-full" on:click>
<div class="flex-row-center background-button-bg-color pr-1 pl-1 border-radius-1 w-full">
{#if value.startDate}
@ -21,7 +22,7 @@
<DatePresenter
bind:value={value.dueDate}
mode={DateRangeMode.DATETIME}
icon={isOverdue ? 'overdue' : undefined}
iconModifier={isOverdue ? 'overdue' : undefined}
{size}
kind="transparent"
/>

View File

@ -80,7 +80,7 @@
</div>
<div class="mb-2">
<StyledTextBox
emphasized
kind={'emphasized'}
content={object.description}
on:value={(evt) => {
client.update(object, { description: evt.detail })

View File

@ -64,6 +64,7 @@
export let showTooltip: LabelAndProps | undefined = undefined
export let showNavigate = true
export let id: string | undefined = undefined
export let short: boolean = false
const icon = IconPerson
@ -147,7 +148,7 @@
>
{#if selected}
{#if hideIcon || selected}
<UserInfo value={selected} size={avatarSize} {icon} on:accent-color />
<UserInfo value={selected} size={avatarSize} {icon} {short} on:accent-color />
{:else}
{getName(selected)}
{/if}

View File

@ -23,12 +23,13 @@
export let subtitle: string | undefined = undefined
export let size: IconSize
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let short: boolean = false
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="flex-row-center" on:click>
<Avatar avatar={value.avatar} {size} {icon} on:accent-color />
<div class="flex-col min-w-0 {size === 'tiny' || size === 'inline' ? 'ml-1' : 'ml-2'}">
<div class="flex-col min-w-0 {size === 'tiny' || size === 'inline' ? 'ml-1' : 'ml-2'}" class:max-w-20={short}>
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
<div class="label overflow-label text-left">{getName(value)}</div>
</div>

View File

@ -106,6 +106,7 @@
labelProps={{ type: typeLabel }}
okAction={saveRequest}
canSave={value !== undefined && !notLimit}
gap={'gapV-4'}
on:close={() => {
dispatch('close')
}}

View File

@ -35,7 +35,6 @@
let firstName = ''
let lastName = ''
let createMore: boolean = false
export function canClose (): boolean {
return firstName === '' && lastName === ''
@ -43,7 +42,7 @@
let avatarEditor: EditableAvatar
let object: Customer = {
const object: Customer = {
_class: contact.class.Person
} as Customer
@ -92,18 +91,6 @@
}
)
}
if (createMore) {
// Prepare for next
object = {
_class: targetClass._id
} as Customer
customerId = generateId()
avatar = undefined
firstName = ''
lastName = ''
channels = []
}
}
const targets = [
@ -163,7 +150,6 @@
on:close={() => {
dispatch('close')
}}
bind:createMore
on:changeContent
>
<svelte:fragment slot="header">

View File

@ -71,13 +71,12 @@
export let preserveVacancy = false
let status: Status = OK
let createMore: boolean = false
let _space = space
$: _candidate = candidate
let doc: Applicant = {
const doc: Applicant = {
state: '' as Ref<State>,
doneState: null,
number: 0,
@ -160,30 +159,6 @@
message: _comment
})
}
if (createMore) {
// Prepare for next
_candidate = '' as Ref<Candidate>
_comment = ''
doc = {
state: selectedState?._id as Ref<State>,
doneState: null,
number: 0,
assignee,
rank: '',
attachedTo: _candidate,
attachedToClass: recruit.mixin.Candidate,
_class: recruit.class.Applicant,
space: _space,
_id: generateId(),
collection: 'applications',
modifiedOn: Date.now(),
modifiedBy: '' as Ref<Account>,
startDate: null,
dueDate: null
}
fillDefaults(hierarchy, doc, recruit.class.Applicant)
}
}
async function invokeValidate (
@ -295,7 +270,7 @@
label={recruit.string.CreateApplication}
okAction={createApplication}
canSave={status.severity === Severity.OK}
bind:createMore
gap={'gapV-4'}
on:close={() => {
dispatch('close')
}}
@ -367,7 +342,7 @@
space={_space}
alwaysEdit
showButtons={false}
emphasized
kind={'emphasized'}
bind:content={_comment}
placeholder={recruit.string.Description}
on:changeSize={() => dispatch('changeContent')}

View File

@ -111,7 +111,6 @@
type: string
lastModified: number
}
let createMore: boolean = false
export function canClose (): boolean {
return true
@ -271,9 +270,7 @@
await applyOps.commit()
draftController.remove()
if (!createMore) {
dispatch('close', object._id)
}
dispatch('close', object._id)
resetObject()
}
@ -516,7 +513,6 @@
dispatch('close')
}}
onCancel={showConfirmationDialog}
bind:createMore
on:changeContent
>
<svelte:fragment slot="header">

View File

@ -236,6 +236,7 @@
label={recruit.string.CreateVacancy}
okAction={createVacancy}
canSave={!!name}
gap={'gapV-4'}
on:close={() => {
dispatch('close')
}}
@ -266,7 +267,7 @@
maxHeight={'card'}
bind:content={fullDescription}
placeholder={recruit.string.FullDescription}
emphasized
kind={'emphasized'}
/>
{/key}

View File

@ -54,7 +54,7 @@
<div class="mt-3">
{#key template._id}
<StyledTextBox
emphasized
kind={'emphasized'}
alwaysEdit
showButtons={false}
content={template.description ?? ''}

View File

@ -83,6 +83,7 @@
label={recruit.string.CreateOpinion}
okAction={createOpinion}
canSave={(doc.value ?? '').trim().length > 0}
gap={'gapV-4'}
on:close={() => {
dispatch('close')
}}
@ -95,5 +96,5 @@
placeholder={recruit.string.OpinionValuePlaceholder}
focus
/>
<StyledTextArea placeholder={recruit.string.Description} bind:content={doc.description} emphasized />
<StyledTextArea placeholder={recruit.string.Description} bind:content={doc.description} kind={'emphasized'} />
</Card>

View File

@ -150,6 +150,7 @@
labelProps={{ label: '' }}
okAction={createReview}
canSave={status.severity === Severity.OK && title.trim().length > 0 && doc.attachedTo !== undefined}
gap={'gapV-4'}
on:close={() => {
dispatch('close')
}}
@ -158,7 +159,7 @@
<StatusControl slot="error" {status} />
<EditBox placeholder={recruit.string.Title} bind:value={title} kind={'large-style'} focus />
<EditBox placeholder={recruit.string.Location} bind:value={location} kind={'small-style'} />
<StyledTextArea bind:content={description} placeholder={recruit.string.AddDescription} emphasized />
<StyledTextArea bind:content={description} placeholder={recruit.string.AddDescription} kind={'emphasized'} />
<svelte:fragment slot="pool">
{#if !preserveCandidate}
<UserBox

View File

@ -62,6 +62,7 @@
label={recruit.string.Opinion}
okAction={editOpinion}
canSave={value.length > 0}
gap={'gapV-4'}
on:close={() => {
dispatch('close')
}}
@ -75,5 +76,5 @@
placeholder={recruit.string.OpinionValue}
focus
/>
<StyledTextArea placeholder={recruit.string.Description} bind:content={description} emphasized />
<StyledTextArea placeholder={recruit.string.Description} bind:content={description} kind={'emphasized'} />
</Card>

View File

@ -104,7 +104,6 @@
labelProps={{ word: keyTitle }}
okAction={createTagElenent}
canSave={title.length > 0}
createMore={false}
on:close={() => {
dispatch('close')
}}

View File

@ -174,10 +174,10 @@
<symbol id="milestone" viewBox="0 0 16 16">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 1.49988C8 1.22374 7.77614 0.999878 7.5 0.999878C7.22386 0.999878 7 1.22374 7 1.49988V2.99988H3C2.44772 2.99988 2 3.44759 2 3.99988V7.99988C2 8.55216 2.44771 8.99988 3 8.99988H7V14.4999C7 14.776 7.22386 14.9999 7.5 14.9999C7.77614 14.9999 8 14.776 8 14.4999V8.99988H11.3787C11.9091 8.99988 12.4178 8.78917 12.7929 8.41409L14.8536 6.35343C14.9473 6.25966 15 6.13249 15 5.99988C15 5.86727 14.9473 5.74009 14.8536 5.64632L12.7929 3.58566C12.4178 3.21059 11.9091 2.99988 11.3787 2.99988H8V1.49988ZM11.3787 7.99988C11.6439 7.99988 11.8982 7.89452 12.0858 7.70698L13.7929 5.99988L12.0858 4.29277C11.8983 4.10523 11.6439 3.99988 11.3787 3.99988H3V7.99988H11.3787Z" />
</symbol>
<symbol id="issuetemplates" viewBox="0 0 16 16">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 5L8 9L15 5L8 1L1 5ZM3.01556 5L8 7.84825L12.9844 5L8 2.15175L3.01556 5Z"/>
<path d="M1 11L8 15L15 11L13.9426 10.3958L8 13.8482L2.05741 10.3958L1 11Z"/>
<path d="M8 11.9992L1 7.99922L2.05741 7.39499L8 10.8475L13.9426 7.39499L15 7.99922L8 11.9992Z"/>
<symbol id="issuetemplates" viewBox="0 0 32 32">
<path d="M26 6V10H6V6H26ZM26 4H6C5.46957 4 4.96086 4.21071 4.58579 4.58579C4.21071 4.96086 4 5.46957 4 6V10C4 10.5304 4.21071 11.0391 4.58579 11.4142C4.96086 11.7893 5.46957 12 6 12H26C26.5304 12 27.0391 11.7893 27.4142 11.4142C27.7893 11.0391 28 10.5304 28 10V6C28 5.46957 27.7893 4.96086 27.4142 4.58579C27.0391 4.21071 26.5304 4 26 4Z" />
<path d="M10 16V26H6V16H10ZM10 14H6C5.46957 14 4.96086 14.2107 4.58579 14.5858C4.21071 14.9609 4 15.4696 4 16V26C4 26.5304 4.21071 27.0391 4.58579 27.4142C4.96086 27.7893 5.46957 28 6 28H10C10.5304 28 11.0391 27.7893 11.4142 27.4142C11.7893 27.0391 12 26.5304 12 26V16C12 15.4696 11.7893 14.9609 11.4142 14.5858C11.0391 14.2107 10.5304 14 10 14Z" />
<path d="M26 16V26H16V16H26ZM26 14H16C15.4696 14 14.9609 14.2107 14.5858 14.5858C14.2107 14.9609 14 15.4696 14 16V26C14 26.5304 14.2107 27.0391 14.5858 27.4142C14.9609 27.7893 15.4696 28 16 28H26C26.5304 28 27.0391 27.7893 27.4142 27.4142C27.7893 27.0391 28 26.5304 28 26V16C28 15.4696 27.7893 14.9609 27.4142 14.5858C27.0391 14.2107 26.5304 14 26 14Z" />
</symbol>
<symbol id="timeReport" viewBox="0 0 16 16">
<path d="M8 1.99988C11.3 1.99988 14 4.69988 14 7.99988C14 11.2999 11.3 13.9999 8 13.9999C4.7 13.9999 2 11.2999 2 7.99988C2 4.69988 4.7 1.99988 8 1.99988ZM8 0.999878C4.15 0.999878 1 4.14988 1 7.99988C1 11.8499 4.15 14.9999 8 14.9999C11.85 14.9999 15 11.8499 15 7.99988C15 4.14988 11.85 0.999878 8 0.999878Z" />

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -78,7 +78,7 @@
"AssignTo": "Assign to...",
"AssignedTo": "Assigned to {value}",
"Parent": "Parent issue",
"SetParent": "Set parent issue\u2026",
"SetParent": "Set parent issue",
"ChangeParent": "Change parent issue\u2026",
"RemoveParent": "Remove parent issue",
"OpenParent": "Open parent issue",
@ -93,6 +93,7 @@
"Labels": "Labels",
"Component": "Component",
"Space": "",
"NoDueDate": "No due date",
"SetDueDate": "Set due date\u2026",
"ChangeDueDate": "Change due date\u2026",
"ModificationDate": "Updated {value}",

View File

@ -78,7 +78,7 @@
"AssignTo": "Назначить...",
"AssignedTo": "Назначен на {value}",
"Parent": "Родительская задача",
"SetParent": "Задать родительскую задачу\u2026",
"SetParent": "Задать родительскую задачу",
"ChangeParent": "Изменить родительскую задачу\u2026",
"RemoveParent": "Удалить родительскую задачу",
"OpenParent": "Открыть родительскую задачу",
@ -93,6 +93,7 @@
"Labels": "Метки",
"Component": "Компонент",
"Space": "",
"NoDueDate": "Нет срока выполнения",
"SetDueDate": "Указать срок выполнения\u2026",
"ChangeDueDate": "Изменить срок выполнения\u2026",
"ModificationDate": "Изменено {value}",

View File

@ -41,7 +41,6 @@
Project
} from '@hcengineering/tracker'
import {
ActionIcon,
addNotification,
Button,
Component,
@ -50,11 +49,11 @@
EditBox,
FocusHandler,
IconAttachment,
IconMoreH,
Label,
Menu,
showPopup
} from '@hcengineering/ui'
import { Attachment } from '@hcengineering/attachment'
import { AttachmentPresenter } from '@hcengineering/attachment-resources'
import view from '@hcengineering/view'
import { ObjectBox } from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy } from 'svelte'
@ -68,7 +67,6 @@
import StatusEditor from './issues/StatusEditor.svelte'
import EstimationEditor from './issues/timereport/EstimationEditor.svelte'
import MilestoneSelector from './milestones/MilestoneSelector.svelte'
import SetDueDateActionPopup from './SetDueDateActionPopup.svelte'
import SetParentIssueActionPopup from './SetParentIssueActionPopup.svelte'
import SubIssues from './SubIssues.svelte'
import { createBacklinks } from '@hcengineering/chunter-resources'
@ -409,51 +407,17 @@
descriptionBox?.removeDraft(false)
}
async function showMoreActions (ev: Event) {
ev.preventDefault()
const selectDueDate = {
label: object.dueDate === null ? tracker.string.SetDueDate : tracker.string.ChangeDueDate,
icon: tracker.icon.DueDate,
action: async () =>
showPopup(
SetDueDateActionPopup,
{ value: object },
'top',
undefined,
(newDueDate) => newDueDate !== undefined && (object.dueDate = newDueDate)
)
}
const setParentIssue = {
label: parentIssue ? tracker.string.ChangeParent : tracker.string.SetParent,
icon: tracker.icon.Parent,
action: async () =>
showPopup(
SetParentIssueActionPopup,
{ value: { ...object, space: _space, attachedTo: parentIssue?._id } },
'top',
(selectedIssue) => {
if (selectedIssue !== undefined) {
parentIssue = selectedIssue
object.parentIssue = parentIssue?._id
}
}
)
}
const removeParentIssue = parentIssue && {
label: tracker.string.RemoveParent,
icon: tracker.icon.Parent,
action: clearParentIssue
}
async function setParentIssue () {
showPopup(
Menu,
{
actions: [selectDueDate, setParentIssue, ...(removeParentIssue ? [removeParentIssue] : [])]
},
ev.target as HTMLElement
SetParentIssueActionPopup,
{ value: { ...object, space: _space, attachedTo: parentIssue?._id } },
'top',
(selectedIssue) => {
if (selectedIssue !== undefined) {
parentIssue = selectedIssue
object.parentIssue = parentIssue?._id
}
}
)
}
@ -530,6 +494,8 @@
$: objectId = object._id
const manager = createFocusManager()
let attachments: Map<Ref<Attachment>, Attachment> = new Map<Ref<Attachment>, Attachment>()
</script>
<FocusHandler {manager} />
@ -540,8 +506,9 @@
{canSave}
okLabel={tracker.string.SaveIssue}
on:close={() => dispatch('close')}
createMore={false}
onCancel={showConfirmationDialog}
hideAttachments={attachments.size === 0}
hideSubheader={!parentIssue}
on:changeContent
>
<svelte:fragment slot="header">
@ -550,7 +517,7 @@
label={tracker.string.Project}
bind:space={_space}
kind={'secondary'}
size={'large'}
size={'small'}
/>
<ObjectBox
_class={tracker.class.IssueTemplate}
@ -560,7 +527,7 @@
}}
on:change={handleTemplateChange}
kind={'secondary'}
size={'large'}
size={'small'}
label={tracker.string.NoIssueTemplate}
icon={tracker.icon.IssueTemplates}
searchField={'title'}
@ -576,20 +543,29 @@
<Label {label} />
</div>
{#if relatedTo}
<div class="mr-2">
<div class="lower mr-2">
<Label label={tracker.string.RelatedTo} />
</div>
<Component
is={view.component.ObjectPresenter}
props={{ value: relatedTo, _class: relatedTo._class, objectId: relatedTo._id, inline: true }}
props={{
value: relatedTo,
_class: relatedTo._class,
objectId: relatedTo._id,
inline: true,
shouldShowAvatar: false,
noUnderline: true
}}
/>
{/if}
</div>
</svelte:fragment>
{#if parentIssue}
<ParentIssue issue={parentIssue} on:close={clearParentIssue} />
{/if}
<div id="issue-name">
<svelte:fragment slot="subheader">
{#if parentIssue}
<ParentIssue issue={parentIssue} on:close={clearParentIssue} />
{/if}
</svelte:fragment>
<div id="issue-name" class="m-3 clear-mins">
<EditBox
focusIndex={1}
bind:value={object.title}
@ -610,7 +586,8 @@
space={_space}
alwaysEdit
showButtons={false}
emphasized
kind={'indented'}
fakeAttach={'hidden'}
enableBackReferences={true}
bind:content={object.description}
placeholder={tracker.string.IssueDescriptionPlaceholder}
@ -620,6 +597,13 @@
object.attachments = ev.detail.value
}
}}
on:attachments={(ev) => {
if (ev.detail.size > 0) attachments = ev.detail.values
else if (ev.detail.size === 0 && ev.detail.values) {
attachments.clear()
attachments = attachments
}
}}
/>
{/key}
</div>
@ -640,6 +624,7 @@
size={'large'}
defaultIssueStatus={currentProject?.defaultIssueStatus}
shouldShowLabel={true}
short
on:refocus={() => {
manager.setFocusPos(3)
}}
@ -672,6 +657,7 @@
kind={'secondary'}
size={'large'}
width={'min-content'}
short
on:change={({ detail }) => {
object.assignee = detail
manager.setFocusPos(5)
@ -696,9 +682,6 @@
object.labels = object.labels.filter((it) => it._id !== evt.detail)
}}
/>
<div id="estimation-editor">
<EstimationEditor focusIndex={7} kind={'secondary'} size={'large'} value={object} />
</div>
<ComponentSelector
focusIndex={8}
value={object.component}
@ -706,25 +689,63 @@
isEditable={true}
kind={'secondary'}
size={'large'}
short
/>
<MilestoneSelector
focusIndex={9}
value={object.milestone}
onChange={handleMilestoneIdChanged}
useComponent={(!originalIssue && object.component) || undefined}
kind={'secondary'}
size={'large'}
/>
{#if object.dueDate !== null}
<DatePresenter bind:value={object.dueDate} kind={'secondary'} size={'large'} editable />
<div id="estimation-editor" class="new-line">
<EstimationEditor focusIndex={7} kind={'secondary'} size={'large'} value={object} />
</div>
<div id="milestone-editor" class="new-line">
<MilestoneSelector
focusIndex={9}
value={object.milestone}
onChange={handleMilestoneIdChanged}
useComponent={(!originalIssue && object.component) || undefined}
kind={'secondary'}
size={'large'}
short
/>
</div>
<div id="duedate-editor" class="new-line">
<DatePresenter
bind:value={object.dueDate}
labelNull={tracker.string.NoDueDate}
icon={tracker.icon.DueDate}
kind={'secondary'}
size={'large'}
editable
/>
</div>
<div id="parentissue-editor" class="new-line">
<Button
icon={tracker.icon.Parent}
label={object.parentIssue ? tracker.string.RemoveParent : tracker.string.SetParent}
kind={'secondary'}
size={'large'}
notSelected={object.parentIssue === undefined}
on:click={object.parentIssue ? clearParentIssue : setParentIssue}
/>
</div>
</svelte:fragment>
<svelte:fragment slot="attachments">
{#if attachments.size > 0}
{#each Array.from(attachments.values()) as attachment}
<AttachmentPresenter
value={attachment}
removable
on:remove={(result) => {
if (result.detail !== undefined) descriptionBox.removeAttachmentById(result.detail._id)
}}
/>
{/each}
{/if}
<div id="more-actions"><ActionIcon icon={IconMoreH} size={'medium'} action={showMoreActions} /></div>
</svelte:fragment>
<svelte:fragment slot="footer">
<Button
focusIndex={10}
icon={IconAttachment}
iconProps={{ fill: 'var(--theme-dark-color)' }}
size={'large'}
kind={'transparent'}
on:click={() => {
descriptionBox.attach()
}}

View File

@ -45,6 +45,7 @@
okAction={onSave}
canSave={object.label !== ''}
okLabel={tracker.string.CreateComponent}
gap={'gapV-4'}
on:close={() => dispatch('close')}
on:changeContent
>
@ -61,7 +62,7 @@
<StyledTextArea
bind:content={object.description}
placeholder={tracker.string.ComponentDescriptionPlaceholder}
emphasized
kind={'emphasized'}
showButtons={false}
/>
<svelte:fragment slot="pool">

View File

@ -33,6 +33,7 @@
{fill}
id={category._id}
style:transform={category._id === tracker.issueStatusCategory.Started ? 'rotate(-90deg)' : ''}
style:flex-shrink={0}
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>

View File

@ -30,6 +30,7 @@
export let tooltipAlignment: TooltipAlignment | undefined = undefined
export let width: string = '100%'
export let focusIndex: number | undefined = undefined
export let short: boolean = false
const client = getClient()
const dispatch = createEventDispatcher()
@ -99,6 +100,7 @@
{kind}
{avatarSize}
{width}
{short}
showNavigate={false}
justify={'left'}
showTooltip={{ label: tracker.string.AssignTo, direction: tooltipAlignment }}

View File

@ -24,7 +24,7 @@
</script>
<div
class="ac-header full divide"
class="ac-header full divide caption-height"
class:header-with-mode-selector={modeSelectorProps !== undefined}
class:header-without-label={!label}
>

View File

@ -15,10 +15,11 @@
<script lang="ts">
import { createQuery } from '@hcengineering/presentation'
import { Project, Issue } from '@hcengineering/tracker'
import { Spinner, IconClose, tooltip } from '@hcengineering/ui'
import { Spinner, IconClose, Button } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import tracker from '../../plugin'
import { getIssueId } from '../../issues'
import PriorityRefPresenter from './PriorityRefPresenter.svelte'
export let issue: Issue
@ -31,51 +32,65 @@
$: issueId = project && getIssueId(project, issue)
</script>
<div class="flex-center root">
<div class="parentIssue-container">
<div class="flex-no-shrink mr-1-5">
<PriorityRefPresenter value={issue.priority} shouldShowLabel={false} />
</div>
{#if issueId}
<span class="overflow-label flex-no-shrink">{issueId}</span>
<span class="overflow-label flex-no-shrink content-dark-color">{issueId}</span>
{:else}
<Spinner size="small" />
{/if}
<span class="overflow-label issue-title">{issue.title}</span>
<Button
icon={IconClose}
showTooltip={{ label: tracker.string.RemoveParent, direction: 'bottom' }}
kind={'transparent'}
size={'small'}
on:click={() => dispatch('close')}
/>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
<!-- <div
class="button-close"
use:tooltip={{ label: tracker.string.RemoveParent, direction: 'bottom' }}
on:click={() => dispatch('close')}
>
<IconClose size="x-small" />
</div>
</div> -->
</div>
<style lang="scss">
.root {
padding: 0.375rem 0.75rem;
line-height: 150%;
.parentIssue-container {
display: flex;
align-items: center;
padding: 0.25rem 0.25rem 0.25rem 0.75rem;
max-width: fit-content;
border: 1px solid var(--button-border-color);
min-width: 0;
// line-height: 150%;
height: 2.25rem;
border: 1px solid var(--theme-button-border);
border-radius: 0.25rem;
box-shadow: var(--button-shadow);
}
.issue-title {
margin: 0 0.75rem 0 0.5rem;
margin: 0 0.25rem 0 0.375rem;
padding-right: 0.75rem;
color: var(--accent-color);
border-right: 1px solid var(--button-border-color);
min-width: 0;
color: var(--theme-caption-color);
border-right: 1px solid var(--theme-button-border);
}
.button-close {
cursor: pointer;
position: relative;
color: var(--content-color);
color: var(--theme-dark-color);
transition: color 0.15s;
&:hover {
color: var(--caption-color);
color: var(--theme-content-color);
}
&:active {
color: var(--accent-color);
color: var(--theme-dark-color);
}
&::before {
position: absolute;

View File

@ -22,6 +22,7 @@
export let colorInherit: boolean = false
export let accent: boolean = false
export let inline: boolean = false
export let shouldShowLabel: boolean = true
$: icon = issuePriorities[value]?.icon
$: label = issuePriorities[value]?.label
@ -31,12 +32,14 @@
{#if !inline && icon}
<Icon {icon} {size} fill={'var(--theme-caption-color)'} />
{/if}
<span
class="overflow-label"
class:ml-2={!inline && icon}
style:color={colorInherit ? 'inherit' : 'var(--theme-content-color)'}
class:fs-bold={accent}
>
<Label {label} />
</span>
{#if shouldShowLabel}
<span
class="overflow-label"
class:ml-2={!inline && icon}
style:color={colorInherit ? 'inherit' : 'var(--theme-content-color)'}
class:fs-bold={accent}
>
<Label {label} />
</span>
{/if}
</div>

View File

@ -40,6 +40,7 @@
export let width: string | undefined = undefined
export let defaultIssueStatus: Ref<IssueStatus> | undefined = undefined
export let focusIndex: number | undefined = undefined
export let short: boolean = false
const client = getClient()
const dispatch = createEventDispatcher()
@ -120,7 +121,10 @@
{#if selectedStatus}<IssueStatusIcon value={selectedStatus} size={kind === 'list' ? 'small' : 'medium'} />{/if}
</div>
{#if selectedStatusLabel}
<span class="{kind === 'list' ? 'ml-1 text-md' : 'ml-2 text-base'} overflow-label disabled content-color">
<span
class="{kind === 'list' ? 'ml-1 text-md' : 'ml-2 text-base'} overflow-label disabled content-color"
class:max-w-20={short}
>
{selectedStatusLabel}
</span>
{/if}
@ -134,22 +138,25 @@
{kind}
{width}
{focusIndex}
{short}
on:click={handleStatusEditorOpened}
>
<span slot="content" class="flex-row-center pointer-events-none">
<svelte:fragment slot="icon">
{#if selectedStatus}
<IssueStatusIcon value={selectedStatus} size={iconSize} />
{/if}
</svelte:fragment>
<svelte:fragment slot="content">
{#if selectedStatusLabel}
<span
class="overflow-label disabled"
class:ml-1={selectedStatus && smallgap}
class:ml-1-5={selectedStatus && smallgap}
class:ml-2={selectedStatus && !smallgap}
>
{selectedStatusLabel}
</span>
{/if}
</span>
</svelte:fragment>
</Button>
{/if}
{/if}

View File

@ -84,7 +84,7 @@
label={tracker.string.TimeSpendValue}
notSelected={value.estimation === 0}
labelParams={{ value: value.estimation }}
icon={tracker.icon.Estimation}
icon={tracker.icon.DueDate}
{justify}
{width}
{size}

View File

@ -68,6 +68,7 @@
dispatch('close')
}}
okLabel={presentation.string.Ok}
gap={'gapV-4'}
on:close={() => {
dispatch('close', null)
}}

View File

@ -89,6 +89,7 @@
label={value === undefined ? tracker.string.TimeSpendReportAdd : tracker.string.TimeSpendReportValue}
{canSave}
okAction={create}
gap={'gapV-4'}
on:close
okLabel={value === undefined ? presentation.string.Create : presentation.string.Save}
on:changeContent

View File

@ -43,7 +43,7 @@
value={dateMs}
editable={true}
shouldShowLabel={true}
icon={'normal'}
iconModifier={'normal'}
{kind}
{size}
on:change={handleDateChanged}

View File

@ -57,6 +57,7 @@
okAction={onSave}
canSave={object.label !== ''}
okLabel={tracker.string.CreateMilestone}
gap={'gapV-4'}
on:close={() => dispatch('close')}
on:changeContent
>
@ -73,7 +74,7 @@
<StyledTextArea
bind:content={object.description}
placeholder={tracker.string.ComponentDescriptionPlaceholder}
emphasized
kind={'emphasized'}
showButtons={false}
/>
<svelte:fragment slot="pool">

View File

@ -183,6 +183,7 @@
okLabel={isNew ? presentation.string.Create : presentation.string.Save}
okAction={handleSave}
canSave={name.length > 0 && !(members.length === 0 && isPrivate)}
gap={'gapV-4'}
on:close={() => {
dispatch('close')
}}

View File

@ -64,6 +64,7 @@
okLabel={tracker.string.CreateScrum}
{canSave}
okAction={onSave}
gap={'gapV-4'}
on:close={() => dispatch('close')}
on:changeContent
>
@ -74,7 +75,7 @@
<StyledTextArea
bind:content={object.description}
placeholder={tracker.string.ScrumDescriptionPlaceholder}
emphasized
kind={'emphasized'}
/>
<svelte:fragment slot="pool">
<UserBoxList bind:items={object.members} label={tracker.string.ScrumMembersSearchPlaceholder} />

View File

@ -125,10 +125,10 @@
okAction={createIssueTemplate}
{canSave}
okLabel={tracker.string.SaveProcess}
gap={'gapV-4'}
on:close={() => {
dispatch('close')
}}
createMore={false}
on:changeContent
>
<svelte:fragment slot="header">
@ -148,7 +148,7 @@
<StyledTextBox
alwaysEdit
showButtons={false}
emphasized
kind={'emphasized'}
bind:content={object.description}
placeholder={tracker.string.IssueDescriptionPlaceholder}
/>

View File

@ -131,6 +131,7 @@ export default mergeIds(trackerId, tracker, {
Attachments: '' as IntlString,
Labels: '' as IntlString,
Space: '' as IntlString,
NoDueDate: '' as IntlString,
SetDueDate: '' as IntlString,
ChangeDueDate: '' as IntlString,
ModificationDate: '' as IntlString,

View File

@ -34,7 +34,7 @@
<div class="selectPopup" use:resizeObserver={() => dispatch('changeContent')}>
<div class="flex-row-center justify-stretch p-2">
<div class="overflow-label flex-grow">
<EditBox bind:value {placeholder} {format} {kind} focus on:keypress={_onkeypress} maxWidth={'12rem'} />
<EditBox bind:value {placeholder} {format} {kind} select on:keypress={_onkeypress} maxWidth={'12rem'} />
</div>
<div class="ml-2">
<Button icon={IconCheck} size={'small'} on:click={() => dispatch('close', value)} />

View File

@ -24,6 +24,7 @@
export let props: Record<string, any> = {}
export let inline: boolean = true
export let shouldShowAvatar: boolean = true
export let noUnderline: boolean = false
const client = getClient()
let presenter: AttributeModel | undefined
@ -58,5 +59,5 @@
</script>
{#if presenter}
<svelte:component this={presenter.presenter} value={doc} {...props} {inline} {shouldShowAvatar} />
<svelte:component this={presenter.presenter} value={doc} {...props} {inline} {shouldShowAvatar} {noUnderline} />
{/if}

View File

@ -23,6 +23,7 @@
export let placeholder: IntlString
export let value: string
export let focus: boolean = false
export let select: boolean = false
export let onChange: (value: string) => void = () => {}
export let kind: ButtonKind | undefined = undefined
export let readonly = false
@ -70,5 +71,5 @@
<span class="content-dark-color"><Label label={placeholder} /></span>
{/if}
{:else}
<EditBox {placeholder} bind:value {focus} on:change={_onchange} />
<EditBox {placeholder} bind:value {focus} {select} on:change={_onchange} />
{/if}

View File

@ -306,6 +306,7 @@
okAction={save}
okLabel={presentation.string.Save}
canSave={true}
gap={'gapV-4'}
on:close={() => {
dispatch('close')
}}

View File

@ -42,6 +42,7 @@
label={view.string.NewFilteredView}
okAction={saveFilter}
canSave={filterName.length > 0}
gap={'gapV-4'}
on:close={() => {
dispatch('close')
}}

View File

@ -87,7 +87,7 @@ async function initIssues (prefix: string, page: Page): Promise<IssueProps[]> {
test.describe('tracker layout tests', () => {
const id = generateId(4)
test.beforeEach(async ({ page }) => {
test.setTimeout(60000)
test.setTimeout(120000)
await navigate(page)
issuesProps = await initIssues(id, page)
})

View File

@ -245,10 +245,8 @@ test('create-issue-draft', async ({ page }) => {
await page.locator('[placeholder="Type text\\.\\.\\."]').fill('1')
await page.locator('.ml-2 > .antiButton').click()
// Click button:nth-child(8)
await page.locator('#more-actions').click()
// Click button:has-text("Set due date…")
await page.locator('button:has-text("Set due date…")').click()
// Click button:has-text("No due date")
await page.locator('button:has-text("No due date")').click()
// Click text=24 >> nth=0
await page.locator('.date-popup-container >> text=24').first().click()