Merge branch 'main' into storybook-7-infrastructure

Signed-off-by: Oleg Markelov <markelolegov@gmail.com>
This commit is contained in:
Oleg Markelov 2023-04-25 13:01:18 +07:00
commit babd91c87a
235 changed files with 3629 additions and 2486 deletions

File diff suppressed because it is too large Load Diff

View File

@ -529,7 +529,7 @@ export function createModel (builder: Builder): void {
{
key: '',
presenter: tracker.component.IssuePresenter,
props: { type: 'issue', listProps: { fixed: 'left' } }
props: { type: 'issue', listProps: { key: 'issue', fixed: 'left' } }
},
{
key: '',
@ -544,7 +544,11 @@ export function createModel (builder: Builder): void {
presenter: tags.component.LabelsPresenter,
props: { kind: 'list', full: false, lookupField: 'labels', listProps: { optional: true, compression: true } }
},
{ key: '', presenter: tracker.component.DueDatePresenter, props: { kind: 'list' } },
{
key: '',
presenter: tracker.component.DueDatePresenter,
props: { kind: 'list', listProps: { optional: true, compression: true } }
},
{
key: '',
presenter: tracker.component.ComponentEditor,
@ -575,15 +579,17 @@ export function createModel (builder: Builder): void {
}
}
},
{ key: '', presenter: view.component.DividerPresenter, props: { type: 'divider' } },
{
key: '',
presenter: tracker.component.EstimationEditor,
props: { kind: 'list', size: 'small', listProps: { optional: true } }
props: { kind: 'list', size: 'small', listProps: { key: 'estimation', fixed: 'left' } }
},
{ key: '', presenter: view.component.DividerPresenter, props: { type: 'divider' } },
{
key: 'modifiedOn',
presenter: tracker.component.ModificationDatePresenter,
props: { listProps: { fixed: 'right', optional: true } }
props: { listProps: { key: 'modified', fixed: 'left' } }
},
{
key: '$lookup.assignee',

View File

@ -210,7 +210,7 @@
}
export function select (offset: 1 | -1 | 0, of?: Doc, dir?: 'vertical' | 'horizontal'): void {
let pos = (of !== undefined ? objects.findIndex((it) => it._id === of._id) : selection) ?? -1
let pos = (of != null ? objects.findIndex((it) => it._id === of._id) : selection) ?? -1
if (pos === -1) {
for (const st of categories) {
const stateObjs = getGroupByValues(groupByDocs, st) ?? []
@ -237,7 +237,7 @@
if (objState === -1) {
return
}
const stateObjs = getGroupByValues(groupByDocs, categories.indexOf(objState)) ?? []
const stateObjs = getGroupByValues(groupByDocs, categories[objState]) ?? []
const statePos = stateObjs.findIndex((it) => it._id === obj._id)
if (statePos === undefined) {
return
@ -302,7 +302,7 @@
}
</script>
<div class="kanban-container top-divider">
<div class="kanban-container">
<ScrollBox>
<div class="kanban-content">
{#each categories as state, si (typeof state === 'object' ? state.name : state)}
@ -360,7 +360,6 @@
position: relative;
width: 100%;
height: 100%;
background: var(--board-bg-color);
}
.kanban-content {
display: flex;

View File

@ -25,13 +25,13 @@
align-items: center;
padding: 0.75rem 1.5rem;
color: var(--dark-color);
background-color: var(--board-card-bg-color);
border: 1px dotted var(--divider-color);
background-color: var(--theme-kanban-card-bg-color);
border: 1px dotted var(--theme-kanban-card-border);
border-radius: 0.75rem;
cursor: pointer;
&:hover {
color: var(--caption-color);
color: var(--theme-caption-color);
}
}
</style>

View File

@ -14,7 +14,7 @@
-->
<script lang="ts">
import { CategoryType, Doc, Ref } from '@hcengineering/core'
import ui, { Button, IconMoreH } from '@hcengineering/ui'
import ui, { Button, IconMoreH, mouseAttractor } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import { slide } from 'svelte/transition'
import { CardDragEvent, Item } from '../types'
@ -69,7 +69,7 @@
class="card-container"
class:selection={selection !== undefined ? objects[selection]?._id === object._id : false}
class:checked={checkedSet.has(object._id)}
on:mouseover={() => dispatch('obj-focus', object)}
on:mouseover={mouseAttractor(() => dispatch('obj-focus', object))}
on:focus={() => {}}
on:contextmenu={(evt) => showMenu(evt, object)}
draggable={true}
@ -106,13 +106,14 @@
<style lang="scss">
.card-container {
background-color: var(--board-card-bg-color);
background-color: var(--theme-kanban-card-bg-color);
border: 1px solid var(--theme-kanban-card-border);
border-radius: 0.25rem;
// transition: box-shadow .15s ease-in-out;
&:hover {
background-color: var(--board-card-bg-hover);
}
// &:hover {
// background-color: var(--board-card-bg-hover);
// }
&.checked {
background-color: var(--highlight-select);
box-shadow: inset 0 0 1px 1px var(--highlight-select-border);
@ -123,7 +124,7 @@
}
&.selection,
&.checked.selection {
box-shadow: inset 0 0 1px 1px var(--primary-bg-color);
box-shadow: inset 0 0 1px 1px var(--primary-button-enabled);
animation: anim-border 1s ease-in-out;
&:hover {

View File

@ -143,7 +143,10 @@
<div class="popupPanel-body__mobile-content clear-mins" class:max={useMaxWidth}>
<slot />
{#if !withoutActivity}
<Component is={activity.component.Activity} props={{ object, showCommenInput: !withoutInput }} />
<Component
is={activity.component.Activity}
props={{ object, showCommenInput: !withoutInput, shouldScroll: embedded }}
/>
{/if}
</div>
{:else}
@ -151,7 +154,10 @@
<div class="popupPanel-body__main-content py-8 clear-mins" class:max={useMaxWidth}>
<slot />
{#if !withoutActivity}
<Component is={activity.component.Activity} props={{ object, showCommenInput: !withoutInput }} />
<Component
is={activity.component.Activity}
props={{ object, showCommenInput: !withoutInput, shouldScroll: embedded }}
/>
{/if}
</div>
</Scroller>

View File

@ -19,6 +19,7 @@
import { createEventDispatcher } from 'svelte'
import presentation from '..'
import { deviceOptionsStore as deviceInfo, resizeObserver } from '@hcengineering/ui'
import IconForward from './icons/Forward.svelte'
export let label: IntlString
export let labelProps: any | undefined = undefined
@ -47,7 +48,7 @@
<div class="antiCard-header__title-wrap">
{#if $$slots.header}
<slot name="header" />
<span class="antiCard-header__divider"></span>
<span class="antiCard-header__divider"><IconForward size={'small'} /></span>
{/if}
<span class="antiCard-header__title">
{#if $$slots.title}
@ -83,6 +84,7 @@
<slot name="pool" />
</div>
{/if}
<div class="antiCard-pool__separator" />
<div class="antiCard-footer reverse">
<div class="buttons-group text-sm flex-no-shrink">
{#if $$slots.buttons}
@ -97,6 +99,7 @@
disabled={!canSave}
label={okLabel}
kind={'primary'}
size={'large'}
on:click={() => {
if (okProcessing) {
return

View File

@ -61,7 +61,7 @@
readonly={isReadonly}
label={attribute?.label}
placeholder={attribute?.label}
kind={'link'}
kind={'secondary'}
size={'large'}
width={'100%'}
justify={'left'}

View File

@ -36,7 +36,7 @@
<Button
focus
label={presentation.string.Ok}
size={'small'}
size={'large'}
kind={'primary'}
loading={processing}
on:click={() => {
@ -55,7 +55,7 @@
{#if canSubmit}
<Button
label={presentation.string.Cancel}
size={'small'}
size={'large'}
on:click={() => {
dispatch('close', false)
}}
@ -71,14 +71,14 @@
padding: 2rem 1.75rem 1.75rem;
width: 30rem;
max-width: 40rem;
background: var(--popup-bg-color);
background: var(--theme-bg-color);
border-radius: 1.25rem;
user-select: none;
box-shadow: var(--popup-shadow);
.message {
margin-bottom: 1.75rem;
color: var(--accent-color);
color: var(--theme-content-color);
}
.footer {
flex-shrink: 0;

View File

@ -20,6 +20,7 @@
export let onClick: ((event: MouseEvent) => void) | undefined = undefined
export let noUnderline = false
export let inline = false
export let colorInherit: boolean = false
export let shrink: number = 0
function clickHandler (e: MouseEvent) {
@ -53,13 +54,14 @@
class:cursor-pointer={!disableClick}
class:noUnderline
class:inline
class:colorInherit
style:flex-shrink={shrink}
on:click={clickHandler}
>
<slot />
</span>
{:else}
<a {href} class:noUnderline class:inline style:flex-shrink={shrink} on:click={clickHandler}>
<a {href} class:noUnderline class:inline class:colorInherit style:flex-shrink={shrink} on:click={clickHandler}>
<slot />
</a>
{/if}
@ -70,32 +72,41 @@
display: flex;
align-items: center;
min-width: 0;
color: var(--accent-color);
// overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
font-weight: inherit;
&:not(.colorInherit) {
color: var(--theme-content-color);
}
&.colorInherit {
color: inherit;
}
&.inline {
display: inline-flex;
align-items: center;
}
&.noUnderline {
color: var(--caption-color);
font-weight: 500;
&:not(.colorInherit) {
color: var(--theme-caption-color);
}
}
&:not(.noUnderline) {
&:hover {
color: var(--caption-color);
text-decoration: underline;
&:not(.colorInherit) {
color: var(--theme-caption-color);
}
}
}
&:active {
color: var(--accent-color);
color: var(--theme-content-color);
}
}
</style>

View File

@ -82,7 +82,7 @@
<iframe
class="pdfviewer-content"
style:margin={$deviceInfo.minWidth ? '.5rem' : '1.5rem'}
src={getFileUrl(file) + '#view=FitH'}
src={getFileUrl(file) + '#view=FitH&navpanes=0'}
title=""
/>
{/if}

View File

@ -0,0 +1,24 @@
<!--
// 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">
export let size: 'small' | 'medium' | 'large'
const fill: string = 'currentColor'
</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"
/>
</svg>

View File

@ -361,7 +361,7 @@
align-items: center;
width: 1rem;
height: 1rem;
color: var(--caption-color);
color: var(--theme-caption-color);
opacity: 0.6;
cursor: pointer;
@ -383,8 +383,8 @@
.formatPanelRef {
padding: 0.5rem;
background-color: var(--body-color);
border: 1px solid var(--divider-color);
background-color: var(--theme-comp-header-color);
border: 1px solid var(--theme-divider-color);
border-radius: 0.5rem 0.5rem 0 0;
border-bottom: 0;
@ -402,8 +402,8 @@
align-items: flex-end;
min-height: 2.75rem;
padding: 0.75rem 1rem;
background-color: var(--body-accent);
border: 1px solid var(--divider-color);
background-color: var(--theme-refinput-color);
border: 1px solid var(--theme-divider-color);
border-radius: 0.5rem;
&.withoutTopBorder {
@ -417,7 +417,7 @@
align-items: center;
width: calc(100% - 1.75rem);
height: 100%;
color: var(--content-color);
color: var(--theme-content-color);
background-color: transparent;
border: none;
outline: none;
@ -468,19 +468,18 @@
.icon {
width: 1.25rem;
height: 1.25rem;
color: var(--dark-color);
color: var(--theme-dark-color);
cursor: pointer;
&:hover {
color: var(--caption-color);
color: var(--theme-caption-color);
}
}
&:focus {
border: 1px solid var(--primary-button-focused-border);
box-shadow: 0 0 0 3px var(--primary-button-outline);
box-shadow: 0 0 0 2px var(--primary-button-focused-border);
& > .icon {
color: var(--caption-color);
color: var(--theme-caption-color);
}
}

View File

@ -15,16 +15,17 @@
/* Common Colors */
* {
--primary-button-color: #fff;
--primary-button-enabled: #4474F6;
--primary-button-hovered: #2A5FF6;
--primary-button-pressed: #194CD7;
--primary-button-focused: #194CD7;
--primary-button-disabled: #5771B9;
--primary-button-focused-border: #7393EB;
--primary-button-outline: rgba(87, 132, 255, .3);
--primary-button-border: rgba(255, 255, 255, .09);
// --primary-button-color: #fff;
// --primary-button-enabled: #4474F6;
// --primary-button-hovered: #2A5FF6;
// --primary-button-pressed: #194CD7;
// --primary-button-focused: #194CD7;
// --primary-button-disabled: #5771B9;
// --primary-button-focused-border: #7393EB;
// --primary-button-outline: rgba(87, 132, 255, .3);
// --primary-button-border: rgba(255, 255, 255, .09);
--white-color: #fff;
--duotone-color: rgba(126, 134, 158, .25);
--system-error-color: #EE7A7A;
@ -52,6 +53,70 @@
/* Dark Theme */
.theme-dark {
--primary-button-color: #fff;
--primary-button-enabled: #205DC2;
--primary-button-hovered: #1A53AF;
--primary-button-pressed: #144CA8;
--primary-button-focused: #144CA8;
--primary-button-disabled: #6E9FED;
--primary-button-focused-border: #6E9FED;
--primary-button-border: rgba(255, 255, 255, .09);
--primary-button-outline: rgba(87, 132, 255, .3); // OLD
--theme-button-enabled: rgba(255, 255, 255, .02);
--theme-button-hovered: rgba(255, 255, 255, .04);
--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-refinput-color: rgba(255, 255, 255, .03);
--theme-bg-color: #1A1A28;
--theme-back-color: #0f0f18;
--theme-overlay-color: #0000007f;
--theme-statusbar-color: #2C2C35;
--theme-navpanel-color: #14141F;
--theme-navpanel-hovered: rgba(255, 255, 255, .04);
--theme-navpanel-selected: rgba(255, 255, 255, .07);
--theme-navpanel-divider: rgba(255, 255, 255, .1);
--theme-navpanel-border: transparent;
--theme-navpanel-icons-color: #7F7F7F;
--theme-comp-header-color: #1F1F2C;
--theme-divider-color: rgba(255, 255, 255, .06);
--theme-trans-color: rgba(255, 255, 255, .3);
--theme-darker-color: rgba(255, 255, 255, .4);
--theme-halfcontent-color: rgba(255, 255, 255, .5);
--theme-dark-color: rgba(255, 255, 255, .6);
--theme-content-color: rgba(255, 255, 255, .8);
--theme-caption-color: #FFF;
--theme-list-border-color: rgba(255, 255, 255, .05);
--theme-list-header-color: #C88C65;
--theme-list-subheader-color: #262634;
--theme-list-row-color: #21212F;
--theme-list-button-color: #262633;
--theme-list-divider-color: rgba(255, 255, 255, .09);
--theme-list-subheader-divider: transparent;
--theme-table-border-color: rgba(255, 255, 255, .1);
--theme-table-header-color: #1C1C29;
--theme-table-row-color: #21212F;
--theme-kanban-card-bg-color: rgba(222, 222, 240, .04);
--theme-kanban-card-border: transparent;
--theme-kanban-card-footer: #D9D9D9;
--theme-tablist-color: rgba(0, 0, 0, .02);
--theme-checkbox-color: #000;
--theme-checkbox-bg-color: #FFF;
--theme-checkbox-border: rgba(0, 0, 0, .12);
--theme-checkbox-disabled: #999;
--theme-progress-color: #FFFFFF;
--theme-popup-color: #292938;
--theme-popup-divider: rgba(255, 255, 255, .1);
--body-color: #1f2023;
--body-accent: #222326;
--board-bg-color: #1c1d1f;
@ -61,7 +126,7 @@
--accent-bg-color: #27282b;
--accent-shadow: rgb(0 0 0 / 10%) 0px 2px 4px;
--highlight-hover: #28292b;
--highlight-hover: #282834;
--highlight-select: #252b3a;
--highlight-select-border: #44506b;
--highlight-select-hover: #2c3346;
@ -106,7 +171,7 @@
--button-bg-hover: #37383b;
--button-border-color: #3c3f44;
--button-border-hover: #45484e;
--button-shadow: rgb(0 0 0 / 25%) 0px 1px 1px;
--button-shadow: rgb(0 0 0 / 15%) 0px 1px 1px 1px;
--button-disabled-color: #313236;
--noborder-bg-color: #313236;
--noborder-bg-hover: #37383b;
@ -135,6 +200,70 @@
/* Light Theme */
.theme-light {
--primary-button-color: #fff;
--primary-button-enabled: #2B5190;
--primary-button-hovered: #1A53AF; // DARK
--primary-button-pressed: #144CA8; // DARK
--primary-button-focused: #144CA8; // DARK
--primary-button-disabled: #6E9FED; // DARK
--primary-button-focused-border: #6E9FED; // DARK
--primary-button-border: rgba(255, 255, 255, .09);
--primary-button-outline: rgba(87, 132, 255, .3); // OLD
--theme-button-enabled: rgba(0, 0, 0, .02);
--theme-button-hovered: rgba(0, 0, 0, .04);
--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-refinput-color: rgba(0, 0, 0, .03);
--theme-bg-color: #F1F1F4;
--theme-back-color: #D9D9DD;
--theme-overlay-color: #0000007f;
--theme-statusbar-color: #bfbfc6;
--theme-navpanel-color: #E8E8ED;
--theme-navpanel-hovered: rgba(218, 218, 231, .5);
--theme-navpanel-selected: #DADAE7;
--theme-navpanel-divider: rgba(0, 0, 0, .06);
--theme-navpanel-border: rgba(0, 0, 0, .06);
--theme-navpanel-icons-color: #7F7F7F;
--theme-comp-header-color: #FBFBFC;
--theme-divider-color: rgba(0, 0, 0, .06);
--theme-trans-color: rgba(0, 0, 0, .3);
--theme-darker-color: rgba(0, 0, 0, .4);
--theme-halfcontent-color: rgba(0, 0, 0, .5);
--theme-dark-color: rgba(0, 0, 0, .6);
--theme-content-color: rgba(0, 0, 0, .8);
--theme-caption-color: #000;
--theme-list-border-color: rgba(0, 0, 0, .09);
--theme-list-header-color: #ECD4CA;
--theme-list-subheader-color: #EEEEF0;
--theme-list-row-color: #F7F7F8;
--theme-list-button-color: #F2F2F4;
--theme-list-divider-color: rgba(0, 0, 0, .07);
--theme-list-subheader-divider: rgba(0, 0, 0, .06);
--theme-table-border-color: rgba(0, 0, 0, .1);
--theme-table-header-color: #EFEFF2;
--theme-table-row-color: #F4F4F6;
--theme-kanban-card-bg-color: rgba(0, 0, 0, .03);
--theme-kanban-card-border: rgba(0, 0, 0, .04);
--theme-kanban-card-footer: rgba(0, 0, 0, .04);
--theme-tablist-color: rgba(0, 0, 0, .02);
--theme-checkbox-color: #000;
--theme-checkbox-bg-color: #FFF;
--theme-checkbox-border: rgba(0, 0, 0, .12);
--theme-checkbox-disabled: #999;
--theme-progress-color: rgba(0, 0, 0, .5);
--theme-popup-color: #F1F1F4;
--theme-popup-divider: rgba(0, 0, 0, .1);
--body-color: #fff;
--body-accent: #fafafa; // HZ
--board-bg-color: #f4f5f8;
@ -144,7 +273,7 @@
--accent-bg-color: #eff0f2; // HZ
--accent-shadow: rgb(0 0 0 / 10%) 0px 2px 4px; // Dark
--highlight-hover: #f9f9f9;
--highlight-hover: #E8E8E9;
--highlight-select: #f0f4ff;
--highlight-select-border: #e6eaff;
--highlight-select-hover: #e4ebff;
@ -189,7 +318,7 @@
--button-bg-hover: #f4f5f8;
--button-border-color: #dfe1e4;
--button-border-hover: #c9cbcd;
--button-shadow: rgb(0 0 0 / 7%) 0px 1px 1px;
--button-shadow: rgb(0 0 0 / 20%) 0px 1px 2px 1px;
--button-disabled-color: #eff1f4;
--noborder-bg-color: #eff1f4;
--noborder-bg-hover: #f4f5f8;

View File

@ -218,6 +218,7 @@ input.search {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
min-width: 0;
min-height: 0;
}
.flex-col-reverse {
@ -363,10 +364,11 @@ input.search {
}
}
.buttons-divider {
min-width: 1px;
flex-shrink: 0;
width: 1px;
height: 1.5rem;
background-color: var(--divider-color);
max-height: 1.5rem;
background-color: var(--theme-list-divider-color);
}
.labels-row {
@ -399,8 +401,8 @@ input.search {
&.reverse > :last-child { margin-right: .375rem; }
}
.gap-2 {
& > *:not(:first-child) { margin-left: .5rem; }
&.reverse > :last-child { margin-right: .5rem; }
&:not(.reverse) > *:not(:first-child) { margin-left: .5rem; }
&.reverse > *:not(:last-child) { margin-right: .5rem; }
}
/* --------- */
@ -467,6 +469,7 @@ input.search {
.mb-8 { margin-bottom: 2rem; }
.mb-9 { margin-bottom: 2.25rem; }
.mb-10 { margin-bottom: 2.5rem; }
.mx-0-5 { margin: 0 .125rem; }
.mx-1 { margin: 0 .25rem; }
.mx-2 { margin: 0 .5rem; }
.mx-3 { margin: 0 .75rem; }
@ -475,6 +478,7 @@ input.search {
.mx-auto { margin: 0 auto; }
.my-2 { margin: .5rem 0; }
.my-4 { margin: 1rem 0; }
.my-5 { margin: 1.25rem 0; }
.m-0-5 { margin: .125rem; }
.m-1 { margin: .25rem; }
@ -484,6 +488,7 @@ input.search {
.pl-3 { padding-left: .75rem; }
.pl-4 { padding-left: 1rem; }
.pl-8 { padding-left: 2rem; }
.pl-10 { padding-left: 2.5rem; }
.pr-1 { padding-right: .25rem; }
.pr-2 { padding-right: .5rem; }
.pr-3 { padding-right: .75rem; }
@ -504,6 +509,7 @@ input.search {
.px-2 { padding: 0 .5rem; }
.px-3 { padding: 0 .75rem; }
.px-4 { padding: 0 1rem; }
.px-10 { padding: 0 2.5rem; }
.py-1 { padding: 0.25rem 0; }
.py-4 { padding: 1rem 0; }
.py-8 { padding: 2rem 0; }
@ -612,6 +618,7 @@ input.search {
.min-w-min { min-width: min-content; }
.min-h-0 { min-height: 0; }
.min-h-2 { min-height: .5rem; }
.min-h-4 { min-height: 1rem; }
.min-h-7 { min-height: 1.75rem; }
.min-h-30 { min-height: 7.5rem; }
.min-h-60 { min-height: 15rem; }
@ -623,7 +630,9 @@ input.search {
.max-w-60 { max-width: 15rem; }
.max-w-80 { max-width: 20rem; }
.max-w-240 { max-width: 60rem; }
.max-h-0 { max-height: 0; }
.max-h-2 { max-height: .5rem; }
.max-h-4 { max-height: 1rem; }
.max-h-30 { max-height: 7.5rem; }
.max-h-50 { max-height: 12.5rem; }
.max-h-60 { max-height: 15rem; }
@ -642,20 +651,20 @@ input.search {
height: 1em;
}
.svg-x-small {
width: .857em;
height: .857em;
width: .75rem;
height: .75rem;
}
.svg-small {
width: 1.143em;
height: 1.143em;
width: 1rem;
height: 1rem;
}
.svg-medium {
width: 1.429em;
height: 1.429em;
width: 1.25rem;
height: 1.25rem;
}
.svg-large {
width: 1.715em;
height: 1.715em;
width: 1.5rem;
height: 1.5rem;
}
.svg-full {
width: inherit;
@ -767,26 +776,26 @@ a.no-line {
}
.focused-button {
background-color: var(--button-bg-color);
background-color: var(--theme-button-enabled);
border: 1px solid transparent;
& > .icon { color: var(--accent-color); }
& > .icon { color: var(--theme-content-color); }
&.selected {
background-color: var(--button-bg-hover);
border: 1px solid var(--button-border-hover);
background-color: var(--theme-button-pressed);
border: 1px solid var(--theme-button-border);
}
&:hover {
background-color: var(--button-bg-hover);
border: 1px solid var(--button-border-hover);
& > .icon { color: var(--caption-color); }
background-color: var(--theme-button-hovered);
border: 1px solid var(--theme-button-border);
& > .icon { color: var(--theme-caption-color); }
}
&:focus {
border: 1px solid var(--primary-button-focused-border);
box-shadow: 0 0 0 3px var(--primary-button-outline);
& > .icon { color: var(--caption-color); }
border: 1px solid var(--theme-button-border);
box-shadow: 0 0 0 2px var(--primary-button-focused-border);
& > .icon { color: var(--theme-caption-color); }
}
&.bordered { border-color: var(--button-border-color); }
&.bordered { border-color: var(--theme-button-border); }
}
.overflow-x-auto { overflow-x: auto; }
@ -817,6 +826,8 @@ a.no-line {
.background-primary-color { background-color: var(--primary-button-enabled); }
.background-content-accent-color { background-color: var(--accent-color); }
.content-trans-color { color: var(--theme-trans-color); }
.dark-color,
.content-dark-color { color: var(--dark-color); }
.content-color { color: var(--content-color); }

View File

@ -15,15 +15,16 @@
/* Panels */
* {
--app-panel-width: 4rem;
--app-panel-width: 4.25rem;
}
.antiPanel-application {
flex-shrink: 0;
display: flex;
justify-content: space-between;
align-items: center;
background-color: var(--board-bg-color);
background-color: var(--theme-navpanel-color);
border-right: 1px solid var(--theme-navpanel-divider);
&.vertical {
flex-direction: column;
min-width: var(--app-panel-width);
@ -43,37 +44,39 @@
flex-direction: column;
height: 100%;
overflow: hidden;
&.filled { background-color: var(--body-accent); }
&.border-left { border-left: 1px solid var(--divider-color); }
&.border-right { border-right: 1px solid var(--divider-color); }
&.filled { background-color: var(--theme-bg-color); }
&.border-left { border-left: 1px solid var(--theme-divider-color); }
&.border-right { border-right: 1px solid var(--theme-divider-color); }
}
.antiPanel-navigator {
position: relative;
min-width: 17.5rem;
max-width: 17.5rem;
width: 17.5rem;
background-color: var(--theme-navpanel-color);
border-right: 1px solid var(--theme-navpanel-border);
}
@media (max-width: 1024px) {
.antiPanel-navigator {
position: fixed;
background-color: var(--body-accent);
top: var(--status-bar-height);
height: calc(100% - var(--status-bar-height));
background-color: var(--theme-navpanel-color);
filter: drop-shadow(2px 0 1px rgba(0, 0, 0, .2));
z-index: 450;
&.portrait {
top: var(--status-bar-height);
left: 0;
}
&.landscape {
top: var(--status-bar-height);
left: var(--app-panel-width);
left: var(--app-panel-width);
}
}
}
.antiPanel-component:not(.aside) {
flex-grow: 1;
// background-color: var(--board-bg-color);
background-color: var(--theme-bg-color);
}
.antiPanel-component.aside {
min-width: 30rem;
@ -169,13 +172,13 @@
}
&:hover, &.hovered, &.selected {
background-color: var(--menu-bg-select);
// background-color: var(--theme-navpanel-hovered);
.an-element__icon,
.an-element__label { color: var(--caption-color); }
.an-element__icon-arrow { opacity: 1; }
}
&:hover, &.hovered { background-color: var(--highlight-hover); }
&.selected { background-color: var(--menu-bg-select); }
&:hover, &.hovered { background-color: var(--theme-navpanel-hovered); }
&.selected { background-color: var(--theme-navpanel-selected); }
&:hover .an-element__tool, &.hovered .an-element__tool { visibility: visible; }
&:not(.collapsed) .an-element__icon-arrow { opacity: 1; }
@ -220,7 +223,7 @@
margin: .5rem 0;
height: 1px;
&.line { background-color: var(--divider-color); }
&.line { background-color: var(--theme-navpanel-divider); }
&.short { margin: .25rem 1rem; }
}
.antiNav-space {
@ -231,7 +234,7 @@
flex-shrink: 0;
width: 100%;
height: 1px;
background-color: var(--divider-color);
background-color: var(--theme-navpanel-divider);
}
.antiNav-footer-grower {
flex-shrink: 10;
@ -297,7 +300,7 @@
margin: .25rem 0;
min-height: 1px;
height: 1px;
background-color: var(--divider-color);
background-color: var(--theme-divider-color);
&.dark { background-color: var(--body-accent); }
&.noMargin { margin: 0; }
@ -351,20 +354,7 @@
border-radius: .5rem .5rem 0 0;
}
&__counter {
display: flex;
align-items: center;
flex-wrap: nowrap;
flex-shrink: 0;
padding: 0.25rem 0.5rem;
min-width: 1.325rem;
text-align: center;
font-weight: 500;
font-size: 1rem;
line-height: 1rem;
color: var(--accent-color);
background-color: var(--body-color);
border: 1px solid var(--divider-color);
border-radius: 1rem;
color: var(--theme-darker-color);
}
&__tag {
display: flex;
@ -431,17 +421,17 @@
// Emphasized
.antiEmphasized {
padding: .75rem;
background-color: var(--body-accent);
border: 1px solid var(--button-border-color);
border-radius: .5rem;
background-color: var(--theme-comp-header-color);
border: 1px solid var(--theme-popup-divider);
border-radius: .25rem;
transition-property: border, background-color;
transition-duration: .15s;
transition-timing-function: var(--timing-main);
&:hover,
&:focus-within {
background-color: var(--body-color);
border-color: var(--button-border-hover);
// background-color: var(--body-color);
border-color: var(--theme-list-divider-color);
}
}

View File

@ -20,10 +20,15 @@
height: 100%;
.ac-header {
padding: 0.5rem 1.5rem 0.5rem 2.5rem;
padding: 0.5rem 2.25rem;
background-color: var(--theme-comp-header-color);
// height: 3.5rem;
// min-height: 2.5rem;
&.caption-height {
min-height: 3.25rem;
}
&.search-start { padding-left: 1.75rem; }
&.short {
display: flex;
align-items: center;
@ -31,21 +36,23 @@
}
&.full,
&-full {
display: grid;
grid-template-columns: auto;
grid-auto-flow: column;
grid-auto-columns: max-content;
gap: .75rem;
display: flex;
justify-content: space-between;
align-items: center;
min-width: 0;
&:not(.small-gap, .medium-gap) > *:not(:last-child) { margin-right: 1.25rem; }
&.small-gap > *:not(:last-child) { margin-right: .75rem; }
&.medium-gap > *:not(:last-child) { margin-right: 1rem; }
}
&.withSettings { padding-right: .75rem; }
// &.withSettings { padding-right: .75rem; }
&.mini {
display: flex;
flex-direction: column;
}
&.mirror {
justify-content: space-between;
padding: 0 1rem;
// padding: 0 1rem;
&-tool {
justify-content: space-between;
@ -53,7 +60,7 @@
}
}
&.divide {
border-bottom: 1px solid var(--divider-color);
border-bottom: 1px solid var(--theme-divider-color);
}
.secondRow {
align-self: flex-end;
@ -77,23 +84,30 @@
.ac-header__icon {
margin-right: 0.5rem;
color: var(--content-color);
color: var(--theme-content-color);
}
.ac-header__title {
flex-shrink: 1;
min-width: 0;
font-weight: 500;
font-size: 1rem;
color: var(--caption-color);
color: var(--theme-caption-color);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
user-select: none;
}
.ac-header__counter {
flex-shrink: 0;
margin-left: .25rem;
min-width: 0;
font-size: 1rem;
color: var(--theme-darker-color);
}
.ac-header__description {
min-width: 0;
font-size: 0.75rem;
color: var(--dark-color);
color: var(--theme-dark-color);
overflow: hidden;
visibility: visible;
@ -316,14 +330,14 @@
// height: 1.5rem;
}
&__element {
fill: var(--accent-bg-color);
stroke: var(--divider-color);
fill: var(--theme-button-enabled);
stroke: var(--theme-button-border);
stroke-linecap: round;
stroke-linejoin: round;
&:hover { fill: var(--button-bg-color); }
&:hover { fill: var(--theme-button-hovered); }
}
&__selected { fill: var(--button-bg-hover); }
&__selected { fill: var(--theme-button-pressed); }
.asb-label__container {
position: absolute;
@ -338,11 +352,11 @@
height: 100%;
font-weight: 500;
font-size: 0.8125rem;
color: var(--dark-color);
color: var(--theme-dark-color);
pointer-events: none;
&.selected {
color: var(--caption-color);
color: var(--theme-caption-color);
}
}
}
@ -360,18 +374,24 @@
&:last-child { padding-right: 0; }
}
th {
height: 2.5rem;
font-weight: 500;
font-size: .75rem;
color: var(--dark-color);
box-shadow: inset 0 -1px 0 0 var(--divider-color);
height: 3rem;
font-weight: 600;
font-size: .625rem;
letter-spacing: .5px;
text-transform: uppercase;
color: var(--theme-dark-color);
box-shadow: inset 0 -1px 0 0 var(--theme-table-border-color);
user-select: none;
// z-index: 5;
&.sortable { cursor: pointer; }
&.sorted .icon {
margin-left: .25rem;
opacity: .6;
&.sorted {
color: var(--theme-caption-color);
.icon {
margin-left: .25rem;
opacity: .6;
}
}
&:hover .antiTable-cells__checkCell { visibility: visible; }
.checkall { visibility: visible; }
@ -379,7 +399,7 @@
&.editable {
th, td, tr {
border: 1px dashed var(--divider-color);
border: 1px dashed var(--theme-divider-color);
}
}
@ -424,10 +444,12 @@
}
.antiTable-body__row {
position: relative;
height: 3.25rem;
color: var(--caption-color);
color: var(--theme-caption-color);
background-color: var(--theme-table-row-color);
border-bottom: 1px solid var(--theme-divider-color);
&:not(:last-child) { border-bottom: 1px solid var(--accent-bg-color); }
&:hover .antiTable-cells__firstCell .antiTable-cells__firstCell-menuRow { visibility: visible; }
&:hover, &.checking {
.antiTable-cells__checkCell { visibility: visible; }
@ -442,14 +464,14 @@
}
.antiTable-body__border {
border: 1px solid var(--divider-color);
border: 1px solid var(--theme-divider-color);
}
&.highlightRows .antiTable-body__row {
&.selected { background-color: var(--highlight-hover); }
&.checking {
background-color: var(--highlight-select);
border-bottom-color: var(--highlight-select-border);
// border-bottom-color: var(--highlight-select-border);
&:hover { background-color: var(--highlight-select-hover); }
}
@ -460,8 +482,8 @@
position: sticky;
z-index: 2;
top: 0;
height: 2.5rem;
background-color: var(--body-color);
height: 3rem;
background-color: var(--theme-table-header-color);
}
.scroller-tfoot {
@ -469,10 +491,10 @@
z-index: 2;
bottom: 0;
height: 2.5rem;
background-color: var(--body-color);
background-color: var(--theme-table-header-color);
tr {
box-shadow: inset 0 1px 0 0 var(--divider-color);
box-shadow: inset 0 1px 0 0 var(--theme-divider-color);
}
}
@ -480,9 +502,22 @@
th, td {
&:first-child {
position: sticky;
z-index: 1;
padding: 0;
left: 0;
background-color: var(--body-color);
background-color: var(--theme-bg-color);
border-right: 1px solid transparent !important;
z-index: 1;
}
.fullfill {
display: flex;
align-items: center;
padding: .5rem;
width: 100%;
height: 100%;
background-color: var(--theme-bg-color);
border-right: 1px solid var(--theme-divider-color);
&.center { justify-content: center; }
}
}
}
@ -497,12 +532,12 @@
// Basic component view.
.antiComponentBox {
padding: 0.5rem;
background-color: var(--board-card-bg-color);
border: 1px solid var(--divider-color);
background-color: var(--theme-list-row-color);
border: 1px solid var(--theme-list-divider-color);
border-radius: .75rem;
&.antiComponentBoxFocused {
background-color: var(--board-card-bg-hover);
background-color: var(--theme-button-hovered);
}
}
@ -649,3 +684,46 @@
&.inter { font-size: 1em; }
}
}
/* ListView - global style */
.list-container .category-container .categoryHeader.subLevel.closed {
border-bottom: 1px solid var(--theme-list-border-color);
border-radius: 0 0 0.25rem 0.25rem;
}
.list-container .category-container .categoryHeader.closed:not(.subLevel) {
border-radius: 0 0 .25rem .25rem;
&::before {
border-bottom-color: var(--theme-list-border-color);
border-radius: 0.25rem;
}
}
.list-container .category-container .listGrid {
.fix-margin { margin-left: .875rem; }
.name { margin-left: .375rem; }
.optional-bar {
overflow: hidden;
display: flex;
align-items: center;
flex-shrink: 1;
min-width: 0;
.label-wrapper {
display: flex;
align-items: center;
flex-shrink: 10;
width: auto;
min-width: 0;
}
& > *:not(:last-child) {
flex-shrink: 10;
width: min-content;
}
& > *:last-child {
flex-shrink: 0;
width: max-content;
}
& > * { margin-left: .375rem; }
}
}

View File

@ -20,7 +20,7 @@
left: 0;
width: 100%;
height: 100vh;
background-color: var(--card-overlay-color);
background-color: var(--theme-overlay-color);
pointer-events: all;
}
@ -29,7 +29,7 @@
display: flex;
flex-direction: column;
min-height: 0;
background: var(--popup-bg-color);
background: var(--theme-popup-color);
border-radius: .5rem;
box-shadow: var(--card-shadow);
@ -39,18 +39,26 @@
justify-content: space-between;
align-items: center;
flex-shrink: 0;
padding: 1rem 1rem 1.5rem;
padding: 1.5rem 1.5rem .5rem 1.75rem;
font-size: .8125rem;
&__title-wrap {
display: flex;
align-items: center;
min-width: 0;
& > *:not(:last-child) { margin-right: .5rem; }
}
&__title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
user-select: none;
min-width: 0;
font-weight: 500;
font-size: 1rem;
color: var(--accent-color);
line-height: 150%;
color: var(--theme-caption-color);
}
&__divider { color: var(--theme-content-color); }
&__error {
min-width: 0;
flex-grow: 1;
@ -71,8 +79,9 @@
flex-direction: column;
flex-grow: 1;
flex-shrink: 1;
margin: 0 1rem;
padding: 1rem 1.75rem;
height: fit-content;
min-width: 0;
min-height: 0;
& > *:not(:last-child) { margin-bottom: 1rem; }
@ -81,36 +90,50 @@
.antiCard-pool {
flex-shrink: 0;
display: flex;
flex-direction: column;
margin: 0 1rem .75rem;
color: var(--caption-color);
align-items: center;
flex-wrap: wrap;
margin: .5rem 1.25rem 0 1.75rem;
min-width: 0;
font-size: .8125rem;
color: var(--theme-caption-color);
&__separator {
margin: 1rem 0;
flex-shrink: 0;
margin-top: 1.25rem;
height: 1px;
background-color: var(--divider-color);
background-color: var(--theme-popup-divider);
}
& > * { margin: .5rem .5rem 0 0; }
}
.antiCard-footer {
overflow: hidden;
flex-shrink: 0;
display: grid;
grid-auto-flow: column;
direction: rtl;
justify-content: flex-start;
direction: ltr;
display: flex;
justify-content: space-between;
align-items: center;
column-gap: .75rem;
padding: 1.5rem 1rem 1rem;
height: 4.5rem;
padding: 1rem 1.75rem;
height: 4.25rem;
border-radius: 0 0 .5rem .5rem;
&.reverse { flex-direction: row-reverse; }
&__error {
flex-grow: 1;
display: flex;
margin-left: .375rem;
min-width: 0;
font-weight: 500;
font-size: .75rem;
color: var(--system-error-color);
&:empty { visibility: hidden; }
}
}
.antiCard-group {
padding: .5rem 1rem;
&:not(:last-child) { border-bottom: 1px solid var(--popup-divider); }
&:not(:last-child) { border-bottom: 1px solid var(--theme-divider-color); }
&.grid {
display: grid;
grid-template-columns: 5rem auto;
@ -124,7 +147,7 @@
font-weight: 500;
font-size: .75rem;
line-height: .75rem;
color: var(--content-color);
color: var(--theme-content-color);
}
.value {
display: flex;
@ -148,26 +171,14 @@
width: 90vw;
max-width: 90vw;
max-height: 90vh;
}
&.dialog,
&.mobile {
.antiCard-header {
padding: .75rem .75rem .375rem;
&__title-wrap {
display: flex;
align-items: center;
min-width: 0;
& > *:not(:first-child) { margin-left: .375rem; }
}
&__divider, &__title {
font-weight: 400;
font-size: .8125rem;
}
&__divider { color: var(--content-color); }
&__title { color: var(--accent-color); }
}
.antiCard-content { margin: .5rem 1.125rem 1rem; }
.antiCard-pool {
@ -175,29 +186,10 @@
align-items: center;
margin: 0 .5rem .25rem;
font-size: .75rem;
& > * { margin: 0 .25rem .5rem; }
}
.antiCard-footer {
direction: ltr;
display: flex;
justify-content: space-between;
align-items: center;
padding: .75rem;
height: auto;
border-top: 1px solid var(--divider-color);
&.reverse { flex-direction: row-reverse; }
&__error {
flex-grow: 1;
display: flex;
margin-left: .375rem;
min-width: 0;
font-weight: 500;
font-size: .75rem;
color: var(--system-error-color);
&:empty { visibility: hidden; }
}
}
}
}

View File

@ -115,7 +115,7 @@ body {
font-weight: 400;
font-size: var(--body-font-size);
color: var(--content-color);
background-color: var(--body-color);
background-color: var(--theme-bg-color);
user-select: none;
}

View File

@ -68,7 +68,7 @@
&:not(.embedded) {
border-radius: .5rem;
box-shadow: var(--popup-panel-shadow);
// box-shadow: var(--popup-panel-shadow);
}
.popupPanel-title {
@ -87,9 +87,9 @@
&__bordered {
min-width: 0;
min-height: 3rem;
background-color: var(--board-card-bg-color);
background-color: var(--theme-comp-header-color);
&:not(.embedded) {
border: 1px solid var(--divider-color);
border: 1px solid var(--theme-divider-color);
border-bottom: none;
border-radius: .5rem .5rem 0 0;
}
@ -116,8 +116,8 @@
min-height: 0;
width: 100%;
height: 100%;
background-color: var(--body-color);
border: 1px solid var(--divider-color);
background-color: var(--theme-bg-color);
border: 1px solid var(--theme-divider-color);
&:not(.embedded) {
border-radius: 0 0 .5rem .5rem;
}
@ -184,8 +184,8 @@
min-width: 320px;
}
&.bottom-divider { border-bottom: 1px solid var(--divider-color); }
&.top-divider { border-top: 1px solid var(--divider-color); }
&.bottom-divider { border-bottom: 1px solid var(--theme-divider-color); }
&.top-divider { border-top: 1px solid var(--theme-divider-color); }
.header-row {
display: flex;
align-items: center;
@ -214,7 +214,7 @@
bottom: 1rem;
left: 0;
width: 0;
border-left: 1px solid var(--divider-color);
border-left: 1px solid var(--theme-divider-color);
}
&.float {
@ -226,8 +226,8 @@
min-width: 0;
max-width: 320px;
height: 100%;
background-color: var(--board-card-bg-color);
border-left: 1px solid var(--divider-color);
background-color: var(--theme-list-row-color);
border-left: 1px solid var(--theme-divider-color);
border-bottom-right-radius: .45rem;
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
transition: box-shadow 150ms ease 0s, transform 150ms cubic-bezier(0.175, 0.885, 0.32, 1.275);

View File

@ -422,7 +422,7 @@
flex-direction: column;
padding: .5rem;
min-height: 22rem;
background: var(--popup-bg-color);
background: var(--theme-popup-color);
border-radius: .5rem;
box-shadow: var(--popup-shadow);

View File

@ -27,7 +27,11 @@
"AddDueDate": "Add due date",
"EditDueDate": "Edit due date",
"SaveDueDate": "Save due date",
"IssueNeedsToBeCompletedByThisDate": "Issue needs to be completed by this date",
"NeedsToBeCompletedByThisDate": "Needs to be completed by this date",
"DueDatePopupTitle": "Due on {value}",
"DueDatePopupOverdueTitle": "Was due on {value}",
"DueDatePopupDescription": "{value, plural, =0 {Today} =1 {Tomorrow} other {# days remaining}}",
"DueDatePopupOverdueDescription": "{value, plural, =1 {1 day overdue} other {# days overdue}}",
"English": "English",
"Russian": "Russian",
"MinutesBefore": "{minutes, plural, =1 {a minute before} other {# minutes before}}",

View File

@ -27,7 +27,11 @@
"AddDueDate": "Установить дату",
"EditDueDate": "Изменить дату",
"SaveDueDate": "Сохранить дату",
"IssueNeedsToBeCompletedByThisDate": "Задача должна быть завершена к этой дате",
"NeedsToBeCompletedByThisDate": "Должно быть завершено к этой дате",
"DueDatePopupTitle": "Срок {value}",
"DueDatePopupOverdueTitle": "Должно было завершится {value}",
"DueDatePopupDescription": "{value, plural, =0 {Сегодня} =1 {Завтра} other {# дней осталось}}",
"DueDatePopupOverdueDescription": "{value, plural, =1 {1 день опоздания} other {# дней опаздания}}",
"English": "Английский",
"Russian": "Русский",
"MinutesBefore": "{minutes, plural, =1 {за минуту} other {за # минут}}",

View File

@ -36,6 +36,7 @@
"@hcengineering/platform": "^0.6.8",
"@hcengineering/theme": "^0.6.2",
"@hcengineering/core": "^0.6.23",
"just-clone": "~6.2.0",
"svelte": "3.55.1",
"fast-equals": "^2.0.3"
},

View File

@ -106,3 +106,58 @@ export function numberToRGB (color: number, alpha?: number): string {
return `rgba(${r}, ${g}, ${b}, ${alpha === undefined ? '1' : alpha})`
}
/**
* @public
*/
export function hsvToRGB (h: number, s: number, v: number): { r: number, g: number, b: number, rgb: string } {
let r: number = 0
let g: number = 0
let b: number = 0
const i = Math.floor(h * 6)
const f = h * 6 - i
const p = v * (1 - s)
const q = v * (1 - f * s)
const t = v * (1 - (1 - f) * s)
switch (i % 6) {
case 0:
r = v
g = t
b = p
break
case 1:
r = q
g = v
b = p
break
case 2:
r = p
g = v
b = t
break
case 3:
r = p
g = q
b = v
break
case 4:
r = t
g = p
b = v
break
case 5:
r = v
g = p
b = q
break
}
r = Math.round(r * 255)
g = Math.round(g * 255)
b = Math.round(b * 255)
return {
r,
g,
b,
rgb: '#' + r.toString(16) + g.toString(16) + b.toString(16)
}
}

View File

@ -15,6 +15,7 @@
<script lang="ts">
import type { IntlString, Asset } from '@hcengineering/platform'
import type { AnySvelteComponent, TooltipAlignment } from '../types'
import { ComponentType } from 'svelte'
import Icon from './Icon.svelte'
import { tooltip } from '../tooltips'
@ -22,7 +23,7 @@
export let label: IntlString = '' as IntlString
export let labelProps: any = undefined
export let direction: TooltipAlignment | undefined = undefined
export let icon: Asset | AnySvelteComponent
export let icon: Asset | AnySvelteComponent | ComponentType
export let iconProps: any | undefined = undefined
export let size: 'x-small' | 'small' | 'medium' | 'large'
export let action: (ev: MouseEvent) => Promise<void> | void = async () => {}

View File

@ -17,7 +17,7 @@
import { onMount, ComponentType } from 'svelte'
import { registerFocus } from '../focus'
import { tooltip } from '../tooltips'
import type { AnySvelteComponent, ButtonKind, ButtonShape, ButtonSize, LabelAndProps } from '../types'
import type { AnySvelteComponent, ButtonKind, ButtonShape, ButtonSize, LabelAndProps, IconSize } from '../types'
import Icon from './Icon.svelte'
import Label from './Label.svelte'
import Spinner from './Spinner.svelte'
@ -29,6 +29,8 @@
export let shape: ButtonShape = undefined
export let icon: Asset | AnySvelteComponent | ComponentType | undefined = undefined
export let iconProps: any | undefined = undefined
export let iconRight: Asset | AnySvelteComponent | ComponentType | undefined = undefined
export let iconRightProps: any | undefined = undefined
export let justify: 'left' | 'center' = 'center'
export let disabled: boolean = false
export let loading: boolean = false
@ -45,10 +47,14 @@
export let id: string | undefined = undefined
export let input: HTMLButtonElement | undefined = undefined
export let showTooltip: LabelAndProps | undefined = undefined
export let iconSize: IconSize = size === 'inline' ? 'inline' : 'small'
export let iconRightSize: IconSize = 'x-small'
export let short: boolean = false
let iconSize: ButtonSize
$: iconSize = size === 'inline' ? 'inline' : 'small'
$: iconOnly = label === undefined && ($$slots.content === undefined || $$slots.icon !== undefined)
// $: iconSize = size === 'inline' ? 'inline' : 'small'
$: iconOnly =
label === undefined &&
($$slots.content === undefined || $$slots.icon !== undefined || $$slots.iconRight !== undefined)
onMount(() => {
if (focus && input) {
@ -95,6 +101,7 @@
class:selected
class:notSelected
disabled={disabled || loading}
class:short
style:width
style:height
{title}
@ -113,7 +120,7 @@
{/if}
{#if loading}
<div class="btn-icon pointer-events-none caption-color spinner" class:resetIconSize>
<Spinner size={iconSize} />
<Spinner size={iconSize === 'inline' ? 'inline' : 'small'} />
</div>
{/if}
{#if label}
@ -121,8 +128,14 @@
<Label {label} params={labelParams} />
</span>
{/if}
{#if iconRight}
<div class="btn-right-icon pointer-events-none" class:resetIconSize>
<Icon bind:icon={iconRight} size={iconRightSize} iconProps={iconRightProps} />
</div>
{/if}
{#if $$slots.icon}<slot name="icon" />{/if}
{#if $$slots.content}<slot name="content" />{/if}
{#if $$slots.iconRight}<slot name="iconRight" />{/if}
</button>
<style lang="scss">
@ -142,9 +155,9 @@
}
}
.medium {
height: 1.75rem;
height: 2rem;
&.only-icon {
width: 1.75rem;
width: 2rem;
}
}
.large {
@ -164,24 +177,33 @@
display: flex;
align-items: center;
flex-shrink: 0;
padding: 0 0.5rem;
padding: 0 0.625rem;
font-weight: 500;
min-width: 1.375rem;
white-space: nowrap;
color: var(--accent-color);
color: var(--theme-caption-color);
background-color: transparent;
border: 1px solid transparent;
transition-property: border, background-color, color, box-shadow;
transition-duration: 0.15s;
.btn-icon {
color: var(--content-color);
color: var(--theme-content-color);
transition: color 0.15s;
pointer-events: none;
}
.btn-right-icon {
margin-left: 0.5rem;
color: var(--theme-halfcontent-color);
transition: color 0.15s;
pointer-events: none;
}
&:not(.only-icon) .btn-icon:not(.spinner) {
margin-right: 0.5rem;
}
&:not(.only-icon) .btn-right-icon {
margin-left: 0.5rem;
}
&.no-border:not(.only-icon) .btn-icon,
&.link-bordered:not(.only-icon) .btn-icon,
&.list:not(.only-icon) .btn-icon,
@ -189,6 +211,9 @@
margin-right: 0.25rem;
}
&.short {
max-width: 7rem;
}
&.sh-no-shape {
border-radius: 0.25rem;
}
@ -217,25 +242,22 @@
}
&.highlight {
box-shadow: inset 0 0 1px 1px var(--primary-bg-color);
box-shadow: inset 0 0 1px 1px var(--primary-button-enabled);
&:hover {
box-shadow: inset 0 0 1px 1px var(--primary-bg-hover);
box-shadow: inset 0 0 1px 1px var(--primary-button-hovered);
}
}
&:hover {
color: var(--accent-color);
transition-duration: 0;
.btn-icon {
color: var(--caption-color);
color: var(--theme-caption-color);
}
}
&:focus {
border-color: var(--primary-edit-border-color) !important;
box-shadow: 0 0 0 2px var(--primary-button-focused-border);
}
&:disabled {
color: rgb(var(--caption-color) / 40%);
color: var(--theme-dark-color);
cursor: not-allowed;
.btn-icon {
@ -254,58 +276,64 @@
}
&.secondary {
background-color: var(--button-bg-color);
border-color: var(--button-border-color);
box-shadow: var(--button-shadow);
background-color: var(--theme-button-enabled);
border-color: var(--theme-button-border);
// &.medium:not(.only-icon) {
// padding: 0 0.75rem;
// }
&:hover {
background-color: var(--button-bg-hover);
border-color: var(--button-border-hover);
background-color: var(--theme-button-hovered);
}
&:active {
background-color: var(--theme-button-pressed);
}
&:focus {
background-color: var(--theme-button-focused);
}
&:disabled {
background-color: var(--button-disabled-color);
border-color: transparent;
background-color: var(--theme-button-disabled);
}
&.selected {
background-color: var(--button-bg-hover);
border-color: var(--button-border-hover);
color: var(--caption-color);
background-color: var(--theme-button-hovered);
.btn-icon {
color: var(--accent-color);
color: var(--theme-caption-color);
}
}
}
&.no-border {
font-weight: 400;
color: var(--accent-color);
background-color: var(--noborder-bg-color);
color: var(--theme-content-color);
background-color: var(--theme-button-enabled);
box-shadow: var(--button-shadow);
&:hover {
color: var(--caption-color);
background-color: var(--noborder-bg-hover);
color: var(--theme-caption-color);
background-color: var(--theme-button-hovered);
.btn-icon {
color: var(--caption-color);
color: var(--theme-caption-color);
}
}
&:disabled {
color: var(--content-color);
background-color: var(--button-disabled-color);
color: var(--theme-trans-color);
background-color: var(--theme-list-button-color);
cursor: default;
.btn-icon {
color: var(--theme-trans-color);
}
&:hover {
color: var(--content-color);
color: var(--theme-trans-color);
.btn-icon {
color: var(--content-color);
color: var(--theme-trans-color);
}
}
}
}
&.transparent {
&:hover {
background-color: var(--highlight-hover);
background-color: var(--theme-button-hovered);
}
&.selected {
background-color: var(--highlight-select);
@ -317,72 +345,79 @@
&.link {
padding: 0 0.875rem;
&:hover {
color: var(--caption-color);
background-color: var(--body-color);
border-color: var(--divider-color);
color: var(--theme-caption-color);
background-color: var(--theme-bg-color);
border-color: var(--theme-divider-color);
.btn-icon {
color: var(--content-color);
color: var(--theme-content-color);
}
}
&:disabled {
color: var(--accent-color);
color: var(--theme-dark-color);
background-color: transparent;
border-color: transparent;
cursor: auto;
.btn-icon {
color: var(--content-color);
color: var(--theme-content-color);
}
}
}
&.link-bordered {
padding: 0 0.375rem;
color: var(--accent-color);
border-color: var(--divider-color);
padding: 0 0.5rem;
color: var(--theme-content-color);
border-color: var(--theme-divider-color);
&:hover {
color: var(--accent-color);
background-color: var(--button-bg-hover);
border-color: var(--button-border-hover);
color: var(--theme-caption-color);
background-color: var(--theme-button-hovered);
border-color: var(--theme-list-divider-color);
.btn-icon {
color: var(--accent-color);
color: var(--theme-caption-color);
}
}
}
&.list {
padding: 0 0.625em 0 0.5rem;
padding: 0 0.625em;
min-height: 1.75rem;
color: var(--content-color);
background-color: var(--body-color);
border: 1px solid var(--divider-color);
border-radius: 3rem;
transition-property: border, color, background-color;
transition-duration: 0.15s;
color: var(--theme-halfcontent-color);
background-color: var(--theme-list-button-color);
border: 1px solid var(--theme-button-border);
border-radius: 1.5rem;
// transition-property: border, color, background-color;
// transition-duration: 0.15s;
.btn-icon {
color: var(--theme-dark-color);
}
&:hover {
color: var(--caption-color);
background-color: var(--board-card-bg-color);
border-color: var(--button-border-color);
color: var(--theme-halfcontent-color);
background-color: var(--theme-list-button-color);
border-color: var(--theme-button-border);
}
&:focus {
box-shadow: none;
}
}
&.primary {
padding: 0 0.75rem;
color: var(--white-color);
background-color: var(--primary-bg-color);
border-color: var(--primary-bg-color);
box-shadow: var(--primary-shadow);
color: var(--primary-button-color);
background-color: var(--primary-button-enabled);
border-color: var(--primary-button-border);
.btn-icon {
color: var(--white-color);
}
&:hover {
background-color: var(--primary-bg-hover);
background-color: var(--primary-button-hovered);
}
&:active {
background-color: var(--primary-button-pressed);
}
&:focus {
border-color: var(--primary-edit-border-color);
background-color: var(--primary-button-focused);
}
&:disabled {
background-color: #5e6ad255;
border-color: #5e6ad255;
background-color: var(--primary-button-disabled);
}
}

View File

@ -17,6 +17,7 @@
export let checked: boolean = false
export let symbol: 'check' | 'minus' = 'check'
export let size: 'small' | 'medium' = 'small'
export let circle: boolean = false
export let primary: boolean = false
export let readonly = false
@ -34,7 +35,7 @@
}
</script>
<label class="checkbox" class:circle class:primary class:readonly class:checked>
<label class="checkbox {size}" class:circle class:primary class:readonly class:checked>
<input class="chBox" disabled={readonly} type="checkbox" bind:checked on:change={handleValueChanged} />
<svg class="checkSVG" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
{#if checked}
@ -53,27 +54,32 @@
display: inline-flex;
justify-content: center;
align-items: center;
width: 0.875rem;
height: 0.875rem;
border: 1px solid var(--dark-color);
background-color: var(--theme-button-hovered);
border: 1px solid var(--theme-checkbox-border);
border-radius: 0.25rem;
&.small {
width: 0.875rem;
height: 0.875rem;
}
&.medium {
width: 1rem;
height: 1rem;
}
&.circle {
width: 1rem;
height: 1rem;
border-radius: 50%;
}
&.checked {
background-color: var(--primary-bg-color);
border-color: transparent;
background-color: var(--theme-checkbox-bg-color);
}
&.primary.checked {
background-color: var(--primary-bg-color);
background-color: var(--primary-button-enabled);
border-color: transparent;
}
&.readonly.checked {
background-color: var(--dark-color);
border-color: transparent;
background-color: var(--theme-checkbox-disabled);
}
.chBox {
@ -89,7 +95,7 @@
&:checked + .checkSVG {
& .check {
visibility: visible;
fill: var(--white-color);
fill: var(--theme-checkbox-color);
&.primary {
fill: var(--primary-button-color);
}
@ -99,7 +105,7 @@
cursor: pointer;
}
&:disabled + .checkSVG .check {
fill: var(--content-color);
fill: var(--theme-checkbox-disabled);
}
}
.checkSVG {
@ -108,7 +114,7 @@
.check {
visibility: hidden;
fill: var(--button-bg-color);
fill: var(--theme-checkbox-color);
}
}
}

View File

@ -15,13 +15,13 @@
<script lang="ts">
import type { Asset, IntlString } from '@hcengineering/platform'
import { translate } from '@hcengineering/platform'
import { createEventDispatcher } from 'svelte'
import { createEventDispatcher, ComponentType } from 'svelte'
import plugin from '../plugin'
import type { AnySvelteComponent } from '../types'
import Icon from './Icon.svelte'
import IconClose from './icons/Close.svelte'
export let icon: Asset | AnySvelteComponent
export let icon: Asset | AnySvelteComponent | ComponentType
export let width: string | undefined = undefined
export let value: string | undefined = undefined
export let placeholder: IntlString = plugin.string.EditBoxPlaceholder
@ -65,9 +65,9 @@
.editbox {
padding: 0 0.5rem 0 0.5rem;
min-width: 10rem;
color: var(--caption-color);
background-color: var(--body-color);
border: 1px solid var(--button-border-color);
color: var(--theme-caption-color);
// background-color: var(--body-color);
border: 1px solid transparent;
border-radius: 0.25rem;
&.small {
@ -77,9 +77,11 @@
height: 2rem;
}
&:focus-within {
border-color: var(--primary-edit-border-color);
border-color: var(--theme-button-border);
box-shadow: 0 0 0 2px var(--primary-button-focused-border);
.icon {
color: var(--menu-icon-hover);
color: var(--theme-dark-color);
}
}
@ -89,19 +91,19 @@
border-radius: 0.25rem;
&::placeholder {
color: var(--content-color);
color: var(--theme-halfcontent-color);
}
}
.btn {
color: var(--content-color);
color: var(--theme-dark-color);
cursor: pointer;
&:hover {
color: var(--caption-color);
color: var(--theme-caption-color);
}
}
.icon {
color: var(--content-color);
color: var(--theme-dark-color);
}
}
</style>

View File

@ -71,8 +71,8 @@
position: relative;
width: 100%;
height: 1.5rem;
background-color: var(--board-bg-color);
border: 1px solid var(--accent-bg-color);
background-color: var(--theme-list-row-color);
border: 1px solid var(--theme-list-divider-color);
border-radius: 0.25rem;
.bar {

View File

@ -50,14 +50,17 @@
closePanel()
}
$: props = $panelstore.panel
$: if (props !== undefined) {
component = undefined
getResource(props.component).then((r) => {
component = r
})
$: if ($panelstore.panel !== undefined) {
if ($panelstore.panel.component === undefined) {
props = $panelstore.panel
} else {
getResource($panelstore.panel.component).then((r) => {
component = r
props = $panelstore.panel
})
}
} else {
props = undefined
}
function escapeClose () {
@ -163,7 +166,7 @@
background-color: transparent;
&.bg {
background-color: var(--body-color);
background-color: var(--theme-back-color);
}
.panel-container {
padding: 0.5rem;

View File

@ -26,6 +26,7 @@
export let autoscroll: boolean = false
export let bottomStart: boolean = false
export let fade: FadeOptions = defaultSP
export let noFade: boolean = false
export let invertScroll: boolean = false
export let horizontal: boolean = false
export let contentDirection: 'vertical' | 'vertical-reverse' | 'horizontal' = 'vertical'
@ -33,6 +34,7 @@
export let buttons: boolean = false
export let shrink: boolean = false
export let divScroll: HTMLElement | undefined = undefined
export let checkForHeaders = false
export function scroll (top: number, left?: number, behavior: 'auto' | 'smooth' = 'auto') {
if (divScroll) {
@ -74,10 +76,13 @@
let timerH: number
const inter = new Set<Element>()
let hasLastCategories: boolean = false
$: fz = $themeOptions.fontSize
$: shiftTop = fade.multipler?.top ? fade.multipler?.top * fz : 0
$: shiftBottom = fade.multipler?.bottom ? fade.multipler?.bottom * fz : 0
$: shiftLeft = fade.multipler?.left ? fade.multipler?.left * fz : 0
$: shiftRight = fade.multipler?.right ? fade.multipler?.right * fz : 0
$: orientir = contentDirection === 'horizontal' ? 'horizontal' : 'vertical'
const checkBar = (): void => {
@ -85,22 +90,42 @@
const trackH = divScroll.clientHeight - shiftTop - shiftBottom - 4
const scrollH = divScroll.scrollHeight
const proc = scrollH / trackH
divBar.style.height = divScroll.clientHeight / proc + 'px'
divBar.style.top = divScroll.scrollTop / proc + shiftTop + 2 + 'px'
if (mask === 'none') divBar.style.visibility = 'hidden'
else {
divBar.style.visibility = 'visible'
const newHeight = divScroll.clientHeight / proc + 'px'
if (divBar.style.height !== 'newHeight') {
divBar.style.height = newHeight
}
const newTop = divScroll.scrollTop / proc + shiftTop + 2 + 'px'
if (divBar.style.top !== newTop) {
divBar.style.top = newTop
}
if (mask === 'none') {
if (divBar.style.visibility !== 'hidden') {
divBar.style.visibility = 'hidden'
}
} else {
if (divBar.style.visibility !== 'visible') {
divBar.style.visibility = 'visible'
}
if (divBar) {
if (timer) {
clearTimeout(timer)
divBar.style.opacity = '1'
if (divBar.style.opacity !== '1') {
divBar.style.opacity = '1'
}
}
timer = setTimeout(() => {
if (divBar) divBar.style.opacity = '0'
if (divBar) {
divBar.style.opacity = '0'
}
}, 1500)
}
}
if (divScroll.clientHeight >= divScroll.scrollHeight) divBar.style.visibility = 'hidden'
if (divScroll.clientHeight >= divScroll.scrollHeight) {
if (divBar.style.visibility !== 'hidden') {
divBar.style.visibility = 'hidden'
}
}
}
}
const checkBarH = (): void => {
@ -183,7 +208,7 @@
}
const renderFade = () => {
if (divScroll) {
if (divScroll && !noFade) {
const th = shiftTop + (topCrop === 'top' ? 2 * fz - topCropValue : 0)
const tf =
topCrop === 'full'
@ -205,15 +230,24 @@
if (divHScroll && horizontal) {
const gradientH = `linear-gradient(
90deg,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 1) ${maskH === 'none' || maskH === 'left' ? '0px' : '2rem'},
rgba(0, 0, 0, 1) calc(100% - ${maskH === 'none' || maskH === 'right' ? '0px' : '2rem'}),
rgba(0, 0, 0, 0) 100%
rgba(0, 0, 0, 1) ${shiftLeft}px,
rgba(0, 0, 0, 0) ${shiftLeft}px,
rgba(0, 0, 0, 1) ${shiftLeft + (maskH === 'both' || maskH === 'right' ? 2 * fz : 0)}px,
rgba(0, 0, 0, 1) calc(100% - ${shiftRight + (maskH === 'both' || maskH === 'left' ? 2 * fz : 0)}px),
rgba(0, 0, 0, 0) calc(100% - ${shiftRight}px),
rgba(0, 0, 0, 1) calc(100% - ${shiftRight}px)
)`
divHScroll.style.webkitMaskImage = gradientH
}
}
let checkBarTimeout: any | undefined = undefined
const delayCall = (op: () => void) => {
clearTimeout(checkBarTimeout)
checkBarTimeout = setTimeout(op, 50)
}
const checkFade = (): void => {
if (divScroll) {
beforeContent = divScroll.scrollTop
@ -234,8 +268,8 @@
if (inter.size) checkIntersectionFade()
renderFade()
}
if (!isScrolling) checkBar()
if (!isScrolling && horizontal) checkBarH()
if (!isScrolling) delayCall(checkBar)
if (!isScrolling && horizontal) delayCall(checkBarH)
}
function checkAutoScroll () {
@ -252,17 +286,47 @@
const checkIntersection = (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
const interArr: Element[] = []
const catEntries: IntersectionObserverEntry[] = []
const lastCatEntries: IntersectionObserverEntry[] = []
entries.forEach((el) => {
if (el.isIntersecting) {
if (el.isIntersecting && el.target.classList.contains('categoryHeader')) {
inter.add(el.target)
interArr.push(el.target)
} else inter.delete(el.target)
if (hasLastCategories) {
if (el.isIntersecting && el.target.classList.contains('categoryHeader')) catEntries.push(el)
if (el.isIntersecting && el.target.classList.contains('lastCat')) lastCatEntries.push(el)
}
})
if (interArr.length > 0) {
dispatch('lastScrolledCategory', interArr[interArr.length - 1]?.getAttribute('id'))
dispatch('firstScrolledCategory', interArr[0]?.getAttribute('id'))
const interCats: string[] = interArr.map((it) => it.getAttribute('id') as string)
dispatch('scrolledCategories', interCats)
}
if (hasLastCategories) {
const targets = new Set<Element>()
const closed = new Set<Element>()
lastCatEntries.forEach((last) => {
catEntries.forEach((cat) => {
if (last.target !== cat.target) {
if (
last.boundingClientRect.top < cat.boundingClientRect.top + 8 &&
last.boundingClientRect.top >= cat.boundingClientRect.top
) {
targets.add(cat.target)
}
if (cat.target.classList.contains('closed') && !closed.has(cat.target)) closed.add(cat.target)
}
})
})
closed.forEach((el) => {
if (!targets.has(el)) el.classList.remove('closed')
})
targets.forEach((el) => el.classList.add('closed'))
}
}
const checkIntersectionFade = () => {
@ -300,8 +364,8 @@
if (divScroll && divBox) {
divScroll.addEventListener('wheel', wheelEvent)
divScroll.addEventListener('scroll', checkFade)
checkBar()
if (horizontal) checkBarH()
delayCall(checkBar)
if (horizontal) delayCall(checkBarH)
}
})
onDestroy(() => {
@ -313,26 +377,46 @@
})
let oldTop: number
beforeUpdate(() => {
if (divBox && divScroll) oldTop = divScroll.scrollTop
})
afterUpdate(() => {
if (divBox && divScroll) {
if (oldTop !== divScroll.scrollTop) divScroll.scrollTop = oldTop
const tempEls = divBox.querySelectorAll('.categoryHeader')
observer = new IntersectionObserver(checkIntersection, { root: null, rootMargin: '0px', threshold: 0.1 })
tempEls.forEach((el) => observer.observe(el))
}
})
if (checkForHeaders) {
beforeUpdate(() => {
if (divBox && divScroll) {
oldTop = divScroll.scrollTop
}
})
afterUpdate(() => {
if (divBox && divScroll) {
if (oldTop !== divScroll.scrollTop) {
divScroll.scrollTop = oldTop
}
delayCall(() => {
const tempEls = divBox.querySelectorAll('.categoryHeader')
observer = new IntersectionObserver(checkIntersection, { root: null, rootMargin: '0px', threshold: 0.1 })
tempEls.forEach((el) => observer.observe(el))
const tempCats = divBox.querySelectorAll('.lastCat')
if (tempCats.length > 0) {
hasLastCategories = true
tempCats.forEach((el) => observer.observe(el))
} else {
hasLastCategories = false
}
})
}
})
}
let divHeight: number
const _resize = (): void => checkFade()
const tapScroll = (n: number, dir: 'up' | 'down') => {
if (divScroll) {
if (orientir === 'horizontal') divScroll.scrollBy({ top: 0, left: dir === 'up' ? -n : n, behavior: 'smooth' })
else divScroll.scrollBy({ top: dir === 'up' ? -n : n, left: 0, behavior: 'smooth' })
if (orientir === 'horizontal') {
divScroll.scrollBy({ top: 0, left: dir === 'up' ? -n : n, behavior: 'smooth' })
} else {
divScroll.scrollBy({ top: dir === 'up' ? -n : n, left: 0, behavior: 'smooth' })
}
}
}
</script>
@ -549,6 +633,7 @@
height: 100%;
}
.scroll {
will-change: opacity;
flex-grow: 1;
min-width: 0;
min-height: 0;

View File

@ -12,7 +12,6 @@
<EditWithIcon
icon={IconSearch}
size={'small'}
width={'12rem'}
placeholder={plugin.string.Search}
bind:value={_search}

View File

@ -78,9 +78,9 @@
word-wrap: none;
font-size: 0.75rem;
color: var(--caption-color);
background: var(--popup-bg-color);
border: 0.5px solid var(--popup-divider);
color: var(--theme-caption-color);
background: var(--theme-list-row-color);
border: 0.5px solid var(--theme-list-divider-color);
box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1);
border-radius: 2.5rem;
// z-index: 1;
@ -92,7 +92,7 @@
transform: translateX(-50%);
}
&:hover {
background: var(--popup-bg-hover);
background: var(--theme-list-button-color);
}
}
</style>

View File

@ -22,7 +22,7 @@
.container {
user-select: none;
font-size: 14px;
color: var(--content-color);
color: var(--theme-content-color);
&.WARNING {
color: yellow;
}

View File

@ -72,14 +72,14 @@
padding: 0;
min-width: 3rem;
height: 3.25rem;
background-color: var(--accent-bg-color);
border: 1px solid var(--button-border-color);
background-color: var(--theme-button-enabled);
border: 1px solid var(--theme-button-border);
border-radius: 0.75rem;
caret-color: var(--caret-color);
&:focus-within {
background-color: var(--body-accent);
border-color: var(--button-border-hover);
background-color: var(--theme-button-focused);
border-color: var(--theme-list-divider-color);
}
input {
height: 3.25rem;
@ -98,7 +98,7 @@
top: 1rem;
left: 1.25rem;
font-size: 0.75rem;
color: var(--caption-color);
color: var(--theme-caption-color);
opacity: 0.3;
transition: top 200ms;
pointer-events: none;

View File

@ -15,7 +15,7 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { tooltip } from '../tooltips'
import type { TabItem } from '../types'
import type { TabItem, IconSize } from '../types'
import Icon from './Icon.svelte'
import Label from './Label.svelte'
@ -23,7 +23,7 @@
export let multiselect: boolean = false
export let items: TabItem[]
export let kind: 'normal' | 'secondary' = 'normal'
export let short: boolean = false
export let onlyIcons: boolean = false
export let size: 'small' | 'medium' = 'medium'
const dispatch = createEventDispatcher()
@ -38,6 +38,9 @@
return res
}
const tabs: HTMLElement[] = []
let iconSize: IconSize
$: iconSize = onlyIcons ? (size === 'small' ? 'small' : 'medium') : size === 'small' ? 'x-small' : 'small'
</script>
{#if items.length > 0}
@ -47,9 +50,10 @@
<div
bind:this={tabs[i]}
class="button"
class:short
class:onlyIcons
class:selected={getSelected(item.id)}
data-view={item.tooltip}
data-id={`tab-${item.id}`}
use:tooltip={{ label: item.tooltip ?? undefined, element: tabs[i] ?? undefined }}
on:click={() => {
if (multiselect) {
@ -64,7 +68,7 @@
>
{#if item.icon}
<div class="icon">
<Icon icon={item.icon} size={size === 'small' ? 'x-small' : 'small'} fill={item.color ?? 'currentColor'} />
<Icon icon={item.icon} size={iconSize} fill={item.color ?? 'currentColor'} />
</div>
{:else if item.color}
<div class="color" style:background-color={item.color} />
@ -110,10 +114,11 @@
}
&::before {
position: absolute;
top: 0.35rem;
top: 50%;
left: -1.5px;
height: 0.8rem;
height: 70%;
border-left: 1px solid var(--button-border-color);
transform: translateY(-50%);
}
}
.button:not(.selected) + .button:not(.selected)::before {
@ -123,6 +128,10 @@
&.small {
.button {
padding: 0 0.5rem;
&.onlyIcons {
padding: 0.375rem;
}
}
&.normal .button {
height: 1.5rem;
@ -132,29 +141,30 @@
}
}
&.medium .button {
height: 1.75rem;
height: 2rem;
padding: 0.25rem 0.75rem;
&.short {
padding: 0.5rem;
&.onlyIcons {
padding: 0.375rem;
}
}
&.normal {
background-color: var(--accent-bg-color);
border-radius: 0.5rem;
background-color: var(--theme-tablist-color);
border-radius: 0.25rem;
.button {
background-color: var(--accent-bg-color);
color: var(--theme-trans-color);
border: 1px solid transparent;
border-radius: calc(0.5rem - 1px);
border-radius: 0.25rem;
&:hover {
background-color: var(--button-bg-hover);
}
&.selected {
color: var(--caption-color);
background-color: var(--button-bg-color);
border-color: var(--button-border-color);
box-shadow: var(--accent-shadow);
color: var(--theme-caption-color);
background-color: var(--theme-button-enabled);
border-color: var(--theme-button-border);
&:hover {
background-color: var(--theme-button-hovered);
}
}
}
}

View File

@ -32,6 +32,17 @@
let time: string = ''
function calculateMonthsPassed (now: number, value: number): number {
const startDate: Date = new Date(value)
const endDate: Date = new Date(now)
const startYear = startDate.getFullYear()
const startMonth = startDate.getMonth()
const endYear = endDate.getFullYear()
const endMonth = endDate.getMonth()
return (endYear - startYear) * 12 + (endMonth - startMonth)
}
async function formatTime (now: number, value: number) {
let passed = now - value
if (passed < 0) passed = 0
@ -42,7 +53,7 @@
} else if (passed < MONTH) {
time = await translate(ui.string.Days, { days: Math.floor(passed / DAY) })
} else if (passed < YEAR) {
time = await translate(ui.string.Months, { months: Math.floor(passed / MONTH) })
time = await translate(ui.string.Months, { months: calculateMonthsPassed(now, value) })
} else {
time = await translate(ui.string.Years, { years: Math.floor(passed / YEAR) })
}

View File

@ -13,21 +13,22 @@
// limitations under the License.
-->
<script lang="ts">
import { IntlString } from '@hcengineering/platform'
import { afterUpdate, createEventDispatcher } from 'svelte'
import ui from '../../plugin'
import ActionIcon from '../ActionIcon.svelte'
import Button from '../Button.svelte'
import Icon from '../Icon.svelte'
import IconClose from '../icons/Close.svelte'
import Label from '../Label.svelte'
import { daysInMonth } from './internal/DateUtils'
import IconClose from '../icons/Close.svelte'
import MonthSquare from './MonthSquare.svelte'
import { daysInMonth } from './internal/DateUtils'
export let currentDate: Date | null
export let withTime: boolean = false
export let mondayStart: boolean = true
export let label = currentDate != null ? ui.string.EditDueDate : ui.string.AddDueDate
export let detail = ui.string.IssueNeedsToBeCompletedByThisDate
export let detail: IntlString | undefined = undefined
const dispatch = createEventDispatcher()
@ -242,8 +243,10 @@
<div class="content">
<div class="label">
<span class="bold"><Label {label} /></span>
<span class="divider">-</span>
<Label label={detail} />
{#if detail}
<span class="divider">-</span>
<Label label={detail} />
{/if}
</div>
<div class="datetime-input">

View File

@ -16,15 +16,16 @@
import type { IntlString } 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 Icon from '../Icon.svelte'
import Label from '../Label.svelte'
import DatePopup from './DatePopup.svelte'
import DPCalendar from './icons/DPCalendar.svelte'
import DPCalendarOver from './icons/DPCalendarOver.svelte'
import { getMonthName } from './internal/DateUtils'
import DatePopup from './DatePopup.svelte'
import { DateRangeMode } from '@hcengineering/core'
export let value: number | null | undefined
export let mode: DateRangeMode = DateRangeMode.DATE
@ -35,10 +36,10 @@
export let labelNull: IntlString = ui.string.NoDate
export let showIcon = true
export let shouldShowLabel: boolean = true
export let size: 'x-small' | 'small' = 'small'
export let kind: 'transparent' | 'primary' | 'link' | 'list' = 'primary'
export let size: ButtonSize | 'x-small' = 'small'
export let kind: ButtonKind = 'link'
export let label = ui.string.DueDate
export let detail = ui.string.IssueNeedsToBeCompletedByThisDate
export let detail = ui.string.NeedsToBeCompletedByThisDate
const dispatch = createEventDispatcher()
@ -64,14 +65,14 @@
</script>
<button
class="datetime-button {kind}"
class="datetime-button {kind} {size}"
class:editable
class:dateTimeButtonNoLabel={!shouldShowLabel}
class:h-6={size === 'small'}
class:h-3={size === 'x-small'}
class:text-xs={size === 'x-small'}
on:click={() => {
on:click={(e) => {
if (editable && !opened) {
e.stopPropagation()
e.preventDefault()
opened = true
showPopup(
DatePopup,
@ -122,10 +123,22 @@
font-weight: 400;
width: auto;
white-space: nowrap;
color: var(--accent-color);
color: var(--theme-content-color);
cursor: default;
&.x-small {
height: 0.75rem;
}
&.small {
height: 1.5rem;
}
&.medium {
height: 2rem;
}
&.large {
height: 2.25rem;
}
&.primary {
padding: 0 0.5rem;
min-width: 1.5rem;
@ -251,6 +264,40 @@
border-color: var(--button-border-color);
}
}
&.link-bordered {
padding: 0 0.375rem;
color: var(--accent-color);
border-color: var(--divider-color);
&:hover {
color: var(--accent-color);
background-color: var(--button-bg-hover);
border-color: var(--button-border-hover);
.btn-icon {
color: var(--accent-color);
}
}
}
&.secondary {
padding: 0 0.625rem;
color: var(--theme-caption-color);
background-color: var(--theme-button-enabled);
border-color: var(--theme-button-border);
border-radius: 0.25rem;
.btn-icon {
color: var(--theme-content-color);
}
&:hover {
background-color: var(--theme-button-hovered);
border-color: var(--theme-divider-color);
.btn-icon {
color: var(--theme-content-color);
}
}
// &.edit {
// padding: 0 0.5rem;
// }
}
.time-divider {
flex-shrink: 0;

View File

@ -32,7 +32,8 @@
export let icon: 'normal' | 'warning' | 'overdue' = 'normal'
export let labelOver: IntlString | undefined = undefined // label instead of date
export let labelNull: IntlString = ui.string.NoDate
export let kind: 'no-border' | 'link' = 'no-border'
export let kind: 'no-border' | 'link' | 'secondary' = 'no-border'
export let size: 'small' | 'medium' | 'large' = 'small'
export let noShift: boolean = false
const dispatch = createEventDispatcher()
@ -315,7 +316,7 @@
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<button
bind:this={datePresenter}
class="datetime-button {kind}"
class="datetime-button {kind} {size}"
class:editable
class:edit
on:click={() => {
@ -455,17 +456,26 @@
padding: 0 0.5rem;
font-weight: 400;
min-width: 1.5rem;
width: auto;
height: 1.5rem;
width: min-content;
white-space: nowrap;
line-height: 1.5rem;
color: var(--accent-color);
color: var(--theme-content-color);
border: 1px solid transparent;
border-radius: 0.25rem;
transition-property: border, background-color, color, box-shadow;
transition-duration: 0.15s;
cursor: pointer;
&.small {
height: 1.5rem;
}
&.medium {
height: 2rem;
}
&.large {
height: 2.25rem;
}
.btn-icon {
margin-right: 0.375rem;
width: 0.875rem;
@ -474,7 +484,7 @@
pointer-events: none;
&.normal {
color: var(--content-color);
color: var(--theme-content-color);
}
&.warning {
color: var(--warning-color);
@ -486,27 +496,31 @@
&.no-border {
font-weight: 400;
color: var(--accent-color);
background-color: var(--noborder-bg-color);
color: var(--theme-content-color);
background-color: var(--theme-button-enabled);
box-shadow: var(--button-shadow);
&:hover {
color: var(--caption-color);
background-color: var(--noborder-bg-hover);
color: var(--theme-caption-color);
background-color: var(--theme-button-hovered);
transition-duration: 0;
.btn-icon {
color: var(--caption-color);
color: var(--theme-caption-color);
}
}
&:disabled {
color: var(--content-color);
background-color: var(--button-disabled-color);
color: var(--theme-trans-color);
background-color: var(--theme-button-disabled);
cursor: default;
.btn-icon {
color: var(--theme-trans-color);
}
&:hover {
color: var(--content-color);
color: var(--theme-trans-color);
.btn-icon {
color: var(--content-color);
color: var(--theme-trans-color);
}
}
}
@ -515,7 +529,7 @@
cursor: pointer;
&:hover {
background-color: var(--noborder-bg-hover);
background-color: var(--theme-button-hovered);
.btn-icon {
&.normal {
color: var(--caption-color);
@ -528,14 +542,14 @@
}
}
.time-divider {
background-color: var(--button-border-hover);
background-color: var(--theme-divider-color);
}
}
&:focus-within {
background-color: var(--button-bg-color);
background-color: var(--theme-button-focused);
border-color: var(--primary-edit-border-color);
&:hover {
background-color: var(--button-bg-color);
background-color: var(--theme-button-hovered);
}
}
}
@ -553,13 +567,12 @@
&.link {
padding: 0 0.875rem;
width: 100%;
height: 2.25rem;
color: var(--caption-color);
color: var(--theme-caption-color);
&:hover {
background-color: var(--body-color);
border-color: var(--divider-color);
background-color: var(--theme-bg-color);
border-color: var(--theme-divider-color);
.btn-icon {
color: var(--content-color);
color: var(--theme-content-color);
}
}
&.edit {
@ -567,6 +580,27 @@
}
}
&.secondary {
padding: 0 0.625rem;
color: var(--theme-caption-color);
background-color: var(--theme-button-enabled);
border-color: var(--theme-button-border);
.btn-icon {
color: var(--theme-content-color);
}
&:hover {
background-color: var(--theme-button-hovered);
border-color: var(--theme-divider-color);
.btn-icon {
color: var(--theme-content-color);
}
}
// &.edit {
// padding: 0 0.5rem;
// }
}
.close-btn {
display: flex;
justify-content: center;
@ -574,15 +608,15 @@
margin: 0 0.25rem;
width: 0.75rem;
height: 0.75rem;
color: var(--content-color);
background-color: var(--button-bg-color);
color: var(--theme-content-color);
background-color: var(--theme-button-enabled);
outline: none;
border-radius: 50%;
cursor: pointer;
&:hover {
color: var(--accent-color);
background-color: var(--button-bg-hover);
background-color: var(--theme-button-hovered);
}
}
@ -614,7 +648,7 @@
width: 1px;
min-width: 1px;
height: 0.75rem;
background-color: var(--button-border-color);
background-color: var(--theme-divider-color);
}
.separator {
margin: 0 0.1rem;

View File

@ -13,8 +13,11 @@
// limitations under the License.
-->
<script lang="ts">
import { Icon, Label, IconDPCalendarOver, IconDPCalendar } from '@hcengineering/ui'
import tracker from '../plugin'
import DPCalendar from './icons/DPCalendar.svelte'
import DPCalendarOver from './icons/DPCalendarOver.svelte'
import ui from '../../plugin'
import Icon from '../Icon.svelte'
import Label from '../Label.svelte'
export let formattedDate: string = ''
export let daysDifference: number = 0
@ -29,18 +32,18 @@
class:mIconContainerWarning={iconModifier === 'warning'}
class:mIconContainerCritical={iconModifier === 'critical' || iconModifier === 'overdue'}
>
<Icon icon={isOverdue ? IconDPCalendarOver : IconDPCalendar} size={'small'} />
<Icon icon={isOverdue ? DPCalendarOver : DPCalendar} size={'small'} />
</div>
<div class="messageContainer">
<div class="title">
<Label
label={isOverdue ? tracker.string.DueDatePopupOverdueTitle : tracker.string.DueDatePopupTitle}
label={isOverdue ? ui.string.DueDatePopupOverdueTitle : ui.string.DueDatePopupTitle}
params={{ value: formattedDate }}
/>
</div>
<div class="description">
<Label
label={isOverdue ? tracker.string.DueDatePopupOverdueDescription : tracker.string.DueDatePopupDescription}
label={isOverdue ? ui.string.DueDatePopupOverdueDescription : ui.string.DueDatePopupDescription}
params={{ value: daysDifference }}
/>
</div>

View File

@ -0,0 +1,89 @@
<!--
// Copyright © 2022 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">
import { Timestamp } from '@hcengineering/core'
import DueDatePopup from './DueDatePopup.svelte'
import { tooltip } from '../../tooltips'
import DatePresenter from './DatePresenter.svelte'
import { getDaysDifference } from './internal/DateUtils'
import { ButtonKind } from '../../types'
export let value: number | null = null
export let shouldRender: boolean = true
export let onChange: (newDate: number | null) => void
export let kind: ButtonKind = 'link'
export let editable: boolean = true
const today = new Date(new Date(Date.now()).setHours(0, 0, 0, 0))
$: isOverdue = value !== null && value < today.getTime()
$: dueDate = value === null ? null : new Date(value)
$: daysDifference = dueDate === null ? null : getDaysDifference(today, dueDate)
$: iconModifier = getDueDateIconModifier(isOverdue, daysDifference)
let formattedDate = getFormattedDate(value)
$: formattedDate = getFormattedDate(value)
function getFormattedDate (value: number | null): string {
return !value ? '' : new Date(value).toLocaleString('default', { month: 'short', day: 'numeric' })
}
const handleDueDateChanged = async (event: CustomEvent<Timestamp>) => {
const newDate = event.detail
if (newDate === undefined || value === newDate || !editable) {
return
}
onChange(newDate)
}
const WARNING_DAYS = 7
const getDueDateIconModifier = (
isOverdue: boolean,
daysDifference: number | null
): 'overdue' | 'critical' | 'warning' | undefined => {
if (isOverdue) {
return 'overdue'
}
if (daysDifference === 0) {
return 'critical'
}
if (daysDifference !== null && daysDifference <= WARNING_DAYS) {
return 'warning'
}
}
</script>
{#if shouldRender}
<div
class="clear-mins"
use:tooltip={formattedDate
? {
direction: 'top',
component: DueDatePopup,
props: {
formattedDate,
daysDifference,
isOverdue,
iconModifier
}
}
: undefined}
>
<DatePresenter {value} {editable} icon={iconModifier} {kind} on:change={handleDueDateChanged} />
</div>
{/if}

View File

@ -87,9 +87,6 @@
grid-auto-columns: max-content;
grid-template-columns: repeat(7, minmax(0, 1fr));
}
.weekend {
background-color: var(--button-bg-color);
}
.cell {
height: calc(100% - 5px);
width: calc(100% - 5px);
@ -98,10 +95,16 @@
cursor: pointer;
}
.cell:hover {
background-color: var(--toggle-bg-hover);
color: var(--primary-button-color);
background-color: var(--highlight-hover);
}
.weekend {
background-color: var(--highlight-select);
&:hover {
background-color: var(--highlight-select-hover);
}
}
.wrongMonth {
color: var(--grayscale-grey-03);
color: var(--theme-trans-color);
}
</style>

View File

@ -43,6 +43,7 @@
{#each [...Array(displayedDaysCount).keys()] as dayOfWeek}
{@const day = getDay(weekMonday, dayOfWeek)}
<th>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="antiTable-cells cursor-pointer uppercase flex-col-center"
class:today={areDatesEqual(todayDate, day)}
@ -88,7 +89,7 @@
width: 5rem;
}
.today {
color: var(--caption-color);
color: var(--theme-caption-color);
}
.calendar-td {
padding: 0;
@ -99,7 +100,7 @@
width: calc(calc(100% - 50px) / 7);
}
.cell:hover:not(.wrongMonth) {
background-color: var(--toggle-bg-hover);
color: var(--primary-button-color);
background-color: var(--highlight-hover);
}
</style>

View File

@ -37,8 +37,8 @@
<div class="year-erp-calendar">
{#each [...Array(12).keys()] as m}
<div class="antiComponentBox flex-grow flex-wrap" style={`min-width: ${minWidth};`}>
{getMonthName(month(currentDate, m))}
<div class="antiComponentBox flex-col flex-grow flex-wrap" style={`min-width: ${minWidth};`}>
<span class="month-caption">{getMonthName(month(currentDate, m))}</span>
<MonthCalendar
{cellHeight}
weekFormat="narrow"
@ -63,4 +63,11 @@
column-gap: 1rem;
border-collapse: collapse;
}
.month-caption {
margin: 0.5rem 0.75rem 0.75rem;
font-weight: 500;
font-size: 0.8125rem;
text-transform: uppercase;
color: var(--theme-dark-color);
}
</style>

View File

@ -20,6 +20,6 @@
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path
d="M12.6429 7.69048L8.92925 11.4041C7.48164 12.8517 5.34347 13.0101 4.16667 11.8333C2.98733 10.654 3.14447 8.52219 4.59216 7.07451L8.00206 3.66461C8.93557 2.73109 10.2976 2.63095 11.0333 3.36667C11.7681 4.10139 11.6658 5.4675 10.7361 6.39727L7.32363 9.8097C6.90202 10.2313 6.32171 10.2741 6.02381 9.97619C5.72651 9.6789 5.76949 9.09718 6.1989 8.66776L9.29048 5.57619C9.56662 5.30005 9.56662 4.85233 9.29048 4.57619C9.01433 4.30005 8.56662 4.30005 8.29048 4.57619L5.1989 7.66776C4.24737 8.6193 4.13865 10.091 5.02381 10.9762C5.9095 11.8619 7.37984 11.7535 8.32363 10.8097L11.7361 7.39727C13.1876 5.94573 13.3564 3.68975 12.0333 2.36667C10.7099 1.04326 8.45782 1.20884 7.00206 2.66461L3.59216 6.07451C1.62229 8.04437 1.39955 11.0662 3.16667 12.8333C4.93146 14.5981 7.9596 14.3737 9.92925 12.4041L13.6429 8.69048C13.919 8.41433 13.919 7.96662 13.6429 7.69048C13.3667 7.41433 12.919 7.41433 12.6429 7.69048Z"
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"
/>
</svg>

View File

@ -0,0 +1,8 @@
<script lang="ts">
export let size: 'small' | 'medium' | 'large'
const fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M10.6572 8.00049L6.82861 4.17187L6.82861 11.8291L10.6572 8.00049Z" />
</svg>

View File

@ -16,6 +16,7 @@
import { getContext } from 'svelte'
import FontSize from './icons/FontSize.svelte'
import { popupstore } from '../../popups'
import { deviceOptionsStore as deviceInfo } from '../..'
const { currentFontSize, setFontSize } = getContext('fontsize') as {
currentFontSize: string
@ -31,6 +32,7 @@
setFontSize(fontsizes[current % fontsizes.length])
$popupstore = $popupstore
}
$: $deviceInfo.fontSize = fontsizes[current % fontsizes.length] === 'normal-font' ? 16 : 14
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->

View File

@ -175,7 +175,7 @@
// min-width: 600px;
font-size: 12px;
line-height: 150%;
background-color: var(--divider-color);
background-color: var(--theme-statusbar-color);
.status-info {
flex-grow: 1;

View File

@ -72,6 +72,7 @@ export { default as TimePopup } from './components/calendar/TimePopup.svelte'
export { default as DateRangePresenter } from './components/calendar/DateRangePresenter.svelte'
export { default as DateTimeRangePresenter } from './components/calendar/DateTimeRangePresenter.svelte'
export { default as DatePresenter } from './components/calendar/DatePresenter.svelte'
export { default as DueDatePresenter } from './components/calendar/DueDatePresenter.svelte'
export { default as DateTimePresenter } from './components/calendar/DateTimePresenter.svelte'
export { default as StylishEdit } from './components/StylishEdit.svelte'
export { default as Grid } from './components/Grid.svelte'
@ -150,6 +151,7 @@ export { default as IconMaxWidth } from './components/icons/MaxWidth.svelte'
export { default as IconMixin } from './components/icons/Mixin.svelte'
export { default as IconCircles } from './components/icons/Circles.svelte'
export { default as IconLike } from './components/icons/Like.svelte'
export { default as IconCollapseArrow } from './components/icons/CollapseArrow.svelte'
export { default as PanelInstance } from './components/PanelInstance.svelte'
export { default as Panel } from './components/Panel.svelte'
@ -205,6 +207,7 @@ export const deviceOptionsStore = writable<DeviceOptions>({
docHeight: 0,
isPortrait: false,
isMobile: false,
fontSize: 0,
minWidth: false,
twoRows: false
})

View File

@ -13,9 +13,10 @@
// limitations under the License.
//
import { writable, derived } from 'svelte/store'
import { Location as PlatformLocation } from './types'
import { derived, writable } from 'svelte/store'
import { closePopup } from './popups'
import justClone from 'just-clone'
import { Location as PlatformLocation } from './types'
export function locationToUrl (location: PlatformLocation): string {
let result = '/'
@ -103,12 +104,23 @@ export function getCurrentLocation (): PlatformLocation {
return parseLocation(window.location)
}
export function getCurrentResolvedLocation (): PlatformLocation {
return justClone(resolvedLocation)
}
const locationWritable = writable(getCurrentLocation())
window.addEventListener('popstate', () => {
locationWritable.set(getCurrentLocation())
})
export const location = derived(locationWritable, (loc) => loc)
export const resolvedLocationStore = writable(getCurrentLocation())
let resolvedLocation = getCurrentLocation()
export function setResolvedLocation (location: PlatformLocation): void {
resolvedLocation = location
resolvedLocationStore.set(justClone(location))
}
export function navigate (location: PlatformLocation, store = true): void {
closePopup()

View File

@ -52,7 +52,7 @@ export const uis = plugin(uiId, {
AddDueDate: '' as IntlString,
EditDueDate: '' as IntlString,
SaveDueDate: '' as IntlString,
IssueNeedsToBeCompletedByThisDate: '' as IntlString,
NeedsToBeCompletedByThisDate: '' as IntlString,
English: '' as IntlString,
Russian: '' as IntlString,
MinutesBefore: '' as IntlString,
@ -70,7 +70,11 @@ export const uis = plugin(uiId, {
DD: '' as IntlString,
MM: '' as IntlString,
YYYY: '' as IntlString,
HH: '' as IntlString
HH: '' as IntlString,
DueDatePopupTitle: '' as IntlString,
DueDatePopupOverdueTitle: '' as IntlString,
DueDatePopupDescription: '' as IntlString,
DueDatePopupOverdueDescription: '' as IntlString
},
metadata: {
DefaultApplication: '' as Metadata<AnyComponent>,

View File

@ -307,7 +307,7 @@ export function fitPopupElement (
newProps.top = `${rect.top}px`
// newProps.bottom = `${Math.min(document.body.clientHeight - rect.bottom + 1, window.innerHeight - rect.top - 1)}px`
newProps.height = `${Math.min(rect.height, window.innerHeight - rect.top)}px`
newProps.left = `${rect.left + 1}px`
newProps.left = `${rect.left}px`
// newProps.right = `${Math.min(document.body.clientWidth - rect.right, window.innerWidth - rect.left - 5)}px`
newProps.width = `${Math.min(rect.width, window.innerWidth - rect.left)}px`
} else if (element === 'middle') {

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
import type { Asset, IntlString } from '@hcengineering/platform'
import { Timestamp } from '@hcengineering/core'
import type { Asset, IntlString } from '@hcengineering/platform'
import { /* Metadata, Plugin, plugin, */ Resource /*, Service */ } from '@hcengineering/platform'
import { /* getContext, */ SvelteComponent } from 'svelte'
@ -28,7 +28,6 @@ export interface Location {
export interface ResolvedLocation {
loc: Location
shouldNavigate: boolean
defaultLocation: Location
}
@ -168,7 +167,7 @@ export type TooltipAlignment = 'top' | 'bottom' | 'left' | 'right'
export type VerticalAlignment = 'top' | 'bottom'
export type HorizontalAlignment = 'left' | 'right'
export type IconSize = 'inline' | 'tiny' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' | 'full'
export type IconSize = 'inline' | 'tiny' | 'x-small' | 'smaller' | 'small' | 'medium' | 'large' | 'x-large' | 'full'
export interface DateOrShift {
date?: number
@ -234,10 +233,10 @@ export interface FadeOptions {
multipler?: Sides<number>
}
export const defaultSP: FadeOptions = { multipler: { top: 0, bottom: 0 } }
export const tableSP: FadeOptions = { multipler: { top: 2.5, bottom: 2.5 } }
export const tableSP: FadeOptions = { multipler: { top: 3, bottom: 2.5 } }
export const topSP: FadeOptions = { multipler: { top: 2.5, bottom: 0 } }
export const tableHRscheduleY: FadeOptions = { multipler: { top: 5, bottom: 0 } }
export const issueSP: FadeOptions = { multipler: { top: 3, bottom: 0 } }
export const issueSP: FadeOptions = { multipler: { top: 2.75, bottom: 0 } }
export const emojiSP: FadeOptions = { multipler: { top: 1.5, bottom: 0 } }
export interface DeviceOptions {
@ -245,6 +244,7 @@ export interface DeviceOptions {
docHeight: number
isPortrait: boolean
isMobile: boolean
fontSize: number
minWidth: boolean
twoRows: boolean
theme?: any

View File

@ -105,3 +105,21 @@ export function tableToCSV (tableId: string, separator = ','): string {
* @public
*/
export const networkStatus = writable<number>(0)
let attractorMx = 0
let attractorMy = 0
/**
* perform mouse movement checks and call method if they was
*/
export function mouseAttractor (op: () => void, diff = 5): (evt: MouseEvent) => void {
return (evt: MouseEvent) => {
const dx = evt.clientX - attractorMx
const dy = evt.clientY - attractorMy
attractorMx = evt.clientX
attractorMy = evt.clientY
if (Math.sqrt(dx * dx + dy * dy) > diff) {
op()
}
}
}

View File

@ -28,6 +28,7 @@
export let object: Doc
export let showCommenInput: boolean = true
export let transparent: boolean = false
export let shouldScroll: boolean = false
getResource(notification.function.GetNotificationClient).then((res) => {
updatesStore = res().docUpdatesStore
@ -83,11 +84,26 @@
let filtered: DisplayTx[] = []
let newTxIndexes: number[] = []
$: newTxIndexes = getNewTxes(filtered, newTxes)
function getNewTxes (filtered: DisplayTx[], newTxes: [Ref<TxCUD<Doc>>, number][]): number[] {
const res: number[] = []
for (let i = 0; i < filtered.length; i++) {
if (isNew(filtered[i], newTxes)) {
res.push(i)
}
}
return res
}
function isNew (tx: DisplayTx | undefined, newTxes: [Ref<TxCUD<Doc>>, number][]): boolean {
if (tx === undefined) return false
const index = newTxes.findIndex((p) => p[0] === tx.originTx._id)
return index !== -1
}
$: scrollIndex = shouldScroll ? newTxIndexes[0] ?? -1 : -1
</script>
<div class="antiSection-header high mt-9" class:invisible={transparent}>
@ -105,7 +121,13 @@
{#if filtered}
<Grid column={1} rowGap={0.75}>
{#each filtered as tx, i}
<TxView {tx} {viewlets} isNew={isNew(tx, newTxes)} isNextNew={isNew(filtered[i + 1], newTxes)} />
<TxView
{tx}
{viewlets}
isNew={newTxIndexes.includes(i)}
isNextNew={newTxIndexes.includes(i + 1)}
shouldScroll={i === scrollIndex}
/>
{/each}
</Grid>
{/if}

View File

@ -40,6 +40,7 @@
import { getValue, TxDisplayViewlet, updateViewlet } from '../utils'
import TxViewTx from './TxViewTx.svelte'
import Edit from './icons/Edit.svelte'
import { tick } from 'svelte'
export let tx: DisplayTx
export let viewlets: Map<ActivityKey, TxViewlet>
@ -47,6 +48,7 @@
export let isNew: boolean = false
export let isNextNew: boolean = false
export let contentHidden: boolean = false
export let shouldScroll: boolean = false
// export let showDocument = false
let ptx: DisplayTx | undefined
@ -87,6 +89,9 @@
modelIcon = result.modelIcon
iconComponent = result.iconComponent
props = getProps(result.props, edit)
if (shouldScroll) {
tick().then(scrollIntoView)
}
}
})
@ -164,10 +169,24 @@
$: withAvatar = isComment || isMentioned || isAttached
$: isEmphasized = viewlet?.display === 'emphasized' || model.every((m) => isMessageType(m.attribute))
$: isColumn = isComment || isEmphasized || hasMessageType
let htmlElement: HTMLDivElement
function scrollIntoView () {
htmlElement?.scrollIntoView({ behavior: 'auto', block: 'start' })
shouldScroll = false
}
</script>
{#if (viewlet !== undefined && !((viewlet?.hideOnRemove ?? false) && tx.removed)) || model.length > 0}
<div class="msgactivity-container" class:showIcon class:withAvatar class:isNew class:isNextNew>
<div
class="msgactivity-container"
bind:this={htmlElement}
class:showIcon
class:withAvatar
class:isNew
class:isNextNew
>
{#if showIcon}
{#if withAvatar}
<div class="msgactivity-avatar">

View File

@ -25,7 +25,7 @@
const dispatch = createEventDispatcher()
const maxLenght: number = 16
const maxLenght: number = 30
const trimFilename = (fname: string): string =>
fname.length > maxLenght ? fname.substr(0, (maxLenght - 1) / 2) + '...' + fname.substr(-(maxLenght - 1) / 2) : fname
@ -83,6 +83,7 @@
class="remove-btn"
on:click={(ev) => {
ev.stopPropagation()
ev.preventDefault()
dispatch('remove')
}}
>

View File

@ -246,9 +246,7 @@
}
</script>
<svelte:window on:paste={pasteAction} />
<div bind:this={refContainer}>
<div bind:this={refContainer} on:paste={pasteAction}>
<input
bind:this={inputFile}
multiple
@ -302,17 +300,17 @@
<style lang="scss">
.list {
padding: 0.5rem;
color: var(--caption-color);
color: var(--theme-caption-color);
overflow-x: auto;
overflow-y: hidden;
background-color: var(--accent-bg-color);
border: 1px solid var(--divider-color);
background-color: var(--theme-refinput-color);
border: 1px solid var(--theme-divider-color);
border-radius: 0.5rem 0.5rem 0 0;
border-bottom: none;
.item + .item {
padding-left: 1rem;
border-left: 1px solid var(--divider-color);
border-left: 1px solid var(--theme-divider-color);
}
}
</style>

View File

@ -298,8 +298,6 @@
}
</script>
<svelte:window on:paste={(ev) => (fakeAttach === 'normal' ? pasteAction(ev) : undefined)} />
<input
bind:this={inputFile}
multiple
@ -312,6 +310,7 @@
<div
class="flex-col clear-mins"
on:paste={(ev) => (fakeAttach === 'normal' ? pasteAction(ev) : undefined)}
on:dragover|preventDefault={() => {}}
on:dragleave={() => {}}
on:drop|preventDefault|stopPropagation={(ev) => {
@ -367,16 +366,16 @@
margin-top: 0.5rem;
padding: 0.5rem;
min-width: 0;
color: var(--caption-color);
color: var(--theme-caption-color);
overflow-x: auto;
overflow-y: hidden;
background-color: var(--accent-bg-color);
border: 1px solid var(--divider-color);
border-radius: 0.5rem;
background-color: var(--theme-button-enabled);
border: 1px solid var(--theme-button-border);
border-radius: 0.25rem;
.item + .item {
padding-left: 1rem;
border-left: 1px solid var(--divider-color);
border-left: 1px solid var(--theme-divider-color);
}
}
</style>

View File

@ -17,7 +17,7 @@
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
import core, { Class, getCurrentAccount, Ref, Space } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import ui, { EditWithIcon, IconSearch, Label, Loading, location, navigate, TabList } from '@hcengineering/ui'
import { ActionIcon, IconMoreH, Label, Loading, location, navigate, TabList, SearchEdit } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { get } from 'svelte/store'
import { dateFileBrowserFilters, FileBrowserSortMode, fileTypeFileBrowserFilters, sortModeToOptionObject } from '..'
@ -101,14 +101,30 @@
</script>
{#if withHeader}
<div class="ac-header full divide">
<div class="ac-header full divide caption-height">
<div class="ac-header__wrap-title">
<span class="ac-header__title"><Label label={attachment.string.FileBrowser} /></span>
</div>
<EditWithIcon icon={IconSearch} size={'small'} bind:value={search} placeholder={ui.string.SearchDots} />
<div class="mb-1 clear-mins">
<TabList
items={[
{ id: 'table', icon: view.icon.Table, tooltip: attachment.string.FileBrowserListView },
{ id: 'card', icon: view.icon.Card, tooltip: attachment.string.FileBrowserGridView }
]}
selected={isListDisplayMode ? 'table' : 'card'}
on:select={(result) => {
if (result.detail !== undefined) isListDisplayMode = result.detail === 'table' ?? false
}}
/>
</div>
</div>
{/if}
<div class="ac-header full">
<div class="ac-header full divide search-start">
<div class="ac-header-full small-gap">
<SearchEdit bind:value={search} on:change={() => {}} />
<ActionIcon icon={IconMoreH} size={'small'} />
<div class="buttons-divider" />
</div>
<FileBrowserFilters
{requestedSpaceClasses}
{spaceId}
@ -117,18 +133,6 @@
bind:selectedDateId
bind:selectedFileTypeId
/>
<TabList
items={[
{ id: 'table', icon: view.icon.Table, tooltip: attachment.string.FileBrowserListView },
{ id: 'card', icon: view.icon.Card, tooltip: attachment.string.FileBrowserGridView }
]}
kind={'secondary'}
size={'small'}
selected={isListDisplayMode ? 'table' : 'card'}
on:select={(result) => {
if (result.detail !== undefined) isListDisplayMode = result.detail === 'table' ?? false
}}
/>
</div>
<div class="group">
<div class="groupHeader">

View File

@ -29,54 +29,48 @@
export let selectedFileTypeId: string
</script>
<div class="filterBlockContainer">
<div class="simpleFilterButton">
<Component
is={contact.component.UserBoxList}
props={{
items: selectedParticipants,
label: attachment.string.FileBrowserFilterFrom
}}
on:update={(evt) => {
selectedParticipants = evt.detail
}}
/>
</div>
<div class="simpleFilterButton">
<SpaceMultiBoxList
_classes={requestedSpaceClasses}
label={attachment.string.FileBrowserFilterIn}
selectedItems={spaceId ? [spaceId] : []}
on:update={(evt) => {
selectedSpaces = evt.detail
}}
/>
</div>
<div class="simpleFilterButton">
<DropdownLabelsIntl
items={dateFileBrowserFilters}
label={attachment.string.FileBrowserFilterDate}
bind:selected={selectedDateId}
/>
</div>
<div class="simpleFilterButton">
<DropdownLabelsIntl
items={fileTypeFileBrowserFilters}
label={attachment.string.FileBrowserFilterFileType}
bind:selected={selectedFileTypeId}
/>
</div>
<div class="filterBlockContainer clear-mins gap-2">
<Component
is={contact.component.UserBoxList}
props={{
items: selectedParticipants,
label: attachment.string.FileBrowserFilterFrom,
kind: 'transparent',
size: 'medium'
}}
on:update={(evt) => {
selectedParticipants = evt.detail
}}
/>
<SpaceMultiBoxList
_classes={requestedSpaceClasses}
label={attachment.string.FileBrowserFilterIn}
selectedItems={spaceId ? [spaceId] : []}
kind={'transparent'}
size={'medium'}
on:update={(evt) => {
selectedSpaces = evt.detail
}}
/>
<DropdownLabelsIntl
items={dateFileBrowserFilters}
label={attachment.string.FileBrowserFilterDate}
bind:selected={selectedDateId}
kind={'transparent'}
size={'medium'}
/>
<DropdownLabelsIntl
items={fileTypeFileBrowserFilters}
label={attachment.string.FileBrowserFilterFileType}
bind:selected={selectedFileTypeId}
kind={'transparent'}
size={'medium'}
/>
</div>
<style lang="scss">
.filterBlockContainer {
display: flex;
flex-flow: row wrap;
margin-top: 10px;
margin-bottom: 10px;
}
.simpleFilterButton {
max-width: 12rem;
margin: 0.125rem 0.5rem 0.125rem 0;
}
</style>

View File

@ -1,10 +1,10 @@
<script lang="ts">
import core, { Ref, Space, WithLookup } from '@hcengineering/core'
import { Button, getCurrentLocation, navigate, location, TabList, Icon } from '@hcengineering/ui'
import { createQuery } from '@hcengineering/presentation'
import board from '../plugin'
import { Button, Icon, TabList, getCurrentResolvedLocation, location, navigate } from '@hcengineering/ui'
import { Viewlet } from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import board from '../plugin'
export let spaceId: Ref<Space> | undefined
export let viewlets: WithLookup<Viewlet>[]
@ -19,7 +19,7 @@
})
function showMenu () {
const loc = getCurrentLocation()
const loc = getCurrentResolvedLocation()
loc.path[4] = space._id
navigate(loc)
}

View File

@ -95,7 +95,7 @@
let kanbanUI: KanbanUI
const listProvider = new ListSelectionProvider((offset: 1 | -1 | 0, of?: Doc, dir?: SelectDirection) => {
kanbanUI.select(offset, of, dir)
kanbanUI?.select(offset, of, dir)
})
onMount(() => {
;(document.activeElement as HTMLElement)?.blur()
@ -125,6 +125,7 @@
...options
}
)
$: listProvider.update(cards)
$: groupByDocs = groupBy(cards, 'state')
const getUpdateProps = (doc: Doc, category: CategoryType): DocumentUpdate<DocWithRank> | undefined => {
@ -151,11 +152,8 @@
getGroupByValues={(groupByDocs, category) => getGroupByValues(groupByDocs, category)}
{setGroupByValues}
categories={states.map((it) => it._id)}
on:content={(evt) => {
listProvider.update(evt.detail)
}}
on:obj-focus={(evt) => {
listProvider.updateFocus(evt.detail)
listProvider.updateFocus(evt.detail.object)
}}
{groupByDocs}
{getUpdateProps}

View File

@ -26,7 +26,8 @@
Scroller,
showPopup,
WeekCalendar,
YearCalendar
YearCalendar,
defaultSP
} from '@hcengineering/ui'
import { BuildModelKey } from '@hcengineering/view'
import { CalendarMode } from '../index'
@ -166,173 +167,166 @@
let indexes = new Map<Ref<Event>, number>()
</script>
<div class="fs-title ml-10 mb-2 flex-row-center">
<div class="text-lg fs-bold px-10 my-4 flex-no-shrink clear-mins">
{label(currentDate, mode)}
</div>
<div class="flex gap-2 mb-4 ml-10">
<Button
size={'small'}
label={calendar.string.ModeDay}
on:click={() => {
mode = CalendarMode.Day
}}
/>
<Button
size={'small'}
label={calendar.string.ModeWeek}
on:click={() => {
mode = CalendarMode.Week
}}
/>
<Button
size={'small'}
label={calendar.string.ModeMonth}
on:click={() => {
mode = CalendarMode.Month
}}
/>
<Button
size={'small'}
label={calendar.string.ModeYear}
on:click={() => {
mode = CalendarMode.Year
}}
/>
<div class="flex ml-4 gap-2">
<div class="flex-between mb-4 px-10 flex-no-shrink clear-mins">
<div class="flex-row-center gap-2">
<Button
icon={IconBack}
size={'small'}
kind={'transparent'}
on:click={() => {
inc(-1)
}}
/>
<Button
size={'small'}
label={calendar.string.Today}
kind={'transparent'}
on:click={() => {
inc(0)
}}
/>
<Button
icon={IconForward}
size={'small'}
kind={'transparent'}
on:click={() => {
inc(1)
}}
/>
</div>
<div class="flex-row-center gap-2 clear-mins">
<Button
label={calendar.string.ModeDay}
on:click={() => {
mode = CalendarMode.Day
}}
/>
<Button
label={calendar.string.ModeWeek}
on:click={() => {
mode = CalendarMode.Week
}}
/>
<Button
label={calendar.string.ModeMonth}
on:click={() => {
mode = CalendarMode.Month
}}
/>
<Button
label={calendar.string.ModeYear}
on:click={() => {
mode = CalendarMode.Year
}}
/>
</div>
</div>
<div class="ml-10 mr-6 h-full clear-mins">
<Scroller
padding={'0 2.25rem'}
fade={mode === CalendarMode.Week || mode === CalendarMode.Day ? { multipler: { top: 3, bottom: 0 } } : defaultSP}
>
{#if mode === CalendarMode.Year}
<Scroller>
<YearCalendar
{mondayStart}
cellHeight={'2.5rem'}
bind:selectedDate
bind:currentDate
on:change={(e) => {
currentDate = e.detail
if (areDatesEqual(selectedDate, currentDate)) {
mode = CalendarMode.Month
}
selectedDate = e.detail
}}
>
<svelte:fragment slot="cell" let:date let:today let:selected let:wrongMonth>
<Day
events={findEvents(objects, date)}
{date}
{_class}
{baseMenuClass}
{options}
{config}
{today}
{selected}
{wrongMonth}
{query}
/>
</svelte:fragment>
</YearCalendar>
</Scroller>
<YearCalendar
{mondayStart}
cellHeight={'2.5rem'}
bind:selectedDate
bind:currentDate
on:change={(e) => {
currentDate = e.detail
if (areDatesEqual(selectedDate, currentDate)) {
mode = CalendarMode.Month
}
selectedDate = e.detail
}}
>
<svelte:fragment slot="cell" let:date let:today let:selected let:wrongMonth>
<Day
events={findEvents(objects, date)}
{date}
{_class}
{baseMenuClass}
{options}
{config}
{today}
{selected}
{wrongMonth}
{query}
/>
</svelte:fragment>
</YearCalendar>
{:else if mode === CalendarMode.Month}
<div class="flex flex-grow">
<MonthCalendar {mondayStart} cellHeight={'8.5rem'} bind:selectedDate bind:currentDate>
<svelte:fragment slot="cell" let:date let:today let:selected let:wrongMonth>
<Day
events={findEvents(objects, date)}
{date}
size={'huge'}
{_class}
{baseMenuClass}
{options}
{config}
{today}
{selected}
{wrongMonth}
{query}
on:select={(e) => {
currentDate = e.detail
if (areDatesEqual(selectedDate, currentDate)) {
mode = CalendarMode.Day
}
selectedDate = e.detail
}}
on:create={(e) => {
showCreateDialog(e.detail, false)
}}
/>
</svelte:fragment>
</MonthCalendar>
</div>
<MonthCalendar {mondayStart} cellHeight={'8.5rem'} bind:selectedDate bind:currentDate>
<svelte:fragment slot="cell" let:date let:today let:selected let:wrongMonth>
<Day
events={findEvents(objects, date)}
{date}
size={'huge'}
{_class}
{baseMenuClass}
{options}
{config}
{today}
{selected}
{wrongMonth}
{query}
on:select={(e) => {
currentDate = e.detail
if (areDatesEqual(selectedDate, currentDate)) {
mode = CalendarMode.Day
}
selectedDate = e.detail
}}
on:create={(e) => {
showCreateDialog(e.detail, false)
}}
/>
</svelte:fragment>
</MonthCalendar>
{:else if mode === CalendarMode.Week}
<Scroller>
<WeekCalendar
{mondayStart}
cellHeight={'4.5rem'}
bind:selectedDate
bind:currentDate
on:select={(e) => {
currentDate = e.detail
selectedDate = e.detail
mode = CalendarMode.Day
}}
>
<svelte:fragment slot="cell" let:date>
<Hour
events={findEvents(objects, date, true)}
{date}
bind:indexes
on:create={(e) => {
showCreateDialog(e.detail, true)
}}
/>
</svelte:fragment>
</WeekCalendar>
</Scroller>
<WeekCalendar
{mondayStart}
cellHeight={'4.5rem'}
bind:selectedDate
bind:currentDate
on:select={(e) => {
currentDate = e.detail
selectedDate = e.detail
mode = CalendarMode.Day
}}
>
<svelte:fragment slot="cell" let:date>
<Hour
events={findEvents(objects, date, true)}
{date}
bind:indexes
on:create={(e) => {
showCreateDialog(e.detail, true)
}}
/>
</svelte:fragment>
</WeekCalendar>
{:else if mode === CalendarMode.Day}
<Scroller>
<WeekCalendar
{mondayStart}
displayedDaysCount={1}
startFromWeekStart={false}
cellHeight={'4.5rem'}
bind:selectedDate
bind:currentDate
>
<svelte:fragment slot="cell" let:date>
<Hour
events={findEvents(objects, date, true)}
{date}
bind:indexes
wide
on:create={(e) => {
showCreateDialog(e.detail, true)
}}
/>
</svelte:fragment>
</WeekCalendar>
</Scroller>
<WeekCalendar
{mondayStart}
displayedDaysCount={1}
startFromWeekStart={false}
cellHeight={'4.5rem'}
bind:selectedDate
bind:currentDate
>
<svelte:fragment slot="cell" let:date>
<Hour
events={findEvents(objects, date, true)}
{date}
bind:indexes
wide
on:create={(e) => {
showCreateDialog(e.detail, true)
}}
/>
</svelte:fragment>
</WeekCalendar>
{/if}
</div>
</Scroller>
<div class="min-h-4 max-h-4 h-4 flex-no-shrink" />

View File

@ -104,6 +104,8 @@
labelNull={ui.string.SelectDate}
on:change={async (event) => await handleNewStartDate(event.detail)}
mode={DateRangeMode.DATETIME}
kind={'secondary'}
size={'large'}
editable
/>
<DateRangePresenter
@ -112,8 +114,10 @@
labelNull={calendar.string.DueTo}
on:change={async (event) => await handleNewDueDate(event.detail)}
mode={DateRangeMode.DATETIME}
kind={'secondary'}
size={'large'}
editable
/>
<UserBoxList bind:items={participants} label={calendar.string.Participants} />
<UserBoxList bind:items={participants} label={calendar.string.Participants} kind={'secondary'} size={'large'} />
</svelte:fragment>
</Card>

View File

@ -72,7 +72,14 @@
<EditBox bind:value={title} placeholder={calendar.string.Title} kind={'large-style'} focus />
<svelte:fragment slot="pool">
<!-- <TimeShiftPicker title={calendar.string.Date} bind:value direction="after" /> -->
<DateRangePresenter bind:value mode={DateRangeMode.DATETIME} editable={true} labelNull={ui.string.SelectDate} />
<UserBoxList bind:items={participants} label={calendar.string.Participants} />
<DateRangePresenter
bind:value
mode={DateRangeMode.DATETIME}
editable={true}
labelNull={ui.string.SelectDate}
kind={'secondary'}
size={'large'}
/>
<UserBoxList bind:items={participants} label={calendar.string.Participants} kind={'secondary'} size={'large'} />
</svelte:fragment>
</Card>

View File

@ -21,13 +21,14 @@
AnyComponent,
Button,
Component,
Icon,
IconAdd,
ActionIcon,
Label,
Loading,
SearchEdit,
showPopup,
TabList
TabList,
IconMoreH,
IconAdd
} from '@hcengineering/ui'
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
import {
@ -38,7 +39,7 @@
viewOptionStore
} from '@hcengineering/view-resources'
import calendar from '../plugin'
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
// import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
export let _class: Ref<Class<Event>> = calendar.class.Event
export let space: Ref<Space> | undefined = undefined
@ -107,45 +108,42 @@
}
})
$: twoRows = $deviceInfo.twoRows
// $: twoRows = $deviceInfo.twoRows
$: viewOptions = getViewOptions(selectedViewlet, $viewOptionStore)
</script>
<div class="ac-header withSettings" class:full={!twoRows} class:mini={twoRows}>
<div class:ac-header-full={!twoRows} class:flex-between={twoRows}>
<div class="ac-header__wrap-title mr-3">
<div class="ac-header__icon"><Icon icon={viewIcon} size={'small'} /></div>
<span class="ac-header__title"><Label label={viewLabel} /></span>
<div class="ml-4"><FilterButton {_class} /></div>
</div>
<SearchEdit
bind:value={search}
on:change={() => {
updateResultQuery(search)
}}
/>
<div class="ac-header full divide">
<div class="ac-header__wrap-title mr-3">
<span class="ac-header__title"><Label label={viewLabel} /></span>
</div>
<div class="ac-header-full" class:secondRow={twoRows}>
<Button icon={IconAdd} label={createLabel} kind={'primary'} size={'small'} on:click={showCreateDialog} />
<div class="ac-header-full medium-gap mb-1">
{#if viewlets.length > 1}
<TabList
items={viewslist}
multiselect={false}
selected={selectedViewlet?._id}
kind={'secondary'}
size={'small'}
on:select={(result) => {
if (result.detail !== undefined) selectedViewlet = viewlets.find((vl) => vl._id === result.detail.id)
}}
/>
{/if}
<ViewletSettingButton bind:viewOptions viewlet={selectedViewlet} />
<Button icon={IconAdd} label={createLabel} kind={'primary'} on:click={showCreateDialog} />
</div>
</div>
<div class="ac-header full divide search-start">
<div class="ac-header-full small-gap">
<SearchEdit bind:value={search} on:change={() => updateResultQuery(search)} />
<ActionIcon icon={IconMoreH} size={'small'} />
<div class="buttons-divider" />
<FilterButton {_class} />
</div>
<div class="ac-header-full medium-gap">
<ViewletSettingButton bind:viewOptions viewlet={selectedViewlet} />
<ActionIcon icon={IconMoreH} size={'small'} />
</div>
</div>
{#if selectedViewlet?.$lookup?.descriptor?.component}
{#if loading}
<Loading />

View File

@ -198,6 +198,7 @@
</script>
<div class="flex-col vScroll" bind:this={div} on:scroll={handleScroll}>
<div class="grower" />
{#if showFixed}
<div class="ml-2 pr-2 fixed">
<JumpToDateSelector {selectedDate} fixed on:jumpToDate={handleJumpToDate} />
@ -224,6 +225,10 @@
</div>
<style lang="scss">
.grower {
flex-grow: 10;
flex-shrink: 5;
}
.fixed {
position: absolute;
align-self: center;

View File

@ -38,7 +38,7 @@
}
</script>
<div class="ac-header divide full">
<div class="ac-header divide full caption-height">
{#if channel}
<Header
icon={channel.private ? Lock : classIcon(client, channel._class)}

View File

@ -48,7 +48,7 @@
</script>
<div class="flex-col h-full">
<div class="ac-header divide full">
<div class="ac-header divide full caption-height">
<Header icon={workbench.icon.Search} intlLabel={plugin.string.ChunterBrowser} />
</div>
<div class="h-full browser">

View File

@ -54,10 +54,11 @@
}
</script>
<div class="ac-header divide full">
<div class="ac-header divide full caption-height">
{#if dm}
{#await getDmName(client, dm) then name}
{#await getEmpolyeeIds() then empolyeeIds}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="ac-header__wrap-title" on:click={onSpaceEdit}>
<div class="ac-header__icon">
<CombineAvatars _class={contact.class.Employee} items={empolyeeIds} size={'x-small'} />

View File

@ -18,8 +18,8 @@
import { AttachmentPresenter, FileDownload } from '@hcengineering/attachment-resources'
import { ChunterSpace } from '@hcengineering/chunter'
import { Doc, SortingOrder, getCurrentAccount } from '@hcengineering/core'
import { createQuery, getFileUrl, getClient } from '@hcengineering/presentation'
import { getCurrentLocation, showPopup, IconMoreV, Label, navigate, Icon, Menu } from '@hcengineering/ui'
import { createQuery, getClient, getFileUrl } from '@hcengineering/presentation'
import { Icon, IconMoreV, Label, Menu, getCurrentResolvedLocation, navigate, showPopup } from '@hcengineering/ui'
export let channel: ChunterSpace | undefined
const myAccId = getCurrentAccount()._id
@ -98,7 +98,7 @@
<div
class="showMoreAttachmentsButton"
on:click={() => {
const loc = getCurrentLocation()
const loc = getCurrentResolvedLocation()
loc.path[3] = 'fileBrowser'
loc.query = channel ? { spaceId: channel._id } : {}
navigate(loc)

View File

@ -28,6 +28,7 @@
</script>
<div class="ac-header__wrap-description">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="ac-header__wrap-title" on:click>
{#if icon}<div class="ac-header__icon"><Icon {icon} size={'small'} /></div>{/if}
{#if label}

View File

@ -15,11 +15,11 @@
$: isCurrentYear = time ? new Date(time).getFullYear() === new Date().getFullYear() : undefined
</script>
<div id={fixed ? '' : time?.toString()} class="flex justify-center mt-5 pr-1 dateSelector">
<div id={fixed ? '' : time?.toString()} class="flex-center clear-mins dateSelector">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
bind:this={div}
class="mb-1 p-1 border-radius-2 over-underline dateSelectorButton"
class="border-radius-4 over-underline dateSelectorButton clear-mins"
on:click={() => {
showPopup(DateRangePopup, {}, div, (v) => {
if (v) {
@ -30,28 +30,37 @@
}}
>
{#if time}
<div>
{new Date(time).toLocaleDateString('default', {
weekday: 'short',
month: 'long',
day: 'numeric',
year: isCurrentYear ? undefined : 'numeric'
})}
</div>
{new Date(time).toLocaleDateString('default', {
weekday: 'short',
month: 'long',
day: 'numeric',
year: isCurrentYear ? undefined : 'numeric'
})}
{/if}
</div>
</div>
<style lang="scss">
.dateSelector {
&:not(:first-child) {
border-top: 1px solid var(--divider-color);
position: relative;
flex-shrink: 0;
margin: 0.25rem 0;
&:not(:first-child)::after {
position: absolute;
content: '';
top: 50%;
left: 0;
width: 100%;
height: 1px;
background-color: var(--theme-divider-color);
}
.dateSelectorButton {
padding: 0.25rem 0.5rem;
height: max-content;
background-color: var(--theme-list-row-color);
border: 1px solid var(--theme-divider-color);
z-index: 10;
}
}
.dateSelectorButton {
margin-top: -1rem;
background-color: var(--body-color);
border: 1px solid var(--divider-color);
}
</style>

View File

@ -221,10 +221,10 @@
let loading = false
</script>
<div class="container" class:highlighted={isHighlighted} id={message._id}>
<div class="container clear-mins" class:highlighted={isHighlighted} id={message._id}>
<div class="avatar"><Avatar size={'medium'} avatar={employee?.avatar} /></div>
<div class="message">
<div class="header">
<div class="message clear-mins">
<div class="header clear-mins">
{#if employee}
<EmployeePresenter value={employee} shouldShowAvatar={false} inline />
{/if}
@ -270,7 +270,7 @@
</div>
{/if}
</div>
<div class="buttons" class:menuShowed>
<div class="buttons clear-mins" class:menuShowed>
<div class="tool">
<ActionIcon
icon={IconMoreH}
@ -305,6 +305,7 @@
.container {
position: relative;
display: flex;
flex-shrink: 0;
padding: 0.5rem 2rem;
&.highlighted {
@ -327,7 +328,7 @@
font-weight: 500;
font-size: 1rem;
line-height: 150%;
color: var(--caption-color);
color: var(--theme-caption-color);
margin-bottom: 0.25rem;
span {
@ -379,7 +380,7 @@
}
&:hover {
background-color: var(--board-card-bg-hover);
background-color: var(--highlight-hover);
}
}
</style>

View File

@ -85,7 +85,7 @@
}
</script>
<div class="ac-header full divide">
<div class="ac-header full divide caption-height">
<div class="ac-header__wrap-title">
<span class="ac-header__title"><Label label={chunter.string.SavedItems} /></span>
</div>
@ -93,7 +93,8 @@
<Scroller>
{#if savedMessages.length > 0 || savedAttachments.length > 0}
{#each savedMessages as message}
<div on:click={() => openMessageFromSpecial(message)}>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="clear-mins flex-no-shrink" on:click={() => openMessageFromSpecial(message)}>
<Message
{message}
on:openThread
@ -105,7 +106,8 @@
</div>
{/each}
{#each savedAttachments as att}
<div class="attachmentContainer" on:click={() => openAttachment(att)}>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="attachmentContainer flex-no-shrink clear-mins" on:click={() => openAttachment(att)}>
<AttachmentPreview value={att} isSaved={true} />
<div class="label">
<Label

View File

@ -163,7 +163,7 @@
let loading = false
</script>
<div class="ml-8 mt-4">
<div class="flex-col ml-8 mt-4 flex-no-shrink">
{#if parent}
{#await getChannel(parent.space) then channel}
{#if channel?._class === chunter.class.Channel}
@ -178,13 +178,13 @@
{/await}
{/if}
</div>
<div class="flex-col content">
<div class="flex-col content flex-no-shrink">
{#if parent}
<MsgView message={parent} thread {savedAttachmentsIds} />
{#if total > comments.length}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="label pb-2 pt-2 pl-8 over-underline"
class="label pb-2 pt-2 pl-8 over-underline clear-mins"
on:click={() => {
showAll = true
}}
@ -195,7 +195,7 @@
{#each comments as comment (comment._id)}
<MsgView message={comment} thread {savedAttachmentsIds} />
{/each}
<div class="mr-4 ml-4 mb-4 mt-2">
<div class="mr-4 ml-4 pb-4 mt-2 clear-mins">
<AttachmentRefInput
space={parent.space}
_class={chunter.class.ThreadMessage}
@ -206,17 +206,19 @@
</div>
{/if}
</div>
<div class="min-h-4 max-h-4 h-4 flex-no-shrink" />
<style lang="scss">
.content {
overflow: hidden;
margin: 1rem 1rem 0px;
background-color: var(--board-bg-color);
border: 1px solid var(--divider-color);
margin: 1rem 1rem 0;
padding-top: 0.5rem;
background-color: var(--theme-list-row-color);
border: 1px solid var(--theme-divider-color);
border-radius: 0.75rem;
}
.label:hover {
background-color: var(--board-card-bg-hover);
background-color: var(--theme-button-hovered);
}
</style>

View File

@ -16,11 +16,11 @@
import attachment, { Attachment } from '@hcengineering/attachment'
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
import type { ChunterMessage, Message, ThreadMessage } from '@hcengineering/chunter'
import core, { generateId, getCurrentAccount, Ref, Space } from '@hcengineering/core'
import core, { Ref, Space, generateId, getCurrentAccount } from '@hcengineering/core'
import { LastView } from '@hcengineering/notification'
import { NotificationClientImpl } from '@hcengineering/notification-resources'
import { createQuery, getClient } from '@hcengineering/presentation'
import { getCurrentLocation, IconClose, Label, navigate } from '@hcengineering/ui'
import { IconClose, Label, getCurrentResolvedLocation, navigate } from '@hcengineering/ui'
import { afterUpdate, beforeUpdate, createEventDispatcher } from 'svelte'
import { createBacklinks } from '../backlinks'
import chunter from '../plugin'
@ -81,7 +81,7 @@
message = res[0]
if (!message) {
const loc = getCurrentLocation()
const loc = getCurrentResolvedLocation()
loc.path.length = 4
navigate(loc)
}

View File

@ -49,19 +49,16 @@
})
</script>
<div class="ac-header full divide">
<div class="ac-header full divide caption-height">
<div class="ac-header__wrap-title">
<span class="ac-header__title"><Label label={chunter.string.Threads} /></span>
</div>
</div>
<Scroller>
{#each threads as thread (thread)}
<div class="item"><Thread _id={thread} {savedAttachmentsIds} /></div>
{#each threads as thread, i (thread)}
<Thread _id={thread} {savedAttachmentsIds} />
{#if i < threads.length - 1}
<div class="antiDivider" />
{/if}
{/each}
</Scroller>
<style lang="scss">
.item + .item {
margin-top: 3rem;
}
</style>

View File

@ -4,7 +4,14 @@ import { employeeByIdStore } from '@hcengineering/contact-resources'
import { Class, Client, Doc, getCurrentAccount, IdMap, Obj, Ref, Space, Timestamp } from '@hcengineering/core'
import { Asset } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { getCurrentLocation, getPanelURI, location, Location, navigate, ResolvedLocation } from '@hcengineering/ui'
import {
getPanelURI,
location,
Location,
navigate,
ResolvedLocation,
getCurrentResolvedLocation
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import { workbenchId } from '@hcengineering/workbench'
import { get, Unsubscriber, writable } from 'svelte/store'
@ -133,14 +140,14 @@ export function scrollAndHighLight (): void {
export async function getLink (doc: Doc): Promise<string> {
const fragment = await getTitle(doc)
const location = getCurrentLocation()
const location = getCurrentResolvedLocation()
return await Promise.resolve(
`${window.location.protocol}//${window.location.host}/${workbenchId}/${location.path[1]}/${chunterId}#${fragment}`
)
}
export async function getFragment (doc: Doc): Promise<Location> {
const loc = getCurrentLocation()
const loc = getCurrentResolvedLocation()
loc.path.length = 2
loc.fragment = undefined
loc.query = undefined
@ -213,7 +220,6 @@ async function generateLocation (loc: Location, shortLink: string): Promise<Reso
path: [appComponent, workspace, chunterId, doc.space],
fragment: doc._id
},
shouldNavigate: true,
defaultLocation: {
path: [appComponent, workspace, chunterId, doc.space],
fragment: doc._id
@ -230,7 +236,6 @@ async function generateLocation (loc: Location, shortLink: string): Promise<Reso
path: [appComponent, workspace],
fragment: getPanelURI(component, comment.attachedTo, comment.attachedToClass, 'content')
},
shouldNavigate: false,
defaultLocation: {
path: [appComponent, workspace],
fragment: getPanelURI(component, comment.attachedTo, comment.attachedToClass, 'content')
@ -244,7 +249,6 @@ async function generateLocation (loc: Location, shortLink: string): Promise<Reso
path: [appComponent, workspace, chunterId, doc.space, msg.attachedTo],
fragment: doc._id
},
shouldNavigate: true,
defaultLocation: {
path: [appComponent, workspace, chunterId, doc.space],
fragment: doc._id

View File

@ -16,7 +16,7 @@
"SelectFolder": "Select folder",
"OrganizationsFolder": "Companies folder",
"PersonsFolder": "Persons folder",
"ContactCreateLabel": "Contact",
"ContactCreateLabel": "Create contact",
"SearchEmployee": "Search for employee...",
"SearchPerson": "Search for person...",
"SearchOrganization": "Search for company...",

View File

@ -16,7 +16,7 @@
"SelectFolder": "Выбрать папку",
"OrganizationsFolder": "Папка с компаниями",
"PersonsFolder": "Папка с людьми",
"ContactCreateLabel": "Контакт",
"ContactCreateLabel": "Создать контакт",
"SearchEmployee": "Поиск сотрудника...",
"SearchPerson": "Поиск персоны...",
"SearchOrganization": "Поиск компании...",

View File

@ -153,7 +153,7 @@
<div class="flex-presenter">
{#if icon}
<div class="icon" class:small-gap={size === 'inline' || size === 'small'}>
<Icon {icon} size={kind === 'link' ? 'small' : size} />
<Icon {icon} size={kind === 'link' || kind === 'secondary' ? 'small' : size} />
</div>
{/if}
<div class="label no-underline">

View File

@ -126,6 +126,10 @@
width: 1.5rem; // 24
height: 1.5rem;
}
.ava-smaller {
width: 1.75rem; // 32
height: 1.75rem;
}
.ava-small {
width: 2rem; // 32
height: 2rem;
@ -157,6 +161,8 @@
.ava-inline.no-img,
.ava-x-small .ava-mask,
.ava-x-small.no-img,
.ava-smaller .ava-mask,
.ava-smaller.no-img,
.ava-small .ava-mask,
.ava-small.no-img,
.ava-medium .ava-mask,

View File

@ -16,7 +16,7 @@
<script lang="ts">
import { Doc, DocumentQuery } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@hcengineering/ui'
import { Button, Label, Loading, SearchEdit, showPopup, IconMoreH, ActionIcon } from '@hcengineering/ui'
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
import {
ActionContext,
@ -30,7 +30,7 @@
} from '@hcengineering/view-resources'
import contact from '../plugin'
import CreateContact from './CreateContact.svelte'
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
// import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
let search = ''
let searchQuery: DocumentQuery<Doc> = {}
@ -74,7 +74,7 @@
showPopup(CreateContact, { space: contact.space.Contacts, targetElement: ev.target }, ev.target as HTMLElement)
}
$: twoRows = $deviceInfo.twoRows
// $: twoRows = $deviceInfo.twoRows
$: viewOptions = getViewOptions(viewlet, $viewOptionStore)
</script>
@ -85,30 +85,29 @@
}}
/>
<div class="antiPanel-component">
<div class="ac-header withSettings" class:full={!twoRows} class:mini={twoRows}>
<div class:ac-header-full={!twoRows} class:flex-between={twoRows}>
<div class="ac-header__wrap-title mr-3">
<div class="ac-header__icon"><Icon icon={contact.icon.Person} size={'small'} /></div>
<span class="ac-header__title"><Label label={contact.string.Contacts} /></span>
<div class="ml-4"><FilterButton _class={contact.class.Contact} /></div>
</div>
<SearchEdit
bind:value={search}
on:change={() => {
updateResultQuery(search)
}}
/>
<div class="ac-header full divide">
<div class="ac-header__wrap-title mr-3">
<span class="ac-header__title"><Label label={contact.string.Contacts} /></span>
</div>
<div class="ac-header-full" class:secondRow={twoRows}>
<div class="mb-1 clear-mins">
<Button
icon={IconAdd}
label={contact.string.ContactCreateLabel}
kind={'primary'}
size={'small'}
size={'medium'}
on:click={(ev) => showCreateDialog(ev)}
/>
</div>
</div>
<div class="ac-header full divide search-start">
<div class="ac-header-full small-gap">
<SearchEdit bind:value={search} on:change={() => updateResultQuery(search)} />
<ActionIcon icon={IconMoreH} size={'small'} />
<div class="buttons-divider" />
<FilterButton _class={contact.class.Contact} />
</div>
<div class="ac-header-full medium-gap">
<ViewletSettingButton bind:viewOptions {viewlet} />
<ActionIcon icon={IconMoreH} size={'small'} />
</div>
</div>

View File

@ -169,7 +169,8 @@
<ChannelsDropdown
bind:value={channels}
focusIndex={10}
kind={'no-border'}
kind={'secondary'}
size={'large'}
editable
restricted={[contact.channelProvider.Email]}
/>

View File

@ -103,6 +103,8 @@
<ChannelsDropdown
bind:value={channels}
focusIndex={10}
kind={'secondary'}
size={'large'}
editable
highlighted={matchedChannels.map((it) => it.provider)}
/>

View File

@ -120,6 +120,6 @@
</div>
</div>
<svelte:fragment slot="pool">
<ChannelsDropdown bind:value={channels} focusIndex={10} kind={'no-border'} editable />
<ChannelsDropdown bind:value={channels} focusIndex={10} kind={'secondary'} size={'large'} editable />
</svelte:fragment>
</Card>

View File

@ -12,6 +12,8 @@
export let kind: ButtonKind = 'link'
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
export let onChange: ((value: Ref<Employee>) => void) | undefined = undefined
export let colorInherit: boolean = false
export let accent: boolean = false
export let inline = false
$: employee = value ? $employeeByIdStore.get(value) : undefined
@ -47,7 +49,9 @@
shouldShowPlaceholder
defaultName={contact.string.NotSpecified}
shouldShowName={kind !== 'list'}
avatarSize={kind === 'list-header' ? 'small' : 'x-small'}
avatarSize={kind === 'list-header' ? 'smaller' : 'x-small'}
disableClick
{colorInherit}
{accent}
/>
{/if}

View File

@ -2,6 +2,7 @@
import { Employee } from '@hcengineering/contact'
import { WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { IconSize } from '@hcengineering/ui'
import { PersonLabelTooltip } from '..'
import PersonPresenter from '../components/PersonPresenter.svelte'
import contact from '../plugin'
@ -12,10 +13,12 @@
export let shouldShowName: boolean = true
export let shouldShowPlaceholder = false
export let onEmployeeEdit: ((event: MouseEvent) => void) | undefined = undefined
export let avatarSize: 'inline' | 'tiny' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' = 'x-small'
export let avatarSize: IconSize = 'x-small'
export let isInteractive = true
export let inline = false
export let disableClick = false
export let colorInherit: boolean = false
export let accent: boolean = false
export let defaultName: IntlString | undefined = undefined
export let element: HTMLElement | undefined = undefined
</script>
@ -31,6 +34,8 @@
{shouldShowPlaceholder}
isInteractive={isInteractive && !disableClick}
{inline}
{colorInherit}
{accent}
{defaultName}
statusLabel={value?.active === false && shouldShowName ? contact.string.Inactive : undefined}
/>

View File

@ -9,17 +9,19 @@
export let kind: ButtonKind = 'link'
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
export let onChange: ((value: Ref<Employee>) => void) | undefined = undefined
export let colorInherit: boolean = false
export let accent: boolean = false
export let inline = false
</script>
{#if Array.isArray(value)}
<div class="inline-content">
{#each value as employee}
<EmployeeAttributePresenter value={employee} {kind} {tooltipLabels} {onChange} {inline} />
<EmployeeAttributePresenter value={employee} {kind} {tooltipLabels} {onChange} {inline} {colorInherit} {accent} />
{/each}
</div>
{:else}
<EmployeeAttributePresenter {value} {kind} {tooltipLabels} {onChange} {inline} />
<EmployeeAttributePresenter {value} {kind} {tooltipLabels} {onChange} {inline} {colorInherit} {accent} />
{/if}
<style lang="scss">

View File

@ -16,6 +16,7 @@
import { Employee, getName, Person } from '@hcengineering/contact'
import { IntlString } from '@hcengineering/platform'
import { Label, LabelAndProps, tooltip } from '@hcengineering/ui'
import type { IconSize } from '@hcengineering/ui'
import { DocNavLink } from '@hcengineering/view-resources'
import Avatar from './Avatar.svelte'
@ -27,11 +28,13 @@
export let shouldShowPlaceholder = false
export let defaultName: IntlString | undefined = undefined
export let statusLabel: IntlString | undefined = undefined
export let avatarSize: 'inline' | 'tiny' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' = 'x-small'
export let avatarSize: IconSize = 'x-small'
export let onEdit: ((event: MouseEvent) => void) | undefined = undefined
export let showTooltip: LabelAndProps | undefined = undefined
export let enlargedText = false
export let element: HTMLElement | undefined = undefined
export let colorInherit: boolean = false
export let accent: boolean = false
const onEditClick = (evt: MouseEvent) => {
if (isInteractive) {
@ -41,7 +44,7 @@
</script>
{#if value}
<DocNavLink object={value} onClick={onEdit} disableClick={!isInteractive} {inline}>
<DocNavLink object={value} onClick={onEdit} disableClick={!isInteractive} {inline} {colorInherit}>
<span
use:tooltip={showTooltip}
class="contentPresenter"
@ -58,7 +61,7 @@
</span>
{/if}
{#if shouldShowName}
<span class="eContentPresenterLabel">{getName(value)}</span>
<span class="eContentPresenterLabel" class:colorInherit>{getName(value)}</span>
{/if}
</span>
</DocNavLink>
@ -80,7 +83,7 @@
</span>
{/if}
{#if shouldShowName && defaultName}
<span class="eContentPresenterLabel">
<span class="eContentPresenterLabel" class:colorInherit>
<Label label={defaultName} />
</span>
{#if statusLabel}
@ -109,7 +112,7 @@
min-width: 0;
font-weight: 500;
text-align: left;
color: var(--accent-color);
color: var(--theme-caption-color);
overflow: hidden;
visibility: visible;
@ -119,14 +122,18 @@
-webkit-line-clamp: 2;
line-clamp: 2;
user-select: none;
&.colorInherit {
color: inherit;
}
}
&:hover {
.eContentPresenterIcon {
color: var(--caption-color);
color: var(--theme-caption-color);
}
.eContentPresenterLabel {
text-decoration: underline;
color: var(--caption-color);
color: var(--theme-caption-color);
}
}
}

View File

@ -15,7 +15,7 @@
<script lang="ts">
import { getName, Person } from '@hcengineering/contact'
import { getEmbeddedLabel, IntlString } from '@hcengineering/platform'
import { LabelAndProps } from '@hcengineering/ui'
import type { LabelAndProps, IconSize } from '@hcengineering/ui'
import { PersonLabelTooltip } from '..'
import PersonContent from './PersonContent.svelte'
@ -29,9 +29,11 @@
export let defaultName: IntlString | undefined = undefined
export let statusLabel: IntlString | undefined = undefined
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
export let avatarSize: 'inline' | 'tiny' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' = 'x-small'
export let avatarSize: IconSize = 'x-small'
export let onEdit: ((event: MouseEvent) => void) | undefined = undefined
export let element: HTMLElement | undefined = undefined
export let colorInherit: boolean = false
export let accent: boolean = false
function getTooltip (
tooltipLabels: PersonLabelTooltip | undefined,
@ -73,6 +75,8 @@
{shouldShowPlaceholder}
{enlargedText}
{statusLabel}
{colorInherit}
{accent}
bind:element
/>
{/if}

View File

@ -51,6 +51,10 @@
width: 1rem;
height: 1rem;
}
.smaller {
width: 1.125rem;
height: 1.125rem;
}
.small {
width: 1.25rem;
height: 1.25rem;

View File

@ -30,7 +30,13 @@ import {
import { Client, Doc, getCurrentAccount, IdMap, ObjQueryType, Ref, Timestamp, toIdMap } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { TemplateDataProvider } from '@hcengineering/templates'
import { DropdownIntlItem, getCurrentLocation, getPanelURI, Location, ResolvedLocation } from '@hcengineering/ui'
import {
DropdownIntlItem,
getCurrentResolvedLocation,
getPanelURI,
Location,
ResolvedLocation
} from '@hcengineering/ui'
import view, { Filter } from '@hcengineering/view'
import { FilterQuery } from '@hcengineering/view-resources'
import { get, writable } from 'svelte/store'
@ -180,7 +186,7 @@ export async function getContactChannel (value: Contact, provider: Ref<ChannelPr
}
export async function getContactLink (doc: Doc): Promise<Location> {
const loc = getCurrentLocation()
const loc = getCurrentResolvedLocation()
loc.path.length = 2
loc.fragment = undefined
loc.query = undefined
@ -213,7 +219,6 @@ async function generateLocation (loc: Location, id: Ref<Contact>): Promise<Resol
path: [appComponent, workspace],
fragment: getPanelURI(view.component.EditDoc, doc._id, doc._class, 'content')
},
shouldNavigate: false,
defaultLocation: {
path: [appComponent, workspace, contactId],
fragment: getPanelURI(view.component.EditDoc, doc._id, doc._class, 'content')

View File

@ -121,28 +121,28 @@
<svelte:fragment slot="pool">
<UserBoxList
items={object.authors}
size="small"
label={document.string.Authors}
emptyLabel={document.string.Authors}
kind="no-border"
kind={'secondary'}
size={'large'}
width={'min-content'}
on:update={({ detail }) => (object.authors = detail)}
/>
<UserBoxList
items={object.approvers}
size="small"
label={document.string.Approvers}
emptyLabel={document.string.Approvers}
kind="no-border"
kind={'secondary'}
size={'large'}
width={'min-content'}
on:update={({ detail }) => (object.approvers = detail)}
/>
<UserBoxList
items={object.reviewers}
size="small"
label={document.string.Reviewers}
emptyLabel={document.string.Reviewers}
kind="no-border"
kind={'secondary'}
size={'large'}
width={'min-content'}
on:update={({ detail }) => (object.reviewers = detail)}
/>

View File

@ -18,7 +18,7 @@
import { Doc, DocumentQuery } from '@hcengineering/core'
import { Document } from '@hcengineering/document'
import { createQuery, getClient } from '@hcengineering/presentation'
import { deviceOptionsStore as deviceInfo, Icon, Label, Loading, SearchEdit } from '@hcengineering/ui'
import { ActionIcon, IconMoreH, Label, Loading, SearchEdit } from '@hcengineering/ui'
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
import {
ActionContext,
@ -70,8 +70,8 @@
}
})
let twoRows: boolean
$: twoRows = $deviceInfo.docWidth <= 680
// let twoRows: boolean
// $: twoRows = $deviceInfo.docWidth <= 680
$: viewOptions = getViewOptions(viewlet, $viewOptionStore)
</script>
@ -82,23 +82,21 @@
}}
/>
<div class="antiPanel-component">
<div class="ac-header withSettings" class:full={!twoRows} class:mini={twoRows}>
<div class:ac-header-full={!twoRows} class:flex-between={twoRows}>
<div class="ac-header__wrap-title mr-3">
<div class="ac-header__icon"><Icon icon={document.icon.Document} size={'small'} /></div>
<span class="ac-header__title"><Label label={document.string.Documents} /></span>
<div class="ml-4"><FilterButton _class={document.class.Document} /></div>
</div>
<SearchEdit
bind:value={search}
on:change={() => {
updateResultQuery(search, query)
}}
/>
<div class="ac-header full divide caption-height">
<div class="ac-header__wrap-title mr-3">
<span class="ac-header__title"><Label label={document.string.Documents} /></span>
</div>
<div class="ac-header-full" class:secondRow={twoRows}>
</div>
<div class="ac-header full divide search-start">
<div class="ac-header-full small-gap">
<SearchEdit bind:value={search} on:change={() => updateResultQuery(search, query)} />
<ActionIcon icon={IconMoreH} size={'small'} />
<div class="buttons-divider" />
<FilterButton _class={document.class.Document} />
</div>
<div class="ac-header-full medium-gap">
<ViewletSettingButton bind:viewOptions {viewlet} />
<ActionIcon icon={IconMoreH} size={'small'} />
</div>
</div>

View File

@ -75,13 +75,21 @@
</div>
</div>
<svelte:fragment slot="header">
<SpaceSelector _class={hr.class.Department} label={hr.string.ParentDepartmentLabel} bind:space />
<SpaceSelector
_class={hr.class.Department}
label={hr.string.ParentDepartmentLabel}
bind:space
kind={'secondary'}
size={'large'}
/>
</svelte:fragment>
<svelte:fragment slot="pool">
<EmployeeBox
focusIndex={3}
label={hr.string.TeamLead}
placeholder={hr.string.TeamLead}
kind={'secondary'}
size={'large'}
bind:value={lead}
allowDeselect
showNavigate={false}

Some files were not shown because too many files have changed in this diff Show More