UBERF-4205: updated Panel header layout, custom aside (#3974)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2023-11-13 08:28:54 +03:00 committed by GitHub
parent d5a7280f15
commit 4c3ba1fbc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 486 additions and 520 deletions

View File

@ -296,7 +296,7 @@ export function createModel (builder: Builder): void {
builder, builder,
{ {
action: view.actionImpl.ShowPanel, action: view.actionImpl.ShowPanel,
actionProps: {}, actionProps: { element: 'content' },
label: view.string.Open, label: view.string.Open,
icon: view.icon.Open, icon: view.icon.Open,
keyBinding: ['e'], keyBinding: ['e'],
@ -353,7 +353,7 @@ export function createModel (builder: Builder): void {
builder, builder,
{ {
action: view.actionImpl.ShowPanel, action: view.actionImpl.ShowPanel,
actionProps: {}, actionProps: { element: 'content' },
label: view.string.Open, label: view.string.Open,
icon: view.icon.Open, icon: view.icon.Open,
keyBinding: ['e'], keyBinding: ['e'],

View File

@ -14,25 +14,15 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { afterUpdate } from 'svelte' import { afterUpdate, createEventDispatcher } from 'svelte'
import { Writable, writable } from 'svelte/store' import { Writable, writable } from 'svelte/store'
import activity from '@hcengineering/activity' import activity from '@hcengineering/activity'
import calendar from '@hcengineering/calendar'
import { Doc } from '@hcengineering/core' import { Doc } from '@hcengineering/core'
import { Asset } from '@hcengineering/platform' import { Component, deviceOptionsStore as deviceInfo, Panel, Scroller } from '@hcengineering/ui'
import { import type { ButtonItem } from '@hcengineering/ui'
AnySvelteComponent,
Component,
deviceOptionsStore as deviceInfo,
Icon,
Panel,
Scroller
} from '@hcengineering/ui'
export let title: string | undefined = undefined export let title: string | undefined = undefined
export let subtitle: string | undefined = undefined
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let withoutActivity: boolean = false export let withoutActivity: boolean = false
export let withoutInput: boolean = false export let withoutInput: boolean = false
export let withoutTitle: boolean = false export let withoutTitle: boolean = false
@ -44,21 +34,30 @@
export let isAside: boolean = true export let isAside: boolean = true
export let isUtils: boolean = true export let isUtils: boolean = true
export let isCustomAttr: boolean = true export let isCustomAttr: boolean = true
export let isReminder: boolean = true export let floatAside: boolean = false
export let floatAside = false export let allowClose: boolean = true
export let allowBack = true
export let allowClose = true
export let useMaxWidth: boolean | undefined = undefined export let useMaxWidth: boolean | undefined = undefined
export let isFullSize = false export let isFullSize: boolean = false
export let embedded = false
export let contentClasses: string | undefined = undefined export let contentClasses: string | undefined = undefined
export let content: HTMLElement | undefined | null = undefined export let content: HTMLElement | undefined | null = undefined
export let withoutContentScroll: boolean = false export let withoutContentScroll: boolean = false
export let customAside: ButtonItem[] | undefined = undefined
export let selectedAside: string | false = customAside ? customAside[0].id : false
export function getAside (): string | false {
return selectedAside
}
export function setAside (id: string | boolean): void {
panel.setAside(id)
}
const dispatch = createEventDispatcher()
let lastHref: string let lastHref: string
let timer: any let timer: any
let lastScrollHeight: number = -1 let lastScrollHeight: number = -1
let count: number = 0 let count: number = 0
let panel: Panel
const waitCount = 10 const waitCount = 10
const PanelScrollTop: Writable<Record<string, number>> = writable<Record<string, number>>({}) const PanelScrollTop: Writable<Record<string, number>> = writable<Record<string, number>>({})
@ -93,6 +92,7 @@
</script> </script>
<Panel <Panel
bind:this={panel}
bind:isAside bind:isAside
{isHeader} {isHeader}
bind:panelWidth bind:panelWidth
@ -100,53 +100,33 @@
bind:withoutTitle bind:withoutTitle
on:open on:open
on:close on:close
{allowBack}
{allowClose} {allowClose}
{floatAside} {floatAside}
{embedded}
bind:useMaxWidth bind:useMaxWidth
{isFullSize} {isFullSize}
{customAside}
bind:selectedAside
on:select={(result) => {
selectedAside = result.detail
dispatch('select', result.detail)
}}
> >
<svelte:fragment slot="navigator">
{#if $$slots.navigator}
<div class="flex-row-center flex-gap-1-5 mx-2">
<slot name="navigator" />
</div>
{/if}
</svelte:fragment>
<svelte:fragment slot="title"> <svelte:fragment slot="title">
{#if !withoutTitle} {#if !withoutTitle}
<div class="popupPanel-title__content-container antiTitle">
{#if $$slots.title} {#if $$slots.title}
<slot name="title" /> <slot name="title" />
{:else} {:else if title}
<div class="icon-wrapper"> <div class="title not-active">{title}</div>
{#if icon}<div class="wrapped-icon"><Icon {icon} size={'medium'} /></div>{/if}
<div class="title-wrapper">
{#if title}<span class="wrapped-title">{title}</span>{/if}
{#if subtitle || $$slots.subtitle}
<span class="wrapped-subtitle">
{#if subtitle}
{subtitle}
{/if} {/if}
<slot name="subtitle" />
</span>
{/if}
</div>
</div>
{/if}
</div>
{/if} {/if}
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="utils"> <svelte:fragment slot="pre-utils">
<slot name="pre-utils" /> <slot name="pre-utils" />
{#if isReminder} </svelte:fragment>
<Component is={calendar.component.DocReminder} props={{ value: object, title, focusIndex: 9000 }} /> <svelte:fragment slot="utils">
{/if}
{#if isUtils && $$slots.utils} {#if isUtils && $$slots.utils}
<div class="buttons-divider" /> <!-- <div class="buttons-divider" /> -->
<slot name="utils" /> <slot name="utils" />
{/if} {/if}
</svelte:fragment> </svelte:fragment>
@ -202,7 +182,7 @@
{#key object._id} {#key object._id}
<Component <Component
is={activity.component.Activity} is={activity.component.Activity}
props={{ object, showCommenInput: !withoutInput, shouldScroll: embedded, focusIndex: 1000 }} props={{ object, showCommenInput: !withoutInput, shouldScroll: true, focusIndex: 1000 }}
/> />
{/key} {/key}
{/if} {/if}
@ -221,7 +201,7 @@
props={{ props={{
object, object,
showCommenInput: !withoutInput, showCommenInput: !withoutInput,
shouldScroll: embedded, shouldScroll: true,
focusIndex: 1000, focusIndex: 1000,
boundary: content boundary: content
}} }}
@ -247,7 +227,7 @@
props={{ props={{
object, object,
showCommenInput: !withoutInput, showCommenInput: !withoutInput,
shouldScroll: embedded, shouldScroll: true,
focusIndex: 1000, focusIndex: 1000,
boundary: content boundary: content
}} }}

View File

@ -13,55 +13,52 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { Component, ScrollerBar, getPlatformColor, themeStore } from '@hcengineering/ui' import { Component, SelectPopup, showPopup } from '@hcengineering/ui'
import type { MouseTargetEvent } from '@hcengineering/ui'
import { NavLink } from '../..' import { NavLink } from '../..'
import BreadcrumbsElement from './BreadcrumbsElement.svelte'
import { BreadcrumbsModel } from './types' import { BreadcrumbsModel } from './types'
import { hasComponent } from './utils' import { hasComponent } from './utils'
export let models: readonly BreadcrumbsModel[] export let models: readonly BreadcrumbsModel[]
export let gap: 'none' | 'small' | 'big' = 'small'
let scroller: HTMLElement $: trimmed = models.length > 3
$: narrowModel = trimmed ? [models[0], models[models.length - 1]] : models
function getPosition (position: number): 'start' | 'end' | 'middle' { const handleMenuOpened = (event: MouseTargetEvent) => {
if (position === 0) { event.preventDefault()
return 'start' const items = models.slice(1, -1).map((m, i) => {
if (hasComponent(m)) {
const { component, props } = m
return { id: i, component, props }
} else {
const { title } = m
return { id: i, text: title }
} }
})
if (position === models.length - 1) { showPopup(SelectPopup, { value: items }, event.currentTarget)
return 'end'
}
return 'middle'
} }
</script> </script>
<ScrollerBar {gap} bind:scroller> {#each narrowModel as model, i}
{#each models as model, i}
{@const { color } = model}
{#if hasComponent(model)} {#if hasComponent(model)}
{@const { component, props } = model} {@const { component, props } = model}
<BreadcrumbsElement <div class="title">
position={getPosition(i)}
color={color !== undefined ? getPlatformColor(color, $themeStore.dark) : 'var(--accent-bg-color)'}
>
{#if typeof component === 'string'} {#if typeof component === 'string'}
<Component is={component} {props} /> <Component is={component} {props} />
{:else} {:else}
<svelte:component this={component} {...props} /> <svelte:component this={component} {...props} />
{/if} {/if}
</BreadcrumbsElement> </div>
{:else} {:else}
{@const { title, href, onClick } = model} {@const { title, href, onClick } = model}
<NavLink {href} noUnderline {onClick}> <NavLink {href} noUnderline {onClick}>
<BreadcrumbsElement <div class="title">{title}</div>
label={title}
{title}
position={getPosition(i)}
color={color !== undefined ? getPlatformColor(color, $themeStore.dark) : 'var(--accent-bg-color)'}
/>
</NavLink> </NavLink>
{/if} {/if}
<div class="title disabled">/</div>
{#if trimmed && i === 0}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="title" on:click={handleMenuOpened}>...</div>
<div class="title disabled">/</div>
{/if}
{/each} {/each}
</ScrollerBar>

View File

@ -102,6 +102,13 @@
--theme-button-disabled: transparent; --theme-button-disabled: transparent;
--theme-button-border: rgba(255, 255, 255, .09); --theme-button-border: rgba(255, 255, 255, .09);
--theme-breadcrumb-default: rgba(255, 255, 255, 0);
--theme-breadcrumb-hovered: rgba(255, 255, 255, .08);
--theme-breadcrumb-pressed: rgba(255, 255, 255, .1);
--theme-button-icon-default: rgba(255, 255, 255, 0);
--theme-button-icon-hovered: rgba(255, 255, 255, .06);
--theme-button-icon-pressed: rgba(255, 255, 255, .1);
--theme-button-contrast-color: #000; --theme-button-contrast-color: #000;
--theme-button-contrast-enabled: rgba(255, 255, 255, .8); --theme-button-contrast-enabled: rgba(255, 255, 255, .8);
--theme-button-contrast-hovered: #fff; --theme-button-contrast-hovered: #fff;
@ -320,6 +327,13 @@
--theme-button-disabled: rgba(0, 0, 0, .08); --theme-button-disabled: rgba(0, 0, 0, .08);
--theme-button-border: rgba(0, 0, 0, .09); --theme-button-border: rgba(0, 0, 0, .09);
--theme-breadcrumb-default: rgba(0, 0, 0, 0);
--theme-breadcrumb-hovered: rgba(0, 0, 0, .08);
--theme-breadcrumb-pressed: rgba(0, 0, 0, .1);
--theme-button-icon-default: rgba(0, 0, 0, 0);
--theme-button-icon-hovered: rgba(0, 0, 0, .06);
--theme-button-icon-pressed: rgba(0, 0, 0, .1);
--theme-button-contrast-color: #fff; --theme-button-contrast-color: #fff;
--theme-button-contrast-enabled: rgba(0, 0, 0, .8); --theme-button-contrast-enabled: rgba(0, 0, 0, .8);
--theme-button-contrast-hovered: #000; --theme-button-contrast-hovered: #000;

View File

@ -483,6 +483,7 @@ input.search {
.ml-12 { margin-left: 3rem; } .ml-12 { margin-left: 3rem; }
.ml-22 { margin-left: 5.5rem; } .ml-22 { margin-left: 5.5rem; }
.ml-auto { margin-left: auto; } .ml-auto { margin-left: auto; }
.mr-0-5 { margin-right: .125rem; }
.mr-1 { margin-right: .25rem; } .mr-1 { margin-right: .25rem; }
.mr-1-5 { margin-right: .375rem; } .mr-1-5 { margin-right: .375rem; }
.mr-2 { margin-right: .5rem; } .mr-2 { margin-right: .5rem; }

View File

@ -343,4 +343,16 @@
width: 20px; width: 20px;
} }
} }
&:focus { z-index: 1; }
&.icon {
background-color: var(--theme-button-icon-default);
.btn-icon { color: var(--theme-dark-color); }
&:hover, &:focus, &.selected { background-color: var(--theme-button-icon-hovered); }
&:active { background-color: var(--theme-button-icon-pressed); }
&:hover, &:focus, &.selected, &:active {
.btn-icon { color: var(--theme-caption-color); }
}
}
} }

View File

@ -94,7 +94,7 @@
::-webkit-scrollbar:horizontal { ::-webkit-scrollbar:horizontal {
height: 6px; height: 6px;
} }
::-webkit-scrollbar-track, .panel-container .scroll .scroll::-webkit-scrollbar-track { ::-webkit-scrollbar-track, .panel-instance .scroll .scroll::-webkit-scrollbar-track {
margin: 6px; margin: 6px;
// background-color: var(--scrollbar-bar-color); // background-color: var(--scrollbar-bar-color);
} }

View File

@ -77,54 +77,49 @@
.popupPanel { .popupPanel {
overflow: hidden; overflow: hidden;
display: flex; display: flex;
min-height: 0;
height: 100%;
&.rowContent {
width: 100%; width: 100%;
height: 100%;
min-width: 0; min-width: 0;
} min-height: 0;
&:not(.rowContent) { flex-direction: column; } &:not(.rowContent) { flex-direction: column; }
&.embedded { width: 100%; }
&:not(.embedded) { border-radius: .5rem; }
.popupPanel-title { .popupPanel-title {
display: flex; display: flex;
flex-shrink: 0; flex-shrink: 0;
justify-content: stretch; justify-content: stretch;
align-items: center; align-items: center;
padding: 0 1rem 0 1.25rem;
&.row {
padding: .5rem .75rem;
width: 100%;
&-top { margin: .5rem .75rem .125rem; }
&-bottom { margin: .5rem .75rem .5rem 1.25rem; }
}
&__bordered {
min-width: 0; min-width: 0;
min-height: 3.25rem; min-height: 3.25rem;
background-color: var(--theme-comp-header-color); background-color: var(--theme-comp-header-color);
&:not(.embedded) {
border: 1px solid var(--theme-divider-color);
border-bottom: none;
border-radius: .5rem .5rem 0 0;
}
&.embedded {
border-bottom: 1px solid var(--theme-divider-color); border-bottom: 1px solid var(--theme-divider-color);
}
}
&__content { &__content {
display: flex;
align-items: center;
flex-grow: 1; flex-grow: 1;
margin: 0 .75rem; margin-right: .75rem;
min-width: 0; min-width: 0;
min-height: 0; min-height: 0;
&-container { .title {
display: flex; padding: .125rem .375rem;
justify-content: stretch; font-size: .8125rem;
min-width: 0; color: var(--theme-content-color);
width: 100%; background-color: var(--theme-breadcrumb-default);
border-radius: .25rem;
&:not(:last-child) { margin-right: .125rem; }
&:not(.disabled, .not-active) {
cursor: pointer;
&:hover, &:focus { background-color: var(--theme-breadcrumb-hovered); }
&:active { background-color: var(--theme-breadcrumb-pressed); }
&:hover, &:focus, &:active { color: var(--theme-caption-color); }
}
&.disabled { color: var(--theme-dark-color); }
&.not-active { color: var(--theme-caption-color); }
} }
} }
} }
@ -138,13 +133,8 @@
height: 100%; height: 100%;
background-color: var(--theme-panel-color); background-color: var(--theme-panel-color);
border: 1px solid var(--theme-divider-color); border: 1px solid var(--theme-divider-color);
&:not(.embedded) {
border-radius: 0 0 .5rem .5rem;
}
&.embedded {
border-top: none; border-top: none;
border-left: none; border-left: none;
}
&.main { &.main {
justify-content: stretch; justify-content: stretch;

View File

@ -0,0 +1,48 @@
<!--
// 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">
import { createEventDispatcher } from 'svelte'
import type { ButtonItem } from '..'
import Button from './Button.svelte'
export let items: ButtonItem[]
export let selected: string | false = false
export let allowDeselected: boolean = true
export let mode: 'filled-icon' | 'highlighted' | 'selected' = 'selected'
export let props: any = {}
const dispatch = createEventDispatcher()
const select = (value: string | false): void => {
selected = value
dispatch('select', value)
}
</script>
{#each items as item}
{@const isSelect = selected === item.id}
<Button
{...item}
id={`btnGID-${item.id}`}
iconProps={mode === 'filled-icon' && isSelect ? { filled: true } : {}}
selected={mode === 'selected' ? isSelect : false}
highlight={mode === 'highlighted' ? isSelect : false}
{...props}
on:click={() => {
if (!isSelect) select(item.id)
else if (allowDeselected) select(false)
}}
/>
{/each}

View File

@ -16,14 +16,14 @@
import { afterUpdate, createEventDispatcher, onMount } from 'svelte' import { afterUpdate, createEventDispatcher, onMount } from 'svelte'
import { import {
deviceOptionsStore as deviceInfo, deviceOptionsStore as deviceInfo,
checkAdaptiveMatching,
IconBack,
Separator, Separator,
defineSeparators, defineSeparators,
resizeObserver, resizeObserver,
Button, Button,
ButtonGroup,
Scroller, Scroller,
panelSeparators panelSeparators,
ButtonItem
} from '../../' } from '../../'
import IconClose from './icons/Close.svelte' import IconClose from './icons/Close.svelte'
import IconDetails from './icons/Details.svelte' import IconDetails from './icons/Details.svelte'
@ -38,21 +38,35 @@
export let isAside: boolean = true export let isAside: boolean = true
export let isFullSize: boolean = false export let isFullSize: boolean = false
export let withoutTitle: boolean = false export let withoutTitle: boolean = false
export let floatAside = false export let floatAside: boolean = false
export let allowBack = true export let allowClose: boolean = true
export let allowClose = true
export let useMaxWidth: boolean | undefined = undefined export let useMaxWidth: boolean | undefined = undefined
export let embedded = false export let customAside: ButtonItem[] | undefined = undefined
export let selectedAside: string | false = customAside ? customAside[0].id : false
export function getAside (): string | false {
return selectedAside
}
export function setAside (id: string | boolean): void {
if (typeof id === 'string' && customAside) {
const i = customAside.findIndex((as) => as.id === id)
if (i === -1) return
handleSelectAside({ detail: id } as CustomEvent<any>)
} else {
asideShown = id as boolean
hideAside = !asideShown
if (id === false) selectedAside = false
}
}
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let asideFloat: boolean = false let asideFloat: boolean = false
let asideShown: boolean = true let asideShown: boolean = true
let hideAside: boolean = false
let fullSize: boolean = false let fullSize: boolean = false
let oldAside: string | false = selectedAside
$: devSize = $deviceInfo.size $: if (typeof selectedAside === 'string' && oldAside !== selectedAside) oldAside = selectedAside
$: twoRows = checkAdaptiveMatching(devSize, 'xs')
$: moveUtils = checkAdaptiveMatching(devSize, 'sm')
let oldWidth = '' let oldWidth = ''
let hideTimer: any | undefined let hideTimer: any | undefined
@ -69,13 +83,13 @@
asideFloat = true asideFloat = true
if (asideShown) { if (asideShown) {
asideShown = false asideShown = false
if (customAside) handleSelectAside({ detail: false } as CustomEvent<any>, false)
} }
} else if (panelWidth > 900) { } else if (panelWidth > 900) {
if (asideFloat) { if (asideFloat) asideFloat = false
asideFloat = false if (!asideShown && !hideAside) {
}
if (!asideShown) {
asideShown = true asideShown = true
if (customAside) handleSelectAside({ detail: oldAside } as CustomEvent<any>, false)
} }
} }
} }
@ -91,73 +105,60 @@
onMount(() => dispatch('open')) onMount(() => dispatch('open'))
defineSeparators('panel-aside', panelSeparators) defineSeparators('panel-aside', panelSeparators)
const handleAside = (): void => {
asideShown = !asideShown
hideAside = !asideShown
}
const handleSelectAside = (result: CustomEvent<any>, sw: boolean = true): void => {
selectedAside = result.detail
if (sw) {
asideShown = selectedAside !== false
hideAside = !asideShown
}
dispatch('select', result.detail)
}
</script> </script>
<div <div
class="popupPanel panel" class="popupPanel panel"
class:embedded
use:resizeObserver={(element) => { use:resizeObserver={(element) => {
panelWidth = element.clientWidth panelWidth = element.clientWidth
checkPanel() checkPanel()
}} }}
> >
<div <div class="popupPanel-title">
class="popupPanel-title__bordered {twoRows && !withoutTitle ? 'flex-col flex-no-shrink' : 'flex-row-center'}"
class:embedded
>
<div class="popupPanel-title {twoRows && !withoutTitle ? 'row-top' : 'row'}">
{#if allowBack}
<Button
focusIndex={10000}
icon={IconBack}
kind={'ghost'}
size={'medium'}
on:click={() => {
history.back()
}}
/>
{/if}
{#if allowClose}
<div class="antiHSpacer" />
<Button
focusIndex={10001}
icon={IconClose}
kind={'ghost'}
size={'medium'}
on:click={() => {
dispatch('close')
}}
/>
{/if}
{#if $$slots.navigator}<slot name="navigator" />{/if}
<div class="popupPanel-title__content"> <div class="popupPanel-title__content">
{#if !twoRows && !withoutTitle}<slot name="title" />{/if} {#if !withoutTitle}<slot name="title" />{/if}
</div> </div>
<div class="buttons-group xsmall-gap"> <slot name="pre-utils" />
{#if !moveUtils} <div class="flex-row-center ml-3">
<slot name="utils" /> <slot name="utils" />
{/if}
{#if isFullSize || useMaxWidth !== undefined || ($$slots.aside && isAside)}
<div class="buttons-divider" />
{/if}
{#if $$slots.aside && isAside} {#if $$slots.aside && isAside}
{#if customAside}
<ButtonGroup
items={customAside}
props={{ kind: 'icon', iconProps: { size: 'medium' } }}
bind:selected={selectedAside}
on:select={handleSelectAside}
/>
{:else}
<Button <Button
focusIndex={10008} focusIndex={10008}
icon={IconDetails} icon={IconDetails}
kind={'ghost'} iconProps={{ size: 'medium', filled: asideShown }}
size={'medium'} kind={'icon'}
selected={asideShown} selected={asideShown}
on:click={() => { on:click={handleAside}
asideShown = !asideShown
}}
/> />
{/if} {/if}
{/if}
{#if useMaxWidth !== undefined} {#if useMaxWidth !== undefined}
<Button <Button
focusIndex={10009} focusIndex={10009}
icon={useMaxWidth ? IconMaxWidth : IconMinWidth} icon={useMaxWidth ? IconMaxWidth : IconMinWidth}
kind={'ghost'} iconProps={{ size: 'medium' }}
size={'medium'} kind={'icon'}
selected={useMaxWidth} selected={useMaxWidth}
on:click={() => { on:click={() => {
useMaxWidth = !useMaxWidth useMaxWidth = !useMaxWidth
@ -169,8 +170,8 @@
<Button <Button
focusIndex={100010} focusIndex={100010}
icon={fullSize ? IconScale : IconScaleFull} icon={fullSize ? IconScale : IconScaleFull}
kind={'ghost'} iconProps={{ size: 'medium' }}
size={'medium'} kind={'icon'}
selected={fullSize} selected={fullSize}
on:click={() => { on:click={() => {
fullSize = !fullSize fullSize = !fullSize
@ -178,13 +179,20 @@
}} }}
/> />
{/if} {/if}
</div> {#if allowClose}
</div> <Button
{#if twoRows && !withoutTitle} focusIndex={10001}
<div class="popupPanel-title row-bottom"><slot name="title" /></div> icon={IconClose}
iconProps={{ size: 'medium' }}
kind={'icon'}
on:click={() => {
dispatch('close')
}}
/>
{/if} {/if}
</div> </div>
<div class="popupPanel-body {$deviceInfo.isMobile ? 'mobile' : 'main'}" class:asideShown class:embedded> </div>
<div class="popupPanel-body {$deviceInfo.isMobile ? 'mobile' : 'main'}" class:asideShown>
{#if $deviceInfo.isMobile} {#if $deviceInfo.isMobile}
<Scroller horizontal padding={'.5rem .75rem'}> <Scroller horizontal padding={'.5rem .75rem'}>
<div <div
@ -223,11 +231,6 @@
<div class="popupPanel-body__aside" class:float={asideFloat} class:shown={asideShown}> <div class="popupPanel-body__aside" class:float={asideFloat} class:shown={asideShown}>
<Separator name={'panel-aside'} float={asideFloat ? 'aside' : true} index={0} /> <Separator name={'panel-aside'} float={asideFloat ? 'aside' : true} index={0} />
<div class="antiPanel-wrap__content"> <div class="antiPanel-wrap__content">
{#if moveUtils}
<div class="buttons-group justify-end xsmall-gap" style:margin={'.5rem 2rem 0'}>
<slot name="utils" />
</div>
{/if}
<slot name="aside" /> <slot name="aside" />
</div> </div>
</div> </div>

View File

@ -143,7 +143,6 @@
<slot name="panel-header" /> <slot name="panel-header" />
<div <div
class="panel-instance" class="panel-instance"
class:bg={false}
bind:this={modalHTML} bind:this={modalHTML}
style:top={options?.props?.top} style:top={options?.props?.top}
style:bottom={options?.props?.bottom} style:bottom={options?.props?.bottom}
@ -156,7 +155,6 @@
style:min-width={options?.props?.minWidth} style:min-width={options?.props?.minWidth}
style:transform={options?.props?.transform} style:transform={options?.props?.transform}
> >
<div class="panel-container" class:padding={props.element === 'content'}>
<svelte:component <svelte:component
this={component} this={component}
bind:this={componentInstance} bind:this={componentInstance}
@ -170,7 +168,6 @@
on:update={_update} on:update={_update}
/> />
</div> </div>
</div>
{#if props.element !== 'content'} {#if props.element !== 'content'}
<div <div
class="modal-overlay" class="modal-overlay"
@ -188,19 +185,6 @@
z-index: 401; z-index: 401;
position: fixed; position: fixed;
background-color: transparent; background-color: transparent;
&.bg {
background-color: var(--theme-back-color);
}
.panel-container {
padding: 0.5rem;
width: 100%;
height: 100%;
&.padding {
padding: 0.75rem;
}
}
} }
.modal-overlay { .modal-overlay {

View File

@ -437,8 +437,12 @@
const checkSibling = (start: boolean = false): void => { const checkSibling = (start: boolean = false): void => {
if (separator === null) return if (separator === null) return
if (prevElement === null || start) prevElement = separator.previousElementSibling as HTMLElement if ((prevElement === null || start) && separator) {
if (nextElement === null || start) nextElement = separator.nextElementSibling as HTMLElement prevElement = separator.previousElementSibling as HTMLElement
}
if ((nextElement === null || start) && separator) {
nextElement = separator.nextElementSibling as HTMLElement
}
if (separators && prevElement && separators[index].float !== undefined) { if (separators && prevElement && separators[index].float !== undefined) {
prevElement.setAttribute('data-float', separators[index].float ?? '') prevElement.setAttribute('data-float', separators[index].float ?? '')
} }
@ -447,7 +451,7 @@
} }
} }
const checkParent = (): void => { const checkParent = (): void => {
if (parentElement === null) parentElement = separator.parentElement as HTMLElement if (parentElement === null && separator) parentElement = separator.parentElement as HTMLElement
if (parentElement && typeof float === 'string') parentElement.setAttribute('data-float', float) if (parentElement && typeof float === 'string') parentElement.setAttribute('data-float', float)
} }

View File

@ -3,8 +3,8 @@
export let fill: string = 'currentColor' export let fill: string = 'currentColor'
</script> </script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> <svg class="svg-{size}" {fill} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path <path
d="M3.14645 3.14645C2.95118 3.34171 2.95118 3.65829 3.14645 3.85355L7.29289 8L3.14645 12.1464C2.95118 12.3417 2.95118 12.6583 3.14645 12.8536C3.34171 13.0488 3.65829 13.0488 3.85355 12.8536L8 8.70711L12.1464 12.8536C12.3417 13.0488 12.6583 13.0488 12.8536 12.8536C13.0488 12.6583 13.0488 12.3417 12.8536 12.1464L8.70711 8L12.8536 3.85355C13.0488 3.65829 13.0488 3.34171 12.8536 3.14645C12.6583 2.95118 12.3417 2.95118 12.1464 3.14645L8 7.29289L3.85355 3.14645C3.65829 2.95118 3.34171 2.95118 3.14645 3.14645Z" d="M6.29289 6.29338C5.90237 6.68391 5.90237 7.31707 6.29289 7.70759L14.5858 16.0005L6.29289 24.2934C5.90237 24.6839 5.90237 25.3171 6.29289 25.7076C6.68342 26.0981 7.31658 26.0981 7.70711 25.7076L16 17.4147L24.2929 25.7076C24.6834 26.0981 25.3166 26.0981 25.7071 25.7076C26.0976 25.3171 26.0976 24.6839 25.7071 24.2934L17.4142 16.0005L25.7071 7.70759C26.0976 7.31707 26.0976 6.68391 25.7071 6.29338C25.3166 5.90286 24.6834 5.90286 24.2929 6.29338L16 14.5863L7.70711 6.29338C7.31658 5.90286 6.68342 5.90286 6.29289 6.29338Z"
/> />
</svg> </svg>

View File

@ -15,14 +15,22 @@
--> -->
<script lang="ts"> <script lang="ts">
export let size: 'x-small' | 'small' | 'medium' | 'large' export let size: 'x-small' | 'small' | 'medium' | 'large'
export let filled: boolean = false
const fill: string = 'currentColor' const fill: string = 'currentColor'
</script> </script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> <svg class="svg-{size}" {fill} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
{#if filled}
<path <path
fill-rule="evenodd" fill-rule="evenodd"
clip-rule="evenodd" clip-rule="evenodd"
d="M 4.25 2 C 2.45508 2 1 3.45508 1 5.25 V 10.7499 C 1 12.5449 2.45508 13.9999 4.25 13.9999 H 11.75 C 13.5449 13.9999 15 12.5449 15 10.7499 V 5.25 C 15 3.45508 13.5449 2 11.75 2 H 4.25 Z M 2.5 10.4999 C 2.5 11.6045 3.39543 12.4999 4.5 12.4999 H 11.75 C 12.7165 12.4999 13.5 11.7164 13.5 10.7499 V 5.25 C 13.5 4.28351 12.7165 3.5 11.75 3.5 H 4.5 C 3.39543 3.5 2.5 4.39543 2.5 5.5 V 10.4999 Z" d="M2 8.00049C2 5.79135 3.79086 4.00049 6 4.00049H26C28.2091 4.00049 30 5.79135 30 8.00049V24.0005C30 26.2096 28.2091 28.0005 26 28.0005H6C3.79086 28.0005 2 26.2096 2 24.0005V8.00049ZM20 6.00049H6C4.89543 6.00049 4 6.89592 4 8.00049V24.0005C4 25.1051 4.89543 26.0005 6 26.0005H20V6.00049Z"
/> />
<rect x="9" y="3" width="1.5" height="10" /> {:else}
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M2 8.00049C2 5.79135 3.79086 4.00049 6 4.00049H26C28.2091 4.00049 30 5.79135 30 8.00049V24.0005C30 26.2096 28.2091 28.0005 26 28.0005H6C3.79086 28.0005 2 26.2096 2 24.0005V8.00049ZM20 6.00049H6C4.89543 6.00049 4 6.89592 4 8.00049V24.0005C4 25.1051 4.89543 26.0005 6 26.0005H20V6.00049ZM22 26.0002V6.00049H26C27.1046 6.00049 28 6.89592 28 8.00049V24.0002C28 25.1048 27.1046 26.0002 26 26.0002H22Z"
/>
{/if}
</svg> </svg>

View File

@ -18,10 +18,10 @@
const fill: string = 'currentColor' const fill: string = 'currentColor'
</script> </script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> <svg class="svg-{size}" {fill} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path <path
fill-rule="evenodd" fill-rule="evenodd"
clip-rule="evenodd" clip-rule="evenodd"
d="M1 5.25C1 3.45508 2.45508 2 4.25 2H11.75C13.5449 2 15 3.45508 15 5.25V10.7499C15 12.5449 13.5449 13.9999 11.75 13.9999H4.25C2.45508 13.9999 1 12.5449 1 10.7499V5.25ZM4.5 12.4999C3.39543 12.4999 2.5 11.6045 2.5 10.4999V5.5C2.5 4.39543 3.39543 3.5 4.5 3.5H9V12.4999H4.5Z" d="M2 8.00049C2 5.79135 3.79086 4.00049 6 4.00049H26C28.2091 4.00049 30 5.79135 30 8.00049V24.0005C30 26.2096 28.2091 28.0005 26 28.0005H6C3.79086 28.0005 2 26.2096 2 24.0005V8.00049ZM20 6.00049H6C4.89543 6.00049 4 6.89592 4 8.00049V24.0005C4 25.1051 4.89543 26.0005 6 26.0005H20V6.00049Z"
/> />
</svg> </svg>

View File

@ -19,14 +19,20 @@
const fill: string = 'currentColor' const fill: string = 'currentColor'
</script> </script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> <svg class="svg-{size}" {fill} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path <path
d="M12.3,7.8H3.7c-0.8,0-1.5-0.7-1.5-1.5V3.7c0-0.8,0.7-1.5,1.5-1.5h8.7c0.8,0,1.5,0.7,1.5,1.5v2.7C13.8,7.2,13.2,7.8,12.3,7.8z M3.7,3.2c-0.3,0-0.5,0.2-0.5,0.5v2.7c0,0.3,0.2,0.5,0.5,0.5h8.7c0.3,0,0.5-0.2,0.5-0.5V3.7c0-0.3-0.2-0.5-0.5-0.5H3.7z" fill-rule="evenodd"
clip-rule="evenodd"
d="M25.0544 4.00001H6.94561C6.52154 3.99992 6.10941 3.99984 5.76949 4.04554C5.38629 4.09706 4.94902 4.22259 4.5858 4.5858C4.22259 4.94902 4.09706 5.38629 4.04554 5.76949C3.99984 6.10941 3.99992 6.52155 4.00001 6.94562V11.0544C3.99992 11.4785 3.99984 11.8906 4.04554 12.2305C4.09706 12.6137 4.22259 13.051 4.5858 13.4142C4.94902 13.7774 5.38629 13.903 5.76949 13.9545C6.1094 14.0002 6.52152 14.0001 6.94558 14H25.0544C25.4785 14.0001 25.8906 14.0002 26.2305 13.9545C26.6137 13.903 27.051 13.7774 27.4142 13.4142C27.7774 13.051 27.903 12.6137 27.9545 12.2305C28.0002 11.8906 28.0001 11.4785 28 11.0544V6.94561C28.0001 6.52154 28.0002 6.10941 27.9545 5.76949C27.903 5.38629 27.7774 4.94902 27.4142 4.5858C27.051 4.22259 26.6137 4.09706 26.2305 4.04554C25.8906 3.99984 25.4785 3.99992 25.0544 4.00001ZM25.9713 6.02871L25.9723 6.03599C25.9979 6.22618 26 6.50034 26 7.00001V11C26 11.4997 25.9979 11.7738 25.9723 11.964L25.9713 11.9713L25.964 11.9723C25.7738 11.9979 25.4997 12 25 12H7.00001C6.50034 12 6.22618 11.9979 6.03599 11.9723L6.02871 11.9713L6.02771 11.964C6.00214 11.7738 6.00001 11.4997 6.00001 11V7.00001C6.00001 6.50034 6.00214 6.22618 6.02771 6.03599L6.02871 6.02871L6.03599 6.02771C6.22618 6.00214 6.50034 6.00001 7.00001 6.00001H25C25.4997 6.00001 25.7738 6.00214 25.964 6.02771L25.9713 6.02871Z"
/> />
<path <path
d="M4.7,13.8c-1,0-1.6,0-2.1-0.4s-0.4-1-0.4-2.1s0-1.6,0.4-2.1s1-0.4,2.1-0.4h2.5v2.5c0,1,0,1.6-0.4,2.1S5.7,13.8,4.7,13.8z M4.7,9.8c-0.7,0-1.2,0-1.4,0.1s-0.1,0.6-0.1,1.4s0,1.2,0.1,1.4s0.6,0.1,1.4,0.1s1.2,0,1.4-0.1s0.1-0.6,0.1-1.4V9.8H4.7z" fill-rule="evenodd"
clip-rule="evenodd"
d="M6.94563 18.0001L14 18.0001L14.0001 25.0545C14.0001 25.4786 14.0002 25.8907 13.9545 26.2306C13.903 26.6138 13.7775 27.0511 13.4143 27.4143C13.051 27.7775 12.6138 27.903 12.2306 27.9545C11.8907 28.0002 11.4786 28.0002 11.0545 28.0001H6.94563C6.52159 28.0002 6.10942 28.0002 5.76952 27.9545C5.38632 27.903 4.94904 27.7775 4.58583 27.4143C4.22261 27.0511 4.09709 26.6138 4.04557 26.2306C3.99987 25.8907 3.99995 25.4786 4.00003 25.0545V20.9457C3.99995 20.5216 3.99987 20.1095 4.04557 19.7696C4.09709 19.3864 4.22261 18.9491 4.58583 18.5859C4.94904 18.2226 5.38632 18.0971 5.76952 18.0456C6.10943 17.9999 6.52157 18 6.94563 18.0001ZM6.02874 20.0288L6.02774 20.0361C6.00217 20.2262 6.00004 20.5004 6.00004 21.0001V25.0001C6.00004 25.4998 6.00217 25.7739 6.02774 25.9641L6.02874 25.9714L6.03602 25.9724C6.22621 25.998 6.50037 26.0001 7.00004 26.0001H11C11.4997 26.0001 11.7739 25.998 11.9641 25.9724L11.9713 25.9714L11.9723 25.9641C11.9979 25.7739 12 25.4998 12 25.0001V20.0001H7.00004C6.50037 20.0001 6.22621 20.0022 6.03602 20.0278L6.02874 20.0288Z"
/> />
<path <path
d="M11.3,13.8c-1,0-1.6,0-2.1-0.4s-0.4-1-0.4-2.1V8.8h2.5c1,0,1.6,0,2.1,0.4s0.4,1,0.4,2.1s0,1.6-0.4,2.1S12.4,13.8,11.3,13.8z M9.8,9.8v1.5c0,0.7,0,1.2,0.1,1.4s0.6,0.1,1.4,0.1s1.2,0,1.4-0.1s0.1-0.6,0.1-1.4s0-1.2-0.1-1.4s-0.6-0.1-1.4-0.1H9.8z" fill-rule="evenodd"
clip-rule="evenodd"
d="M27.4143 18.5859C27.051 18.2226 26.6138 18.0971 26.2306 18.0456C25.8906 17.9999 25.4785 18 25.0545 18.0001L18 18.0001L18 25.0545C18 25.4786 17.9999 25.8907 18.0456 26.2306C18.0971 26.6138 18.2226 27.0511 18.5858 27.4143C18.949 27.7775 19.3863 27.903 19.7695 27.9545C20.1094 28.0002 20.5215 28.0002 20.9456 28.0001H25.0545C25.4785 28.0002 25.8907 28.0002 26.2306 27.9545C26.6138 27.903 27.051 27.7775 27.4143 27.4143C27.7775 27.0511 27.903 26.6138 27.9545 26.2306C28.0002 25.8907 28.0001 25.4786 28 25.0545V20.9457C28.0001 20.5216 28.0002 20.1095 27.9545 19.7696C27.903 19.3864 27.7775 18.9491 27.4143 18.5859ZM25 20.0001C25.4997 20.0001 25.7739 20.0022 25.9641 20.0278L25.9713 20.0288L25.9723 20.0361C25.9979 20.2262 26 20.5004 26 21.0001V25.0001C26 25.4998 25.9979 25.7739 25.9723 25.9641L25.9713 25.9714L25.9641 25.9724C25.7739 25.998 25.4997 26.0001 25 26.0001H21C20.5004 26.0001 20.2262 25.998 20.036 25.9724L20.0287 25.9714L20.0277 25.9641C20.0022 25.7739 20 25.4998 20 25.0001V20.0001H25Z"
/> />
</svg> </svg>

View File

@ -33,7 +33,7 @@
}) })
}} }}
> >
<span>{getTimeZoneName()}</span>&nbsp;&nbsp; {#if getTimeZoneName() !== ''}<span>{getTimeZoneName()}</span>&nbsp;&nbsp;{/if}
<span>{hours}</span> <span>{hours}</span>
<span style:visibility={delimiter ? 'visible' : 'hidden'}>:</span> <span style:visibility={delimiter ? 'visible' : 'hidden'}>:</span>
<span>{minutes}</span> <span>{minutes}</span>

View File

@ -84,10 +84,12 @@
{#each selectedTZ as selected, i} {#each selectedTZ as selected, i}
<div class="statusPopup-option"> <div class="statusPopup-option">
<ClockFace bind:timeZone={selected} size={clockSize} /> <ClockFace bind:timeZone={selected} size={clockSize} />
{#if selected !== ''}
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<span class="label overflow-label" style:max-width={clockSize} on:click={(ev) => changeTimeZone(ev, i)}> <span class="label overflow-label" style:max-width={clockSize} on:click={(ev) => changeTimeZone(ev, i)}>
{selected === '' ? '--' : convertTimeZone(selected).short} {convertTimeZone(selected).short}
</span> </span>
{/if}
</div> </div>
{/each} {/each}
</div> </div>

View File

@ -33,6 +33,7 @@ export type {
PopupPositionElement, PopupPositionElement,
ButtonKind, ButtonKind,
ButtonSize, ButtonSize,
ButtonItem,
IconSize, IconSize,
TabItem, TabItem,
DeviceOptions, DeviceOptions,
@ -40,7 +41,8 @@ export type {
SeparatedItem, SeparatedItem,
DefSeparators, DefSeparators,
SeparatedElement, SeparatedElement,
TimeZone TimeZone,
MouseTargetEvent
} from './types' } from './types'
export { themeStore } from '@hcengineering/theme' export { themeStore } from '@hcengineering/theme'
@ -50,6 +52,7 @@ export { getCurrentLocation, locationToUrl, navigate, location, setLocationStora
export { default as EditBox } from './components/EditBox.svelte' export { default as EditBox } from './components/EditBox.svelte'
export { default as Label } from './components/Label.svelte' export { default as Label } from './components/Label.svelte'
export { default as Button } from './components/Button.svelte' export { default as Button } from './components/Button.svelte'
export { default as ButtonGroup } from './components/ButtonGroup.svelte'
export { default as Status } from './components/Status.svelte' export { default as Status } from './components/Status.svelte'
export { default as Component } from './components/Component.svelte' export { default as Component } from './components/Component.svelte'
export { default as Icon } from './components/Icon.svelte' export { default as Icon } from './components/Icon.svelte'

View File

@ -141,6 +141,7 @@ export type ButtonKind =
| 'negative' | 'negative'
| 'regular' | 'regular'
| 'ghost' | 'ghost'
| 'icon'
| 'no-border' | 'no-border'
| 'link' | 'link'
| 'link-bordered' | 'link-bordered'
@ -170,6 +171,16 @@ export type EditStyle =
| 'ghost' | 'ghost'
| 'default-large' | 'default-large'
| 'ghost-large' | 'ghost-large'
export interface ButtonItem {
id: string
icon?: Asset | AnySvelteComponent | ComponentType
label?: IntlString
labelParams?: Record<string, any>
disabled?: boolean
showTooltip?: LabelAndProps
}
export interface PopupPositionElement { export interface PopupPositionElement {
getBoundingClientRect: () => DOMRect getBoundingClientRect: () => DOMRect
position?: { position?: {
@ -241,6 +252,7 @@ export interface IconProps {
icon?: number icon?: number
size?: IconSize size?: IconSize
fill?: string fill?: string
filled?: boolean
} }
export function getIconSize2x (size: IconSize): IconSize { export function getIconSize2x (size: IconSize): IconSize {
@ -484,3 +496,8 @@ export interface TimeZone {
short: string short: string
offset?: number offset?: number
} }
/**
* @public
*/
export type MouseTargetEvent = MouseEvent & { currentTarget: EventTarget & HTMLElement }

View File

@ -32,13 +32,7 @@
Label, Label,
showPopup showPopup
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { import { ContextMenu, DocAttributeBar, invokeAction, ParentsNavigator } from '@hcengineering/view-resources'
ContextMenu,
DocAttributeBar,
invokeAction,
ParentsNavigator,
UpDownNavigator
} from '@hcengineering/view-resources'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import board from '../plugin' import board from '../plugin'
import { getCardActions } from '../utils/CardActionUtils' import { getCardActions } from '../utils/CardActionUtils'
@ -112,18 +106,12 @@
{#if object !== undefined} {#if object !== undefined}
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<Panel <Panel {object} isHeader isAside={true} isSub={false} on:close={() => dispatch('close')}>
icon={board.icon.Card} <svelte:fragment slot="title">
title={object?.title}
{object}
isHeader
isAside={true}
isSub={false}
on:close={() => dispatch('close')}
>
<svelte:fragment slot="navigator">
<UpDownNavigator element={object} />
<ParentsNavigator element={object} /> <ParentsNavigator element={object} />
{#if object?.title}
<div class="title not-active">{object?.title}</div>
{/if}
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="header"> <svelte:fragment slot="header">
<div class="flex fs-title flex-gap-1"> <div class="flex fs-title flex-gap-1">

View File

@ -20,7 +20,7 @@
export let _id: Ref<Space> export let _id: Ref<Space>
</script> </script>
<div class="antiPanel-component"> <div class="antiComponent">
<SpaceHeader spaceId={_id} withSearch={false} /> <SpaceHeader spaceId={_id} withSearch={false} />
<ChannelView space={_id} /> <ChannelView space={_id} />
</div> </div>

View File

@ -21,7 +21,6 @@
import { Class, Doc, Ref, getCurrentAccount } from '@hcengineering/core' import { Class, Doc, Ref, getCurrentAccount } from '@hcengineering/core'
export let _id: Ref<Comment> | undefined export let _id: Ref<Comment> | undefined
export let embedded: boolean = true
const client = getClient() const client = getClient()
const hierarchy = client.getHierarchy() const hierarchy = client.getHierarchy()
@ -91,5 +90,5 @@
{#if loading} {#if loading}
<Loading /> <Loading />
{:else if component && attachedDocId && attachedDocClass} {:else if component && attachedDocId && attachedDocClass}
<Component is={component} props={{ _id: attachedDocId, _class: attachedDocClass, embedded }} on:close /> <Component is={component} props={{ _id: attachedDocId, _class: attachedDocClass }} on:close />
{/if} {/if}

View File

@ -18,13 +18,12 @@
import type { Class, Ref } from '@hcengineering/core' import type { Class, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import { SpaceMembers } from '@hcengineering/contact-resources' import { SpaceMembers } from '@hcengineering/contact-resources'
import { Icon, Label, Panel, Scroller } from '@hcengineering/ui' import { Label, Panel, Scroller } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import chunter from '../plugin' import chunter from '../plugin'
import EditChannelDescriptionTab from './EditChannelDescriptionTab.svelte' import EditChannelDescriptionTab from './EditChannelDescriptionTab.svelte'
import EditChannelSettingsTab from './EditChannelSettingsTab.svelte' import EditChannelSettingsTab from './EditChannelSettingsTab.svelte'
import Lock from './icons/Lock.svelte'
export let _id: Ref<ChunterSpace> export let _id: Ref<ChunterSpace>
export let _class: Ref<Class<ChunterSpace>> export let _class: Ref<Class<ChunterSpace>>
@ -54,20 +53,12 @@
<svelte:fragment slot="title"> <svelte:fragment slot="title">
{#if clazz && channel} {#if clazz && channel}
{#if _class === chunter.class.DirectMessage} {#if _class === chunter.class.DirectMessage}
<span class="fs-title"><Label label={clazz.label} /></span> <span class="title"><Label label={clazz.label} /></span>
{:else} {:else}
<div class="antiTitle icon-wrapper"> <span class="title">
<div class="wrapped-icon">
{#if clazz.icon}<Icon icon={channel.private ? Lock : clazz.icon} size={'medium'} />{/if}
</div>
<div class="title-wrapper">
<span class="wrapped-title">
<span class="trans-title content-color"><Label label={clazz.label} /></span> <span class="trans-title content-color"><Label label={clazz.label} /></span>
{channel.name} {channel.name}
</span> </span>
<span class="wrapped-subtitle">{channel.description}</span>
</div>
</div>
{/if} {/if}
{/if} {/if}
</svelte:fragment> </svelte:fragment>

View File

@ -21,7 +21,6 @@
export let _id: Ref<Channel> export let _id: Ref<Channel>
export let _class: Ref<Class<Channel>> export let _class: Ref<Class<Channel>>
export let embedded = false
const client = getClient() const client = getClient()
@ -43,9 +42,6 @@
{#await getPresenter(channel) then presenter} {#await getPresenter(channel) then presenter}
{#if presenter} {#if presenter}
<Component <Component is={presenter} props={{ _id: channel?.attachedTo, _class: channel?.attachedToClass, channel }} />
is={presenter}
props={{ embedded, _id: channel?.attachedTo, _class: channel?.attachedToClass, channel }}
/>
{/if} {/if}
{/await} {/await}

View File

@ -27,7 +27,6 @@
Button, Button,
EditBox, EditBox,
eventToHTMLElement, eventToHTMLElement,
Icon,
IconAttachment, IconAttachment,
Label, Label,
Panel, Panel,
@ -236,12 +235,7 @@
}} }}
> >
<svelte:fragment slot="title"> <svelte:fragment slot="title">
<div class="antiTitle icon-wrapper"> <div class="title not-active">Email</div>
<div class="wrapped-icon"><Icon icon={contact.icon.Email} size={'medium'} /></div>
<div class="title-wrapper">
<span class="wrapped-title">Email</span>
</div>
</div>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="utils"> <svelte:fragment slot="utils">

View File

@ -17,16 +17,7 @@
import { DocumentQuery, Ref, WithLookup } from '@hcengineering/core' import { DocumentQuery, Ref, WithLookup } from '@hcengineering/core'
import type { Department, Staff } from '@hcengineering/hr' import type { Department, Staff } from '@hcengineering/hr'
import { createQuery } from '@hcengineering/presentation' import { createQuery } from '@hcengineering/presentation'
import { import { Button, eventToHTMLElement, Label, Scroller, SearchEdit, showPopup, IconAdd } from '@hcengineering/ui'
Button,
deviceOptionsStore as deviceInfo,
eventToHTMLElement,
Label,
Scroller,
SearchEdit,
showPopup,
IconAdd
} from '@hcengineering/ui'
import hr from '../plugin' import hr from '../plugin'
import CreateDepartment from './CreateDepartment.svelte' import CreateDepartment from './CreateDepartment.svelte'
import DepartmentCard from './DepartmentCard.svelte' import DepartmentCard from './DepartmentCard.svelte'
@ -75,8 +66,6 @@
spaceMembers.query(hr.mixin.Staff, {}, (res) => { spaceMembers.query(hr.mixin.Staff, {}, (res) => {
allEmployees = res allEmployees = res
}) })
$: twoRows = $deviceInfo.twoRows
</script> </script>
<div class="ac-header full divide caption-height"> <div class="ac-header full divide caption-height">

View File

@ -38,7 +38,6 @@
const client = getClient() const client = getClient()
const query = createQuery() const query = createQuery()
const clazz = client.getHierarchy().getClass(lead.class.Funnel)
function updateObject (_id: Ref<Funnel>): void { function updateObject (_id: Ref<Funnel>): void {
query.query(lead.class.Funnel, { _id }, (result) => { query.query(lead.class.Funnel, { _id }, (result) => {
@ -58,8 +57,6 @@
{#if object} {#if object}
<Panel <Panel
title={object.name} title={object.name}
subtitle={object.description}
icon={clazz.icon}
isHeader={false} isHeader={false}
isAside={true} isAside={true}
{object} {object}

View File

@ -187,7 +187,7 @@
}} }}
/> />
{:else if component && _id && _class} {:else if component && _id && _class}
<Component is={component} props={{ _id, _class, embedded: true }} on:close={() => select(undefined)} /> <Component is={component} props={{ _id, _class }} on:close={() => select(undefined)} />
{/if} {/if}
</div> </div>
</div> </div>

View File

@ -23,14 +23,22 @@
import { Vacancy } from '@hcengineering/recruit' import { Vacancy } from '@hcengineering/recruit'
import tracker from '@hcengineering/tracker' import tracker from '@hcengineering/tracker'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import { Button, Component, EditBox, IconMixin, IconMoreH, Label, LinkWrapper, showPopup } from '@hcengineering/ui' import {
import { ContextMenu, DocAttributeBar } from '@hcengineering/view-resources' Button,
Component,
EditBox,
IconMixin,
IconMoreH,
Label,
showPopup,
deviceOptionsStore as deviceInfo
} from '@hcengineering/ui'
import { ContextMenu, DocAttributeBar, DocNavLink } from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy } from 'svelte' import { createEventDispatcher, onDestroy } from 'svelte'
import recruit from '../plugin' import recruit from '../plugin'
import VacancyApplications from './VacancyApplications.svelte' import VacancyApplications from './VacancyApplications.svelte'
export let _id: Ref<Vacancy> export let _id: Ref<Vacancy>
export let embedded = false
let object: Required<Vacancy> let object: Required<Vacancy>
let rawName: string = '' let rawName: string = ''
@ -50,7 +58,7 @@
const client = getClient() const client = getClient()
const query = createQuery() const query = createQuery()
const clazz = client.getHierarchy().getClass(recruit.class.Vacancy) // const clazz = client.getHierarchy().getClass(recruit.class.Vacancy)
function updateObject (_id: Ref<Vacancy>): void { function updateObject (_id: Ref<Vacancy>): void {
if (lastId !== _id) { if (lastId !== _id) {
@ -125,26 +133,19 @@
{#if object} {#if object}
<Panel <Panel
icon={clazz.icon}
title={object.name}
isHeader={false} isHeader={false}
isSub={false} isSub={false}
isAside={true} isAside={true}
{embedded}
{object} {object}
on:open on:open
on:close={() => { on:close={() => {
dispatch('close') dispatch('close')
}} }}
> >
<svelte:fragment slot="subtitle"> <svelte:fragment slot="title">
{#if object.description} <DocNavLink noUnderline {object}>
<div class="flex"> <div class="title">{object.name}</div>
<span class="overflow-label" title={object.description}> </DocNavLink>
<LinkWrapper text={object.description} />
</span>
</div>
{/if}
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="attributes" let:direction={dir}> <svelte:fragment slot="attributes" let:direction={dir}>
@ -159,7 +160,7 @@
placeholder={recruit.string.VacancyPlaceholder} placeholder={recruit.string.VacancyPlaceholder}
kind={'large-style'} kind={'large-style'}
focusable focusable
autoFocus={!embedded} autoFocus={!$deviceInfo.isMobile}
on:blur={save} on:blur={save}
/> />
</span> </span>
@ -170,10 +171,11 @@
{/if} {/if}
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="utils"> <svelte:fragment slot="utils">
<Button icon={IconMoreH} kind={'ghost'} size={'medium'} on:click={showMenu} /> <Button icon={IconMoreH} iconProps={{ size: 'medium' }} kind={'icon'} on:click={showMenu} />
<Button <Button
icon={IconMixin} icon={IconMixin}
kind={'ghost'} kind={'icon'}
iconProps={{ size: 'medium' }}
selected={showAllMixins} selected={showAllMixins}
on:click={() => { on:click={() => {
showAllMixins = !showAllMixins showAllMixins = !showAllMixins

View File

@ -24,7 +24,6 @@
export let _id: Ref<Integration> export let _id: Ref<Integration>
export let _class: Ref<Class<Integration>> export let _class: Ref<Class<Integration>>
export let embedded = false
let integration: Integration | undefined = undefined let integration: Integration | undefined = undefined
const query = createQuery() const query = createQuery()
@ -54,16 +53,7 @@
</script> </script>
{#if integration} {#if integration}
<Panel <Panel {title} object={integration} isHeader={false} isAside={false} withoutActivity withoutInput>
icon={setting.icon.Integrations}
{title}
object={integration}
{embedded}
isHeader={false}
isAside={false}
withoutActivity
withoutInput
>
<div class="max-w-80 min-w-80"> <div class="max-w-80 min-w-80">
{#if type} {#if type}
<PluginCard {integration} integrationType={type} /> <PluginCard {integration} integrationType={type} />

View File

@ -24,10 +24,11 @@
import { activeProjects } from '../../utils' import { activeProjects } from '../../utils'
export let value: WithLookup<Issue> export let value: WithLookup<Issue>
export let disabled = false export let disabled: boolean = false
export let onClick: (() => void) | undefined = undefined export let onClick: (() => void) | undefined = undefined
export let shouldShowAvatar: boolean = false export let shouldShowAvatar: boolean = false
export let noUnderline = disabled export let noUnderline: boolean = disabled
export let noSelect: boolean = false
export let inline = false export let inline = false
export let kind: 'list' | undefined = undefined export let kind: 'list' | undefined = undefined
export let icon: Asset | AnySvelteComponent | undefined = undefined export let icon: Asset | AnySvelteComponent | undefined = undefined
@ -64,7 +65,7 @@
<Icon icon={icon ?? tracker.icon.Issues} size={'small'} /> <Icon icon={icon ?? tracker.icon.Issues} size={'small'} />
</div> </div>
{/if} {/if}
<span class="overflow-label select-text" title={value?.title}> <span class="overflow-label" class:select-text={!noSelect} title={value?.title}>
{title} {title}
<slot name="details" /> <slot name="details" />
</span> </span>

View File

@ -24,7 +24,8 @@
<Button <Button
{icon} {icon}
kind={'ghost'} iconProps={{ size: 'medium' }}
kind={'icon'}
showTooltip={{ label: title, direction: 'bottom' }} showTooltip={{ label: title, direction: 'bottom' }}
on:click={() => copyTextToClipboard(text)} on:click={() => copyTextToClipboard(text)}
/> />

View File

@ -40,9 +40,10 @@
createFocusManager, createFocusManager,
getCurrentResolvedLocation, getCurrentResolvedLocation,
navigate, navigate,
showPopup showPopup,
deviceOptionsStore as deviceInfo
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { ContextMenu, DocNavLink, ParentsNavigator, UpDownNavigator } from '@hcengineering/view-resources' import { ContextMenu, DocNavLink, ParentsNavigator } from '@hcengineering/view-resources'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import { createEventDispatcher, onDestroy } from 'svelte' import { createEventDispatcher, onDestroy } from 'svelte'
import { generateIssueShortLink, getIssueId } from '../../../issues' import { generateIssueShortLink, getIssueId } from '../../../issues'
@ -55,7 +56,6 @@
export let _id: Ref<Issue> export let _id: Ref<Issue>
export let _class: Ref<Class<Issue>> export let _class: Ref<Class<Issue>>
export let embedded = false
let lastId: Ref<Doc> = _id let lastId: Ref<Doc> = _id
const queryClient = createQuery() const queryClient = createQuery()
@ -165,14 +165,12 @@
let content: HTMLElement let content: HTMLElement
</script> </script>
{#if !embedded}
<FocusHandler {manager} isEnabled={isContextEnabled} /> <FocusHandler {manager} isEnabled={isContextEnabled} />
<ActionContext <ActionContext
context={{ context={{
mode: 'editor' mode: 'editor'
}} }}
/> />
{/if}
{#if issue !== undefined} {#if issue !== undefined}
<Panel <Panel
@ -181,26 +179,19 @@
isAside={true} isAside={true}
isSub={false} isSub={false}
withoutActivity={false} withoutActivity={false}
withoutTitle
bind:content bind:content
{embedded}
bind:innerWidth bind:innerWidth
on:open on:open
on:close={() => dispatch('close')} on:close={() => dispatch('close')}
on:select
> >
<svelte:fragment slot="navigator"> <svelte:fragment slot="title">
{#if !embedded}
<UpDownNavigator element={issue} />
<ParentsNavigator element={issue} /> <ParentsNavigator element={issue} />
{/if} {#if issueId}
<DocNavLink noUnderline object={issue}>
<span class="ml-4 fs-title select-text-i overflow-label"> <div class="title">{issueId}</div>
{#if embedded}
<DocNavLink object={issue}>
{#if issueId}{issueId}{/if}
</DocNavLink> </DocNavLink>
{:else if issueId}{issueId}{/if} {/if}
</span>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="pre-utils"> <svelte:fragment slot="pre-utils">
<ComponentExtensions <ComponentExtensions
@ -212,6 +203,39 @@
{/if} {/if}
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="utils">
<Button icon={IconMoreH} iconProps={{ size: 'medium' }} kind={'icon'} on:click={showMenu} />
{#if issueId}
<CopyToClipboard issueUrl={generateIssueShortLink(issueId)} />
{/if}
<Button
icon={setting.icon.Setting}
kind={'icon'}
iconProps={{ size: 'medium' }}
showTooltip={{ label: setting.string.ClassSetting }}
on:click={(ev) => {
ev.stopPropagation()
const loc = getCurrentResolvedLocation()
loc.path[2] = settingId
loc.path[3] = 'setting'
loc.path[4] = 'classes'
loc.path.length = 5
loc.query = { _class }
loc.fragment = undefined
navigate(loc)
}}
/>
<Button
icon={IconMixin}
iconProps={{ size: 'medium' }}
kind={'icon'}
selected={showAllMixins}
on:click={() => {
showAllMixins = !showAllMixins
}}
/>
</svelte:fragment>
{#if parentIssue} {#if parentIssue}
<div class="mb-6"> <div class="mb-6">
{#if currentProject} {#if currentProject}
@ -227,7 +251,7 @@
placeholder={tracker.string.IssueTitlePlaceholder} placeholder={tracker.string.IssueTitlePlaceholder}
kind="large-style" kind="large-style"
on:blur={save} on:blur={save}
autoFocus={!embedded} autoFocus={!$deviceInfo.isMobile}
/> />
<div class="w-full mt-6"> <div class="w-full mt-6">
<AttachmentStyleBoxEditor <AttachmentStyleBoxEditor
@ -260,36 +284,6 @@
<span slot="actions-label" class="select-text"> <span slot="actions-label" class="select-text">
{#if issueId}{issueId}{/if} {#if issueId}{issueId}{/if}
</span> </span>
<svelte:fragment slot="utils">
<Button icon={IconMoreH} kind={'ghost'} size={'medium'} on:click={showMenu} />
{#if issueId}
<CopyToClipboard issueUrl={generateIssueShortLink(issueId)} {issueId} />
{/if}
<Button
icon={setting.icon.Setting}
kind={'ghost'}
showTooltip={{ label: setting.string.ClassSetting }}
on:click={(ev) => {
ev.stopPropagation()
const loc = getCurrentResolvedLocation()
loc.path[2] = settingId
loc.path[3] = 'setting'
loc.path[4] = 'classes'
loc.path.length = 5
loc.query = { _class }
loc.fragment = undefined
navigate(loc)
}}
/>
<Button
kind={'ghost'}
icon={IconMixin}
selected={showAllMixins}
on:click={() => {
showAllMixins = !showAllMixins
}}
/>
</svelte:fragment>
<svelte:fragment slot="custom-attributes"> <svelte:fragment slot="custom-attributes">
{#if issue && currentProject} {#if issue && currentProject}

View File

@ -22,17 +22,8 @@
import setting, { settingId } from '@hcengineering/setting' import setting, { settingId } from '@hcengineering/setting'
import tags from '@hcengineering/tags' import tags from '@hcengineering/tags'
import { IssueTemplate, IssueTemplateChild, Project } from '@hcengineering/tracker' import { IssueTemplate, IssueTemplateChild, Project } from '@hcengineering/tracker'
import { import { Button, EditBox, IconMoreH, Label, getCurrentResolvedLocation, navigate, showPopup } from '@hcengineering/ui'
Button, import { ContextMenu } from '@hcengineering/view-resources'
EditBox,
Icon,
IconMoreH,
Label,
getCurrentResolvedLocation,
navigate,
showPopup
} from '@hcengineering/ui'
import { ContextMenu, ParentsNavigator, UpDownNavigator } from '@hcengineering/view-resources'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import { createEventDispatcher, onDestroy, onMount } from 'svelte' import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import tracker from '../../plugin' import tracker from '../../plugin'
@ -42,7 +33,6 @@
export let _id: Ref<IssueTemplate> export let _id: Ref<IssueTemplate>
export let _class: Ref<Class<IssueTemplate>> export let _class: Ref<Class<IssueTemplate>>
export let embedded = false
let lastId: Ref<Doc> = _id let lastId: Ref<Doc> = _id
const query = createQuery() const query = createQuery()
@ -52,7 +42,6 @@
let template: WithLookup<IssueTemplate> | undefined let template: WithLookup<IssueTemplate> | undefined
let currentProject: Project | undefined let currentProject: Project | undefined
let title = '' let title = ''
let description = ''
let innerWidth: number let innerWidth: number
let descriptionBox: AttachmentStyleBoxEditor let descriptionBox: AttachmentStyleBoxEditor
@ -80,7 +69,6 @@
async (result) => { async (result) => {
;[template] = result ;[template] = result
title = template.title title = template.title
description = template.description
currentProject = template.$lookup?.space currentProject = template.$lookup?.space
}, },
{ lookup: { space: tracker.class.Project, labels: tags.class.TagElement } } { lookup: { space: tracker.class.Project, labels: tags.class.TagElement } }
@ -149,30 +137,16 @@
{#if template !== undefined} {#if template !== undefined}
<Panel <Panel
title={template.title}
object={template} object={template}
isHeader={false} isHeader={false}
isAside={true} isAside={true}
isSub={false} isSub={false}
withoutActivity={false} withoutActivity={false}
{embedded}
bind:innerWidth bind:innerWidth
on:open on:open
on:close={() => dispatch('close')} on:close={() => dispatch('close')}
> >
<svelte:fragment slot="navigator">
{#if !embedded}
<UpDownNavigator element={template} />
<ParentsNavigator element={template} />
{/if}
<div class="ml-2">
<Icon icon={tracker.icon.IssueTemplates} size={'small'} />
</div>
<span class="fs-title flex-row-center">
{template.title}
</span>
</svelte:fragment>
<EditBox bind:value={title} placeholder={tracker.string.IssueTitlePlaceholder} kind="large-style" on:blur={save} /> <EditBox bind:value={title} placeholder={tracker.string.IssueTitlePlaceholder} kind="large-style" on:blur={save} />
<div class="w-full mt-6"> <div class="w-full mt-6">
<AttachmentStyleBoxEditor <AttachmentStyleBoxEditor
@ -207,10 +181,11 @@
{/if} {/if}
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="utils"> <svelte:fragment slot="utils">
<Button icon={IconMoreH} kind={'ghost'} size={'medium'} on:click={showMenu} /> <Button icon={IconMoreH} iconProps={{ size: 'medium' }} kind={'icon'} on:click={showMenu} />
<Button <Button
icon={setting.icon.Setting} icon={setting.icon.Setting}
kind={'ghost'} iconProps={{ size: 'medium' }}
kind={'icon'}
showTooltip={{ label: setting.string.ClassSetting }} showTooltip={{ label: setting.string.ClassSetting }}
on:click={(ev) => { on:click={(ev) => {
ev.stopPropagation() ev.stopPropagation()

View File

@ -63,9 +63,9 @@
<path d="M5 11C4.72386 11 4.5 11.2239 4.5 11.5C4.5 11.7761 4.72386 12 5 12H8C8.27614 12 8.5 11.7761 8.5 11.5C8.5 11.2239 8.27614 11 8 11H5Z" /> <path d="M5 11C4.72386 11 4.5 11.2239 4.5 11.5C4.5 11.7761 4.72386 12 5 12H8C8.27614 12 8.5 11.7761 8.5 11.5C8.5 11.2239 8.27614 11 8 11H5Z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 1C10.1531 1 10.7087 1.4174 10.9146 2H11.5C12.6046 2 13.5 2.89543 13.5 4V13C13.5 14.1046 12.6046 15 11.5 15H4.5C3.39543 15 2.5 14.1046 2.5 13V4C2.5 2.89543 3.39543 2 4.5 2H5.08535C5.29127 1.4174 5.84689 1 6.5 1H9.5ZM6 2.5C6 2.22386 6.22386 2 6.5 2H9.5C9.77614 2 10 2.22386 10 2.5C10 2.77614 9.77614 3 9.5 3H6.5C6.22386 3 6 2.77614 6 2.5ZM9.5 4C10.1531 4 10.7087 3.5826 10.9146 3H11.5C12.0523 3 12.5 3.44772 12.5 4V13C12.5 13.5523 12.0523 14 11.5 14H4.5C3.94772 14 3.5 13.5523 3.5 13V4C3.5 3.44772 3.94772 3 4.5 3H5.08535C5.29127 3.5826 5.84689 4 6.5 4H9.5Z" /> <path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 1C10.1531 1 10.7087 1.4174 10.9146 2H11.5C12.6046 2 13.5 2.89543 13.5 4V13C13.5 14.1046 12.6046 15 11.5 15H4.5C3.39543 15 2.5 14.1046 2.5 13V4C2.5 2.89543 3.39543 2 4.5 2H5.08535C5.29127 1.4174 5.84689 1 6.5 1H9.5ZM6 2.5C6 2.22386 6.22386 2 6.5 2H9.5C9.77614 2 10 2.22386 10 2.5C10 2.77614 9.77614 3 9.5 3H6.5C6.22386 3 6 2.77614 6 2.5ZM9.5 4C10.1531 4 10.7087 3.5826 10.9146 3H11.5C12.0523 3 12.5 3.44772 12.5 4V13C12.5 13.5523 12.0523 14 11.5 14H4.5C3.94772 14 3.5 13.5523 3.5 13V4C3.5 3.44772 3.94772 3 4.5 3H5.08535C5.29127 3.5826 5.84689 4 6.5 4H9.5Z" />
</symbol> </symbol>
<symbol id="copyLink" viewBox="0 0 16 16"> <symbol id="copyLink" viewBox="0 0 32 32">
<path d="M14.6,3.4c-0.3-0.3-0.6-0.5-1-0.7c-0.4-0.2-0.8-0.2-1.2-0.2c-0.4,0-0.8,0.1-1.2,0.2c-0.2,0.1-0.4,0.2-0.6,0.3c-0.2,0.2-0.2,0.5,0,0.7c0.2,0.2,0.5,0.2,0.7,0c0.1,0,0.2-0.1,0.3-0.1c0.2-0.1,0.5-0.2,0.8-0.2c0.3,0,0.5,0.1,0.8,0.2s0.5,0.2,0.7,0.4c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.2,0.2,0.5,0.2,0.8c0,0.3-0.1,0.5-0.2,0.8c-0.1,0.2-0.2,0.5-0.4,0.7l-4,4c-0.4,0.4-0.9,0.6-1.4,0.6c-0.5,0-1-0.2-1.4-0.6C6.7,10.6,6.5,10,6.5,9.5c0-0.5,0.2-1,0.6-1.4l0.4-0.4c0.2-0.2,0.2-0.5,0-0.7C7.2,6.8,6.9,6.8,6.7,7L6.4,7.4C6.1,7.7,5.9,8,5.7,8.4C5.6,8.7,5.5,9.1,5.5,9.5c0,0.4,0.1,0.8,0.2,1.2c0.2,0.4,0.4,0.7,0.7,1c0.6,0.6,1.3,0.9,2.1,0.9c0.4,0,0.8-0.1,1.2-0.2c0.4-0.2,0.7-0.4,1-0.7l4-4c0.6-0.6,0.9-1.3,0.9-2.1C15.5,4.7,15.2,3.9,14.6,3.4z" /> <path d="M29.25 6.75997C28.6926 6.20061 28.0302 5.75679 27.3009 5.45396C26.5716 5.15112 25.7897 4.99524 25 4.99524C24.2103 4.99524 23.4284 5.15112 22.6991 5.45396C22.278 5.62881 21.8792 5.85065 21.5101 6.1146C21.0614 6.43545 21.0693 7.07931 21.4594 7.46935C21.8499 7.85988 22.4827 7.84994 22.9575 7.5679C23.1218 7.47029 23.2933 7.38434 23.4707 7.31086C23.9571 7.10938 24.4785 7.00567 25.005 7.00567C25.5315 7.00567 26.0528 7.10938 26.5393 7.31086C27.0257 7.51235 27.4677 7.80767 27.84 8.17997C28.2123 8.55227 28.5076 8.99425 28.7091 9.48068C28.9106 9.96711 29.0143 10.4885 29.0143 11.015C29.0143 11.5415 28.9106 12.0628 28.7091 12.5493C28.5076 13.0357 28.2123 13.4777 27.84 13.85L19.84 21.85C19.0894 22.6019 18.0709 23.0248 17.0085 23.0257C15.9461 23.0267 14.9269 22.6055 14.175 21.855C13.4231 21.1044 13.0001 20.0859 12.9992 19.0235C12.9983 17.9611 13.4194 16.9419 14.17 16.19L14.8803 15.4746C15.2675 15.0846 15.2659 14.4537 14.8787 14.0637C14.4886 13.6709 13.8519 13.6681 13.4604 14.0596L12.75 14.77C12.1906 15.3274 11.7468 15.9897 11.444 16.7191C11.1411 17.4484 10.9852 18.2303 10.9852 19.02C10.9852 19.8097 11.1411 20.5916 11.444 21.3209C11.7468 22.0502 12.1906 22.7125 12.75 23.27C13.8815 24.387 15.41 25.0092 17 25C17.7927 25.0032 18.5782 24.8494 19.3111 24.5473C20.044 24.2452 20.7098 23.8009 21.27 23.24L29.27 15.24C30.3909 14.1123 31.0184 12.5858 31.0147 10.9958C31.0109 9.40582 30.3762 7.88232 29.25 6.75997Z" />
<path d="M2.1,12.4c-0.2-0.2-0.3-0.4-0.4-0.6c-0.1-0.2-0.2-0.5-0.2-0.8c0-0.3,0.1-0.5,0.2-0.8C1.8,10,1.9,9.8,2.1,9.6l4-4c0.2-0.2,0.4-0.3,0.6-0.4C7,5,7.2,5,7.5,5C7.8,5,8,5,8.3,5.1c0.2,0.1,0.5,0.2,0.6,0.4C9.1,5.8,9.3,6,9.4,6.2C9.5,6.5,9.5,6.7,9.5,7c0,0.3,0,0.5-0.1,0.8C9.2,8,9.1,8.2,8.9,8.4L8.2,9.1C8,9.3,8,9.7,8.2,9.9c0.2,0.2,0.5,0.2,0.7,0l0.7-0.7c0.6-0.6,0.9-1.3,0.9-2.1c0-0.8-0.3-1.6-0.9-2.1C9.1,4.3,8.3,4,7.5,4C6.7,4,5.9,4.3,5.4,4.9l-4,4c-0.3,0.3-0.5,0.6-0.7,1c-0.2,0.4-0.2,0.8-0.2,1.2c0,0.4,0.1,0.8,0.2,1.2c0.2,0.4,0.4,0.7,0.7,1C1.9,13.7,2.7,14,3.5,14c0.6,0,1.2-0.2,1.8-0.6c0.2-0.2,0.2-0.5,0-0.7c-0.2-0.2-0.5-0.2-0.7,0c-0.1,0-0.2,0.1-0.3,0.1C4,12.9,3.8,13,3.5,13c-0.3,0-0.5-0.1-0.8-0.2S2.3,12.6,2.1,12.4z" /> <path d="M4.18997 24.82C3.81656 24.4483 3.52026 24.0065 3.31807 23.52C3.11589 23.0335 3.01181 22.5118 3.01181 21.985C3.01181 21.4581 3.11589 20.9365 3.31807 20.4499C3.52026 19.9634 3.81656 19.5216 4.18997 19.15L12.19 11.15C12.5616 10.7766 13.0034 10.4803 13.4899 10.2781C13.9765 10.0759 14.4981 9.97181 15.025 9.97181C15.5518 9.97181 16.0735 10.0759 16.56 10.2781C17.0465 10.4803 17.4883 10.7766 17.86 11.15C18.231 11.5246 18.5231 11.9698 18.7189 12.4594C18.9147 12.9489 19.0103 13.4728 19 14C19.003 14.5288 18.9012 15.0529 18.7004 15.5421C18.4995 16.0313 18.2037 16.4758 17.83 16.85L16.4072 18.2929C16.0213 18.6842 16.0289 19.3189 16.4175 19.7075C16.808 20.098 17.4466 20.1034 17.8371 19.7129L19.25 18.3C20.3785 17.1715 21.0124 15.6409 21.0124 14.045C21.0124 12.449 20.3785 10.9185 19.25 9.78997C18.1215 8.66147 16.5909 8.02749 14.995 8.02749C13.399 8.02749 11.8685 8.66147 10.74 9.78997L2.73997 17.79C2.17911 18.3476 1.73401 19.0106 1.43029 19.7408C1.12657 20.471 0.970215 21.2541 0.970215 22.045C0.970215 22.8358 1.12657 23.6189 1.43029 24.3492C1.73401 25.0794 2.17911 25.7424 2.73997 26.3C3.87879 27.4084 5.41087 28.0198 6.99997 28C8.26594 28.0012 9.49169 27.6069 10.5118 26.885C10.964 26.5651 10.961 25.921 10.5693 25.5293C10.1781 25.1381 9.54699 25.1518 9.0717 25.4348C8.90786 25.5324 8.73689 25.6184 8.56 25.6919C8.07349 25.8941 7.55182 25.9981 7.02497 25.9981C6.49812 25.9981 5.97645 25.8941 5.48994 25.6919C5.00342 25.4897 4.56164 25.1934 4.18997 24.82Z" />
</symbol> </symbol>
<symbol id="arrow-right" viewBox="0 0 16 16"> <symbol id="arrow-right" viewBox="0 0 16 16">

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -15,10 +15,10 @@
--> -->
<script lang="ts"> <script lang="ts">
import contact, { Contact, getName } from '@hcengineering/contact' import contact, { Contact, getName } from '@hcengineering/contact'
import core, { Class, ClassifierKind, Doc, Mixin, Obj, Ref } from '@hcengineering/core' import core, { Class, ClassifierKind, Doc, Mixin, Ref } from '@hcengineering/core'
import notification from '@hcengineering/notification' import notification from '@hcengineering/notification'
import { Panel } from '@hcengineering/panel' import { Panel } from '@hcengineering/panel'
import { Asset, getResource, translate } from '@hcengineering/platform' import { getResource, translate } from '@hcengineering/platform'
import { import {
AttributeCategory, AttributeCategory,
AttributeCategoryOrder, AttributeCategoryOrder,
@ -33,14 +33,12 @@
import { AnyComponent, Button, Component, IconMixin, IconMoreH, showPopup, themeStore } from '@hcengineering/ui' import { AnyComponent, Button, Component, IconMixin, IconMoreH, showPopup, themeStore } from '@hcengineering/ui'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import { createEventDispatcher, onDestroy } from 'svelte' import { createEventDispatcher, onDestroy } from 'svelte'
import { ContextMenu, ParentsNavigator } from '..' import { ContextMenu, ParentsNavigator, DocNavLink } from '..'
import { categorizeFields, getCollectionCounter, getFiltredKeys } from '../utils' import { categorizeFields, getCollectionCounter, getFiltredKeys } from '../utils'
import DocAttributeBar from './DocAttributeBar.svelte' import DocAttributeBar from './DocAttributeBar.svelte'
import UpDownNavigator from './UpDownNavigator.svelte'
export let _id: Ref<Doc> export let _id: Ref<Doc>
export let _class: Ref<Class<Doc>> export let _class: Ref<Class<Doc>>
export let embedded = false
let realObjectClass: Ref<Class<Doc>> = _class let realObjectClass: Ref<Class<Doc>> = _class
let lastId: Ref<Doc> = _id let lastId: Ref<Doc> = _id
@ -203,21 +201,6 @@
} }
} }
function getIcon (_class: Ref<Class<Obj>> | undefined): Asset | undefined {
if (_class === undefined) return undefined
let clazz = hierarchy.getClass(_class)
if (clazz.icon !== undefined) return clazz.icon
while (clazz.extends !== undefined) {
clazz = hierarchy.getClass(clazz.extends)
if (clazz.icon != null) {
return clazz.icon
}
}
// throw new Error(`Icon not found for ${_class}`)
}
$: icon = getIcon(realObjectClass)
let title: string = '' let title: string = ''
let rawTitle: string = '' let rawTitle: string = ''
@ -282,20 +265,15 @@
let content: HTMLElement let content: HTMLElement
</script> </script>
{#if !embedded}
<ActionContext <ActionContext
context={{ context={{
mode: 'editor' mode: 'editor'
}} }}
/> />
{/if}
{#if object !== undefined && finalTitle !== undefined} {#if object !== undefined && finalTitle !== undefined}
<Panel <Panel
{icon}
title={finalTitle}
{object} {object}
{embedded}
isHeader={mainEditor?.pinned ?? false} isHeader={mainEditor?.pinned ?? false}
isAside={true} isAside={true}
bind:content bind:content
@ -309,18 +287,19 @@
withoutActivity={!activityOptions.enabled} withoutActivity={!activityOptions.enabled}
withoutInput={!activityOptions.showInput} withoutInput={!activityOptions.showInput}
> >
<svelte:fragment slot="navigator"> <svelte:fragment slot="title">
{#if !embedded}
<UpDownNavigator element={object} />
<ParentsNavigator element={object} /> <ParentsNavigator element={object} />
{/if} <DocNavLink noUnderline {object}>
<div class="title">{finalTitle}</div>
</DocNavLink>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="utils"> <svelte:fragment slot="utils">
<Button icon={IconMoreH} kind={'ghost'} size={'medium'} on:click={showMenu} /> <Button icon={IconMoreH} iconProps={{ size: 'medium' }} kind={'icon'} on:click={showMenu} />
<Button <Button
icon={IconMixin} icon={IconMixin}
kind={'ghost'} iconProps={{ size: 'medium' }}
kind={'icon'}
selected={showAllMixins} selected={showAllMixins}
on:click={() => { on:click={() => {
showAllMixins = !showAllMixins showAllMixins = !showAllMixins
@ -333,7 +312,7 @@
{#if headerEditor !== undefined} {#if headerEditor !== undefined}
<Component <Component
is={headerEditor} is={headerEditor}
props={{ object, keys, mixins, ignoreKeys, vertical: dir === 'column', allowedCollections, embedded }} props={{ object, keys, mixins, ignoreKeys, vertical: dir === 'column', allowedCollections }}
on:update={updateKeys} on:update={updateKeys}
/> />
{:else if dir === 'column'} {:else if dir === 'column'}

View File

@ -58,7 +58,13 @@
if (attributeModel) { if (attributeModel) {
const breadcrumbsModel: BreadcrumbsModel = { const breadcrumbsModel: BreadcrumbsModel = {
component: attributeModel.presenter, component: attributeModel.presenter,
props: { shouldShowAvatar: false, ...(attributeModel.props ?? {}), value: parent } props: {
shouldShowAvatar: false,
noUnderline: true,
noSelect: true,
...(attributeModel.props ?? {}),
value: parent
}
} }
models.push(breadcrumbsModel) models.push(breadcrumbsModel)
@ -71,6 +77,6 @@
{#await getBreadcrumbsModels(element) then models} {#await getBreadcrumbsModels(element) then models}
{#if models.length > 0} {#if models.length > 0}
<Breadcrumbs {models} gap="none" /> <Breadcrumbs {models} />
{/if} {/if}
{/await} {/await}

View File

@ -97,7 +97,7 @@
Mem: {data.statistics.memoryUsed} / {data.statistics.memoryTotal} CPU: {data.statistics.cpuUsage} Mem: {data.statistics.memoryUsed} / {data.statistics.memoryTotal} CPU: {data.statistics.cpuUsage}
{/if} {/if}
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="navigator"> <svelte:fragment slot="title">
<span class="p-3"> Server manager </span> <span class="p-3"> Server manager </span>
<TabList <TabList
items={tabs} items={tabs}

View File

@ -17,7 +17,7 @@
import type { Class, Ref, Space } from '@hcengineering/core' import type { Class, Ref, Space } from '@hcengineering/core'
import core from '@hcengineering/core' import core from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import { EditBox, Icon, Label, Scroller, Panel, Component } from '@hcengineering/ui' import { EditBox, Label, Scroller, Panel, Component } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import workbench from '../../plugin' import workbench from '../../plugin'
import contact from '@hcengineering/contact' import contact from '@hcengineering/contact'
@ -62,12 +62,7 @@
> >
<svelte:fragment slot="title"> <svelte:fragment slot="title">
{#if clazz} {#if clazz}
<div class="antiTitle icon-wrapper"> <div class="title not-active"><Label label={clazz.label} /></div>
<div class="wrapped-icon">
{#if clazz.icon}<Icon icon={clazz.icon} size={'medium'} />{/if}
</div>
<span class="wrapped-title"><Label label={clazz.label} /></span>
</div>
{/if} {/if}
</svelte:fragment> </svelte:fragment>

View File

@ -9,7 +9,7 @@ export class ApplicationsDetailsPage extends CommonRecruitingPage {
constructor (page: Page) { constructor (page: Page) {
super(page) super(page)
this.page = page this.page = page
this.textApplicationId = page.locator('div.popupPanel-title div.title-wrapper > span') this.textApplicationId = page.locator('div.popupPanel-title a.noUnderline > div.title')
this.buttonState = page this.buttonState = page
.locator('div[class*="collapsed-container"]') .locator('div[class*="collapsed-container"]')
.nth(0) .nth(0)

View File

@ -22,7 +22,7 @@ export class CommonRecruitingPage extends CalendarPage {
this.inputAddAttachment = page.locator('div.antiSection #file') this.inputAddAttachment = page.locator('div.antiSection #file')
this.textAttachmentName = page.locator('div.name a') this.textAttachmentName = page.locator('div.name a')
this.buttonCreateFirstReview = page.locator('span:has-text("Create review")') this.buttonCreateFirstReview = page.locator('span:has-text("Create review")')
this.buttonMoreActions = page.locator('div.popupPanel-title div.buttons-group > button:nth-of-type(2)') this.buttonMoreActions = page.locator('.popupPanel-title > .flex-row-center > button >> nth=0')
this.buttonDelete = page.locator('button[class*="menuItem"] span', { hasText: 'Delete' }) this.buttonDelete = page.locator('button[class*="menuItem"] span', { hasText: 'Delete' })
} }