Update Panel layout. (#1591)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2022-04-29 11:29:26 +03:00 committed by GitHub
parent 21f0928e71
commit c4267cc7ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 767 additions and 531 deletions

View File

@ -19,49 +19,108 @@
import type { Doc } from '@anticrm/core' import type { Doc } from '@anticrm/core'
import notification from '@anticrm/notification' import notification from '@anticrm/notification'
import type { Asset } from '@anticrm/platform' import type { Asset } from '@anticrm/platform'
import { AnyComponent, AnySvelteComponent, Component, Panel } from '@anticrm/ui' import { AnyComponent, AnySvelteComponent, Component, Panel, Icon } from '@anticrm/ui'
export let title: string | undefined = undefined export let title: string | undefined = undefined
export let subtitle: string | undefined = undefined export let subtitle: string | undefined = undefined
export let icon: Asset | AnySvelteComponent | undefined = undefined export let icon: Asset | AnySvelteComponent | undefined = undefined
export let showHeader: boolean = true
export let rightSection: AnyComponent | undefined = undefined export let rightSection: AnyComponent | undefined = undefined
export let object: Doc export let object: Doc
export let panelWidth: number = 0 export let panelWidth: number = 0
export let innerWidth: number = 0 export let innerWidth: number = 0
export let isSubtitle: boolean = false export let isHeader: boolean = true
export let isProperties: boolean = false export let isSub: boolean = true
export let isAside: boolean = true
export let minimize: boolean = false
let docWidth: number = 0
$: minimize = docWidth < 1280 && docWidth >= 1024
$: needHeader = $$slots.header || minimize || isHeader
</script> </script>
<svelte:window bind:innerWidth={docWidth} />
<Panel <Panel
{title}
{subtitle}
{icon}
rightSection={rightSection !== undefined} rightSection={rightSection !== undefined}
{showHeader} bind:isAside
isHeader={needHeader}
bind:panelWidth bind:panelWidth
bind:innerWidth bind:innerWidth
isProperties={innerWidth >= 500 || isProperties}
isSubtitle={innerWidth < 900 || isSubtitle}
on:close on:close
> >
<svelte:fragment slot="subtitle"> <svelte:fragment slot="title">
{#if $$slots.subtitle}<slot name="subtitle" />{/if} <div class="popupPanel-title__content-container antiTitle">
{#if $$slots.navigator}
<div class="buttons-group xsmall-gap mr-4">
<slot name="navigator" />
</div>
{/if}
{#if $$slots.title}
<slot name="title" />
{:else}
<div class="icon-wrapper">
{#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}<span class="wrapped-subtitle">{subtitle}</span>{/if}
</div>
</div>
{/if}
</div>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="properties">
{#if $$slots.properties}<slot name="properties" />{/if} <svelte:fragment slot="utils">
</svelte:fragment>
<svelte:fragment slot="navigate-actions">
<slot name="navigate-actions" />
</svelte:fragment>
<svelte:fragment slot="commands">
<div class="buttons-group xsmall-gap">
<Component is={calendar.component.DocReminder} props={{ value: object, title }} /> <Component is={calendar.component.DocReminder} props={{ value: object, title }} />
<Component is={notification.component.LastViewEditor} props={{ value: object }} /> <Component is={notification.component.LastViewEditor} props={{ value: object }} />
</div> {#if $$slots.utils}
<slot name="actions" /> <div class="buttons-divider" />
<slot name="utils" />
{/if}
</svelte:fragment> </svelte:fragment>
<Component is={rightSection ?? activity.component.Activity} props={{ object, integrate: true }}>
<svelte:fragment slot="header">
{#if $$slots.header || ($$slots.actions && minimize)}
<div class="header-row between">
{#if $$slots.header}<slot name="header" />{/if}
<div class="buttons-group xsmall-gap ml-4">
<slot name="tools" />
{#if $$slots.actions && minimize}
<div class="buttons-divider" />
<slot name="actions" />
{/if}
</div>
</div>
{/if}
{#if $$slots['custom-attributes']}
{#if isSub}<div class="header-row"><slot name="custom-attributes" direction="row" /></div>{/if}
{:else}
{#if $$slots.attributes && minimize}<div class="header-row"><slot name="attributes" direction="row" /></div>{/if}
{/if}
</svelte:fragment>
<svelte:fragment slot="aside">
<div style="padding: .75rem 1.5rem">
{#if $$slots.actions}
<div class="flex-row-center pb-3 bottom-divider">
<span class="fs-bold w-24 mr-6"><slot name="actions-label" /></span>
<div class="buttons-group xsmall-gap">
<slot name="actions" />
</div>
</div>
{/if}
{#if $$slots['custom-attributes']}
<slot name="custom-attributes" direction="column" />
{:else}
{#if $$slots.attributes}<slot name="attributes" direction="column" />{/if}
{/if}
{#if $$slots.aside}<slot name="aside" />{/if}
</div>
</svelte:fragment>
{#if rightSection !== undefined}
<slot />
{:else}
<Component is={activity.component.Activity} props={{ object, integrate: true }}>
<slot /> <slot />
</Component> </Component>
{/if}
</Panel> </Panel>

View File

@ -80,6 +80,8 @@
--popup-bg-hover: #37373c; --popup-bg-hover: #37373c;
--popup-divider: #313236; --popup-divider: #313236;
--popup-shadow: rgb(0 0 0 / 20%) 0px 4px 24px; --popup-shadow: rgb(0 0 0 / 20%) 0px 4px 24px;
--popup-panel-shadow: rgb(0 0 0 / 55%) 0px 7px 24px;
--popup-aside-shadow: rgb(0 0 0 / 25%) 0px 8px 16px;
--card-shadow: rgb(0 0 0 / 50%) 0px 16px 70px; --card-shadow: rgb(0 0 0 / 50%) 0px 16px 70px;
--card-overlay-color: rgba(28, 29, 31, .5); --card-overlay-color: rgba(28, 29, 31, .5);
--avatar-bg-color: #4f5358; --avatar-bg-color: #4f5358;
@ -220,6 +222,7 @@
--popup-bg-hover: #f8f9fb; --popup-bg-hover: #f8f9fb;
--popup-divider: #eff1f4; --popup-divider: #eff1f4;
--popup-shadow: rgb(0 0 0 / 20%) 0px 4px 24px; // Dark --popup-shadow: rgb(0 0 0 / 20%) 0px 4px 24px; // Dark
--popup-panel-shadow: rgb(0 0 0 / 55%) 0px 7px 24px; // Dark
--card-shadow: rgb(0 0 0 / 50%) 0px 16px 70px; --card-shadow: rgb(0 0 0 / 50%) 0px 16px 70px;
--card-overlay-color: rgba(144, 149, 157, .4); --card-overlay-color: rgba(144, 149, 157, .4);
--avatar-bg-color: #e0e0e0; // HZ --avatar-bg-color: #e0e0e0; // HZ

View File

@ -263,6 +263,12 @@ p:last-child { margin-block-end: 0; }
grid-auto-flow: row; grid-auto-flow: row;
} }
} }
.buttons-divider {
min-width: 1px;
width: 1px;
height: 1.5rem;
background-color: var(--divider-color);
}
.gap-1, .gap-1-5, .gap-2 { .gap-1, .gap-1-5, .gap-2 {
& > *:not(:last-child) { margin-right: .25rem; } & > *:not(:last-child) { margin-right: .25rem; }
@ -365,6 +371,7 @@ p:last-child { margin-block-end: 0; }
.pb-3 { padding-bottom: .75rem; } .pb-3 { padding-bottom: .75rem; }
.pb-4 { padding-bottom: 1rem; } .pb-4 { padding-bottom: 1rem; }
.px-2 { padding: 0 .5rem; } .px-2 { padding: 0 .5rem; }
.px-3 { padding: 0 .75rem; }
.px-4 { padding: 0 1rem; } .px-4 { padding: 0 1rem; }
.p-1 { padding: .25rem; } .p-1 { padding: .25rem; }
@ -419,6 +426,7 @@ p:last-child { margin-block-end: 0; }
.h-7 { height: 1.75rem; } .h-7 { height: 1.75rem; }
.h-8 { height: 2rem; } .h-8 { height: 2rem; }
.h-9 { height: 2.25rem; } .h-9 { height: 2.25rem; }
.h-14 { height: 3.5rem; }
.h-16 { height: 4rem; } .h-16 { height: 4rem; }
.h-18 { height: 4.5rem; } .h-18 { height: 4.5rem; }
.w-full { width: 100%; } .w-full { width: 100%; }

View File

@ -202,3 +202,48 @@
.antiNav-bottomFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1px, rgba(0, 0, 0, 1) calc(100% - 2rem), rgba(0, 0, 0, 0) 100%); } .antiNav-bottomFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1px, rgba(0, 0, 0, 1) calc(100% - 2rem), rgba(0, 0, 0, 0) 100%); }
.antiNav-bothFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 2rem, rgba(0, 0, 0, 1) calc(100% - 2rem), rgba(0, 0, 0, 0) 100%); } .antiNav-bothFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 2rem, rgba(0, 0, 0, 1) calc(100% - 2rem), rgba(0, 0, 0, 0) 100%); }
.antiNav-noneFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1px, rgba(0, 0, 0, 1) calc(100% - 1px), rgba(0, 0, 0, 0) 100%); } .antiNav-noneFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1px, rgba(0, 0, 0, 1) calc(100% - 1px), rgba(0, 0, 0, 0) 100%); }
/* Basic */
.antiTitle {
.icon-wrapper,
.title-wrapper {
display: flex;
flex-wrap: nowrap;
min-width: 0;
}
.title-wrapper {
flex-direction: column;
flex-grow: 1;
}
.icon-wrapper { align-items: center; }
.wrapped-icon {
margin-right: .75rem;
color: var(--content-color);
}
.wrapped-title {
min-width: 0;
font-weight: 500;
font-size: 1rem;
color: var(--caption-color);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
user-select: none;
}
.wrapped-subtitle {
min-width: 0;
font-size: 0.75rem;
color: var(--dark-color);
overflow: hidden;
visibility: visible;
display: -webkit-box;
/* autoprefixer: ignore next */
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
user-select: none;
}
}

View File

@ -55,3 +55,159 @@
} }
} }
.popupPanel {
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
height: 100%;
border-radius: .5rem;
box-shadow: var(--popup-panel-shadow);
.popupPanel-title {
display: flex;
justify-content: stretch;
align-items: center;
flex-shrink: 0;
padding: .5rem .75rem;
width: 100%;
height: 3rem;
max-height: 3rem;
background-color: var(--board-card-bg-color);
border: 1px solid var(--divider-color);
border-bottom: none;
border-radius: .5rem .5rem 0 0;
&__content {
flex-grow: 1;
margin: 0 .75rem;
min-width: 0;
min-height: 0;
&-container {
display: flex;
justify-content: stretch;
min-width: 0;
width: 100%;
}
}
}
.popupPanel-body {
position: relative;
display: flex;
justify-content: stretch;
min-width: 0;
min-height: 0;
width: 100%;
height: 100%;
background-color: var(--body-color);
border: 1px solid var(--divider-color);
border-radius: 0 0 .5rem .5rem;
&__main, &__aside {
display: flex;
flex-direction: column;
min-width: 0;
min-height: 0;
height: 100%;
}
&__main {
flex-grow: 2;
flex-basis: 760px;
border: none;
border-radius: 0;
&-header {
display: flex;
flex-direction: column;
margin: 0 auto;
padding: .75rem 1.25rem;
width: calc(100% - 5rem);
min-width: 0;
max-width: 900px;
border-bottom: 1px solid var(--divider-color);
.header-row {
display: flex;
align-items: center;
width: 100%;
min-width: 0;
&.between { justify-content: space-between; }
}
.header-row + .header-row { margin-top: .625rem; }
}
&-content {
display: flex;
flex-direction: column;
flex-grow: 1;
flex-shrink: 0;
margin-left: auto;
margin-right: auto;
width: calc(100% - 7.5rem);
height: 100%;
max-width: 860px;
}
}
&__aside {
position: relative;
width: 25%;
min-width: 320px;
&::before {
position: absolute;
content: '';
top: 1rem;
bottom: 1rem;
left: 0;
width: 0;
border-left: 1px solid var(--divider-color);
}
&.float {
position: absolute;
flex-shrink: 0;
top: 0;
left: 100%;
width: 320px;
height: 100%;
min-width: 0;
background-color: var(--board-card-bg-color);
border-top: 1px solid var(--board-card-bg-color);
border-left: 1px solid var(--divider-color);
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);
transform: translateX(0);
&::before { content: none; }
&.shown {
box-shadow: var(--popup-aside-shadow);
transform: translateX(-100%);
&::after {
position: absolute;
content: '';
top: 0;
left: 100%;
width: 360px;
height: 100%;
background-color: var(--board-card-bg-color);
border: 1px solid var(--board-card-bg-color);
}
}
}
}
&.asideShown {
border: none;
.popupPanel-body__main {
border: 1px solid var(--divider-color);
border-radius: 0 0 .5rem .5rem;
}
}
}
}

View File

@ -32,6 +32,7 @@
export let width: string | undefined = undefined export let width: string | undefined = undefined
export let resetIconSize: boolean = false export let resetIconSize: boolean = false
export let highlight: boolean = false export let highlight: boolean = false
export let selected: boolean = false
export let focus: boolean = false export let focus: boolean = false
export let click: boolean = false export let click: boolean = false
export let title: string | undefined = undefined export let title: string | undefined = undefined
@ -60,6 +61,7 @@
class:border-radius-2={shape === 'round'} class:border-radius-2={shape === 'round'}
class:border-radius-4={shape === 'circle'} class:border-radius-4={shape === 'circle'}
class:highlight class:highlight
class:selected
disabled={disabled || loading} disabled={disabled || loading}
style={width ? 'width: ' + width : ''} style={width ? 'width: ' + width : ''}
{title} {title}
@ -215,9 +217,8 @@
} }
} }
} }
&.transparent:hover { &.transparent:hover,
background-color: var(--button-bg-hover); &.transparent.selected { background-color: var(--button-bg-hover); }
}
&.link { &.link {
padding: 0 0.875rem; padding: 0 0.875rem;
&:hover { &:hover {

View File

@ -13,152 +13,65 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import type { Asset } from '@anticrm/platform'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { AnySvelteComponent } from '../types' import { Button, IconClose, IconDetails, Scroller } from '..'
import Button from './Button.svelte'
import Icon from './Icon.svelte'
import IconClose from './icons/Close.svelte'
export let title: string | undefined = undefined
export let subtitle: string | undefined = undefined
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let rightSection: boolean = false export let rightSection: boolean = false
export let showHeader: boolean = true
export let innerWidth: number = 0 export let innerWidth: number = 0
export let panelWidth: number = 0 export let panelWidth: number = 0
export let isSubtitle: boolean = true export let isHeader: boolean = true
export let isProperties: boolean = true export let isAside: boolean = true
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let asideFloat: boolean = false
let asideShown: boolean = false
let docWidth: number
$: if (docWidth < 1024 && !asideFloat) asideFloat = true
$: if (docWidth >= 1024 && asideFloat) {
asideFloat = false
asideShown = false
}
</script> </script>
<div class="antiPanel antiComponent" bind:clientWidth={panelWidth}> <svelte:window bind:innerWidth={docWidth} />
<div class="panel-content-container"> <div class="popupPanel" bind:clientWidth={panelWidth}>
{#if showHeader} <div class="popupPanel-title">
<div class="ac-header short mirror divide highlight"> <Button icon={IconClose} kind={'transparent'} size={'medium'} on:click={() => { dispatch('close') }} />
<div class="buttons-group"> <div class="popupPanel-title__content"><slot name="title" /></div>
<div class="buttons-group xsmall-gap">
<slot name="utils" />
{#if asideFloat}
{#if $$slots.utils}<div class="buttons-divider" />{/if}
<Button <Button
icon={IconClose} icon={IconDetails}
size={'medium'}
kind={'transparent'} kind={'transparent'}
size={'medium'}
selected={asideShown}
on:click={() => { on:click={() => {
dispatch('close') asideShown = !asideShown
}} }}
/> />
{#if $$slots['navigate-actions']}
<div class="buttons-group xsmall-gap">
<slot name="navigate-actions" />
</div>
{/if} {/if}
</div> </div>
<div class="ml-4 ac-header__wrap-title flex-grow"> </div>
{#if icon} <div class="popupPanel-body" class:asideShown>
<div class="ac-header__icon"> <div class="popupPanel-body__main" bind:clientWidth={innerWidth}>
<Icon {icon} size={'large'} /> {#if $$slots.header && isHeader}
<div class="popupPanel-body__main-header">
<slot name="header" />
</div> </div>
{/if} {/if}
<div class="ac-header__wrap-description"> <Scroller>
{#if title}<span class="ac-header__title">{title}</span>{/if} <div class="popupPanel-body__main-content">
{#if subtitle}<span class="ac-header__description">{subtitle}</span>{/if}
</div>
</div>
<div class="buttons-group xsmall-gap">
<slot name="commands" />
<slot name="actions" />
</div>
</div>
{:else}
<div class="ac-header short mirror divide highlight">
<div class="buttons-group">
<Button
icon={IconClose}
size={'medium'}
kind={'transparent'}
on:click={() => {
dispatch('close')
}}
/>
{#if $$slots['navigate-actions']}
<div class="buttons-group xsmall-gap">
<slot name="navigate-actions" />
</div>
{/if}
</div>
{#if $$slots['custom-title']}
<div class="ml-4 flex-row-center flex-grow">
<slot name="custom-title" />
</div>
{/if}
</div>
{/if}
<div class="main-content" class:withProperties={$$slots.properties} bind:clientWidth={innerWidth}>
{#if $$slots.subtitle && $$slots.properties && isSubtitle}
<div class="flex-col flex-grow clear-mins">
<div class="ac-subtitle">
<div class="ac-subtitle-content">
<slot name="subtitle" />
</div>
</div>
<div class="flex-col flex-grow clear-mins">
<slot /> <slot />
</div> </div>
</Scroller>
</div> </div>
{:else} {#if $$slots.aside && isAside}
<div class="flex-col flex-grow clear-mins"> <div class="popupPanel-body__aside" class:float={asideFloat} class:shown={asideShown}>
<slot /> <slot name="aside" />
</div>
{/if}
{#if rightSection}
<slot name="rightSection" />
{/if}
{#if $$slots.properties && isProperties}
<div class="properties-container">
<slot name="properties" />
</div> </div>
{/if} {/if}
</div> </div>
</div> </div>
</div>
<style lang="scss">
.panel-content-container {
display: flex;
flex-grow: 1;
flex-direction: column;
}
.main-content {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
min-width: 0;
min-height: 0;
flex-grow: 1;
// height: 100%;
&.withProperties {
flex-direction: row;
}
.properties-container {
position: relative;
display: flex;
flex-direction: column;
flex-shrink: 0;
min-width: 20rem;
width: 20rem;
border-left: 1px solid var(--divider-color);
// background-color: var(--board-card-bg-color);
// &::before {
// position: absolute;
// content: '';
// top: 1.5rem;
// bottom: 1.5rem;
// left: 0;
// width: 1px;
// background-color: var(--divider-color);
// }
}
}
</style>

View File

@ -0,0 +1,25 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 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: 'x-small' | '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 fill-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" />
<rect x="9" y="3" width="1.5" height="10" />
</svg>

View File

@ -119,6 +119,7 @@ export { default as IconNavNext } from './components/icons/NavNext.svelte'
export { default as IconDPCalendar } from './components/calendar/icons/DPCalendar.svelte' export { default as IconDPCalendar } from './components/calendar/icons/DPCalendar.svelte'
export { default as IconDPCalendarOver } from './components/calendar/icons/DPCalendarOver.svelte' export { default as IconDPCalendarOver } from './components/calendar/icons/DPCalendarOver.svelte'
export { default as IconOptions } from './components/icons/Options.svelte' export { default as IconOptions } from './components/icons/Options.svelte'
export { default as IconDetails } from './components/icons/Details.svelte'
export { default as PanelInstance } from './components/PanelInstance.svelte' export { default as PanelInstance } from './components/PanelInstance.svelte'
export { default as Panel } from './components/Panel.svelte' export { default as Panel } from './components/Panel.svelte'

View File

@ -195,6 +195,13 @@ export function fitPopupElement (modalHTML: HTMLElement, element?: PopupAlignmen
newProps.left = '50%' newProps.left = '50%'
newProps.transform = 'translateX(-50%)' newProps.transform = 'translateX(-50%)'
show = true show = true
} else if (element === 'float') {
newProps.top = 'calc(var(--status-bar-height) + .25rem)'
newProps.bottom = '.25rem'
newProps.width = '40rem'
newProps.maxWidth = '40%'
newProps.right = '.25rem'
show = true
} else if (element === 'account') { } else if (element === 'account') {
newProps.bottom = '2.75rem' newProps.bottom = '2.75rem'
newProps.left = '5rem' newProps.left = '5rem'

View File

@ -72,7 +72,7 @@ export interface PopupPositionElement {
h: HorizontalAlignment h: HorizontalAlignment
} }
} }
export type PopupAlignment = PopupPositionElement | null | 'right' | 'top' | 'account' | 'full' | 'content' | 'middle' export type PopupAlignment = PopupPositionElement | null | 'right' | 'top' | 'float' | 'account' | 'full' | 'content' | 'middle'
export type TooltipAlignment = 'top' | 'bottom' | 'left' | 'right' export type TooltipAlignment = 'top' | 'bottom' | 'left' | 'right'
export type VerticalAlignment = 'top' | 'bottom' export type VerticalAlignment = 'top' | 'bottom'
export type HorizontalAlignment = 'left' | 'right' export type HorizontalAlignment = 'left' | 'right'

View File

@ -82,14 +82,13 @@
{/if} {/if}
</div> </div>
{:else} {:else}
<Scroller> <div class="mt-4 pb-4 bottom-highlight-select">
<div class="p-10 bottom-highlight-select">
<slot /> <slot />
</div> </div>
<div class="ac-header short mirror-tool mt-2"> <div class="flex-row-center h-14 px-3 mt-4 antiTitle">
<div class="ac-header__wrap-title"> <div class="icon-wrapper">
<div class="flex-center icon"><IconActivity size={'small'} /></div> <div class="wrapped-icon icon flex-center"><IconActivity size={'small'} /></div>
<span class="ac-header__title"><Label label={activity.string.Activity} /></span> <span class="wrapped-title"><Label label={activity.string.Activity} /></span>
</div> </div>
</div> </div>
{#if showCommenInput} {#if showCommenInput}
@ -106,7 +105,6 @@
</Grid> </Grid>
{/if} {/if}
</div> </div>
</Scroller>
{/if} {/if}
<style lang="scss"> <style lang="scss">
@ -120,10 +118,11 @@
} }
.ref-input { .ref-input {
flex-shrink: 0; flex-shrink: 0;
padding: 1.5rem 2.5rem; padding: 1.5rem 0;
} }
.p-activity { .p-activity {
padding: 1.5rem 2.5rem; padding: 1.5rem 0;
} }
:global(.grid .msgactivity-container:last-child::after) { :global(.grid .msgactivity-container:last-child::after) {

View File

@ -22,7 +22,7 @@
import type { State } from '@anticrm/task' import type { State } from '@anticrm/task'
import task from '@anticrm/task' import task from '@anticrm/task'
import { StyledTextBox } from '@anticrm/text-editor' import { StyledTextBox } from '@anticrm/text-editor'
import { EditBox, Icon, Label } from '@anticrm/ui' import { Button, EditBox, Icon, Label } from '@anticrm/ui'
import { UpDownNavigator } from '@anticrm/view-resources' import { UpDownNavigator } from '@anticrm/view-resources'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import board from '../plugin' import board from '../plugin'
@ -72,11 +72,20 @@
onMount(() => { onMount(() => {
dispatch('open', { ignoreKeys: ['comments', 'number', 'title'] }) dispatch('open', { ignoreKeys: ['comments', 'number', 'title'] })
}) })
let minimize: boolean = false
</script> </script>
{#if object !== undefined} {#if object !== undefined}
<Panel icon={board.icon.Card} title={object?.title} {object} on:close={() => dispatch('close')}> <Panel
<svelte:fragment slot="navigate-actions"> icon={board.icon.Card}
title={object?.title}
{object}
bind:minimize
isHeader={minimize}
isAside={!minimize}
on:close={() => dispatch('close')}
>
<svelte:fragment slot="navigator">
<UpDownNavigator element={object}/> <UpDownNavigator element={object}/>
</svelte:fragment> </svelte:fragment>
@ -128,8 +137,12 @@
</div> </div>
</div> </div>
<svelte:fragment slot="properties"> <svelte:fragment slot="custom-attributes" let:direction>
<div class="p-4"><CardActions bind:value={object} /></div> {#if direction === 'column'}
<CardActions bind:value={object} />
{:else}
<Button icon={board.icon.Card} label={board.string.Actions} kind={'no-border'} size={'small'} />
{/if}
</svelte:fragment> </svelte:fragment>
</Panel> </Panel>
{/if} {/if}

View File

@ -18,7 +18,6 @@
import type { ButtonKind, ButtonSize } from '@anticrm/ui' import type { ButtonKind, ButtonSize } from '@anticrm/ui'
import ChannelsDropdown from './ChannelsDropdown.svelte' import ChannelsDropdown from './ChannelsDropdown.svelte'
import { showPanel } from '@anticrm/ui' import { showPanel } from '@anticrm/ui'
import view from '@anticrm/view'
export let value: Channel[] | Channel | null export let value: Channel[] | Channel | null
@ -32,7 +31,13 @@
if (ev.detail.presenter !== undefined && Array.isArray(value)) { if (ev.detail.presenter !== undefined && Array.isArray(value)) {
const channel = value[0] const channel = value[0]
if (channel !== undefined) { if (channel !== undefined) {
showPanel(view.component.EditDoc, channel.attachedTo, channel.attachedToClass, 'content', ev.detail.presenter) showPanel(
ev.detail.presenter,
channel.attachedTo,
channel.attachedToClass,
'float',
ev.detail.presenter
)
} }
} }
} }

View File

@ -45,6 +45,7 @@
"@anticrm/attachment": "~0.6.1", "@anticrm/attachment": "~0.6.1",
"@anticrm/attachment-resources": "~0.6.0", "@anticrm/attachment-resources": "~0.6.0",
"@anticrm/login": "~0.6.1", "@anticrm/login": "~0.6.1",
"@anticrm/core": "~0.6.16" "@anticrm/core": "~0.6.16",
"@anticrm/panel": "~0.6.0"
} }
} }

View File

@ -195,6 +195,7 @@
border-bottom: 1px solid var(--theme-zone-bg); border-bottom: 1px solid var(--theme-zone-bg);
.icon { .icon {
flex-shrink: 0;
margin-right: 1rem; margin-right: 1rem;
width: 2.25rem; width: 2.25rem;
height: 2.25rem; height: 2.25rem;
@ -206,6 +207,6 @@
.right-content { .right-content {
flex-grow: 1; flex-grow: 1;
padding: 1.5rem 1rem; padding: 1.5rem 0;
} }
</style> </style>

View File

@ -14,31 +14,49 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import contact, { Channel, Contact } from '@anticrm/contact' import type { AnyComponent } from '@anticrm/ui'
import { Ref, Doc, Class } from '@anticrm/core'
import contact, { Channel } from '@anticrm/contact'
import { SharedMessage } from '@anticrm/gmail' import { SharedMessage } from '@anticrm/gmail'
import NewMessage from './NewMessage.svelte' import NewMessage from './NewMessage.svelte'
import FullMessage from './FullMessage.svelte' import FullMessage from './FullMessage.svelte'
import Chats from './Chats.svelte' import Chats from './Chats.svelte'
import { getClient } from '@anticrm/presentation' import { createQuery, getClient } from '@anticrm/presentation'
import { NotificationClientImpl } from '@anticrm/notification-resources' import { NotificationClientImpl } from '@anticrm/notification-resources'
import { Panel } from '@anticrm/panel'
import { createEventDispatcher } from 'svelte'
export let object: Contact export let _id: Ref<Doc>
export let _class: Ref<Class<Doc>>
export let rightSection: AnyComponent | undefined = undefined
// export let object: Contact
// $: console.log('!!!!!!!!!!!! id: ', _id, ' - class: ', _class)
let object: any
let newMessage: boolean = false let newMessage: boolean = false
let currentMessage: SharedMessage | undefined = undefined let currentMessage: SharedMessage | undefined = undefined
let channel: Channel | undefined = undefined let channel: Channel | undefined = undefined
const notificationClient = NotificationClientImpl.getClient() const notificationClient = NotificationClientImpl.getClient()
const client = getClient() const client = getClient()
const dispatch = createEventDispatcher()
client client
.findOne(contact.class.Channel, { .findOne(contact.class.Channel, {
attachedTo: object._id, attachedTo: _id,
provider: contact.channelProvider.Email provider: contact.channelProvider.Email
}) })
.then((res) => { .then((res) => {
channel = res channel = res
}) })
const query = createQuery()
$: _id &&
_class &&
query.query(_class, { _id }, (result) => {
object = result[0]
})
function back () { function back () {
if (newMessage) { if (newMessage) {
return (newMessage = false) return (newMessage = false)
@ -54,7 +72,18 @@
} }
</script> </script>
{#if channel} {#if channel && object}
<Panel
icon={contact.icon.Email}
title={'Email'}
{rightSection}
{object}
isHeader={false}
isAside={false}
on:close={() => {
dispatch('close')
}}
>
{#if newMessage} {#if newMessage}
<NewMessage {object} {channel} {currentMessage} on:close={back} /> <NewMessage {object} {channel} {currentMessage} on:close={back} />
{:else if currentMessage} {:else if currentMessage}
@ -62,4 +91,5 @@
{:else} {:else}
<Chats {object} {channel} bind:newMessage on:select={selectHandler} /> <Chats {object} {channel} bind:newMessage on:select={selectHandler} />
{/if} {/if}
</Panel>
{/if} {/if}

View File

@ -43,6 +43,7 @@
"@anticrm/core": "~0.6.16", "@anticrm/core": "~0.6.16",
"@anticrm/notification-resources": "~0.6.0", "@anticrm/notification-resources": "~0.6.0",
"@anticrm/attachment": "~0.6.1", "@anticrm/attachment": "~0.6.1",
"@anticrm/attachment-resources": "~0.6.0" "@anticrm/attachment-resources": "~0.6.0",
"@anticrm/panel": "~0.6.0"
} }
} }

View File

@ -16,12 +16,15 @@
<script lang="ts"> <script lang="ts">
import attachment from '@anticrm/attachment' import attachment from '@anticrm/attachment'
import { AttachmentRefInput } from '@anticrm/attachment-resources' import { AttachmentRefInput } from '@anticrm/attachment-resources'
import contact, { Channel, Contact, EmployeeAccount, formatName } from '@anticrm/contact' import { Panel } from '@anticrm/panel'
import { generateId, getCurrentAccount, Ref, SortingOrder, Space } from '@anticrm/core' import { createEventDispatcher } from 'svelte'
import contact, { Channel, EmployeeAccount, formatName } from '@anticrm/contact'
import { generateId, getCurrentAccount, Ref, SortingOrder, Space, Doc, Class } from '@anticrm/core'
import { NotificationClientImpl } from '@anticrm/notification-resources' import { NotificationClientImpl } from '@anticrm/notification-resources'
import { createQuery, getClient } from '@anticrm/presentation' import { createQuery, getClient } from '@anticrm/presentation'
import setting, { Integration } from '@anticrm/setting' import setting, { Integration } from '@anticrm/setting'
import type { NewTelegramMessage, SharedTelegramMessage, TelegramMessage } from '@anticrm/telegram' import type { NewTelegramMessage, SharedTelegramMessage, TelegramMessage } from '@anticrm/telegram'
import type { AnyComponent } from '@anticrm/ui'
import { Button, eventToHTMLElement, IconShare, Tooltip, Scroller, showPopup } from '@anticrm/ui' import { Button, eventToHTMLElement, IconShare, Tooltip, Scroller, showPopup } from '@anticrm/ui'
import telegram from '../plugin' import telegram from '../plugin'
import Connect from './Connect.svelte' import Connect from './Connect.svelte'
@ -29,22 +32,35 @@
import Messages from './Messages.svelte' import Messages from './Messages.svelte'
import Reconnect from './Reconnect.svelte' import Reconnect from './Reconnect.svelte'
export let object: Contact export let _id: Ref<Doc>
export let _class: Ref<Class<Doc>>
export let rightSection: AnyComponent | undefined = undefined
let object: any
let channel: Channel | undefined = undefined let channel: Channel | undefined = undefined
let objectId: Ref<NewTelegramMessage> = generateId() let objectId: Ref<NewTelegramMessage> = generateId()
const dispatch = createEventDispatcher()
const client = getClient() const client = getClient()
const notificationClient = NotificationClientImpl.getClient() const notificationClient = NotificationClientImpl.getClient()
client client
.findOne(contact.class.Channel, { .findOne(contact.class.Channel, {
attachedTo: object._id, attachedTo: _id,
provider: contact.channelProvider.Telegram provider: contact.channelProvider.Telegram
}) })
.then((res) => { .then((res) => {
channel = res channel = res
}) })
const query = createQuery()
$: _id &&
_class &&
query.query(_class, { _id }, (result) => {
object = result[0]
})
let messages: TelegramMessage[] = [] let messages: TelegramMessage[] = []
let accounts: EmployeeAccount[] = [] let accounts: EmployeeAccount[] = []
let integration: Integration | undefined let integration: Integration | undefined
@ -171,14 +187,22 @@
} }
</script> </script>
<div class="telegram-header"> {#if object !== undefined}
<div class="ac-header__wrap-title"> <Panel
<div class="flex-center icon"><TelegramIcon size={'small'} /></div> icon={TelegramIcon}
<div class="ac-header__wrap-description"> title={'Telegram'}
<span class="ac-header__title">Telegram</span> {rightSection}
<span class="ac-header__description">You and {formatName(object.name)}</span> {object}
</div> isHeader={false}
</div> isAside={false}
on:close={() => {
dispatch('close')
}}
>
<svelte:fragment slot="header">
You and {formatName(object.name)}
</svelte:fragment>
<svelte:fragment slot="tools">
<Tooltip label={telegram.string.Share}> <Tooltip label={telegram.string.Share}>
<Button <Button
icon={IconShare} icon={IconShare}
@ -189,7 +213,8 @@
}} }}
/> />
</Tooltip> </Tooltip>
</div> </svelte:fragment>
<div class="telegram-content" class:selectable> <div class="telegram-content" class:selectable>
<Scroller bottomStart autoscroll> <Scroller bottomStart autoscroll>
{#if messages && accounts} {#if messages && accounts}
@ -246,36 +271,17 @@
/> />
{/if} {/if}
</div> </div>
</Panel>
{/if}
<style lang="scss"> <style lang="scss">
.telegram-header {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: nowrap;
margin: 0 2.5rem;
padding: 0;
height: 4rem;
min-height: 4rem;
border-bottom: 1px solid var(--divider-color);
}
.icon {
margin-right: 1rem;
width: 2.25rem;
height: 2.25rem;
color: var(--white-color);
background-color: var(--primary-bg-color);
border-radius: 50%;
}
.ref-input { .ref-input {
padding: 0 2.5rem 1.5rem; padding: 0 0 1.5rem;
&.selectable { &.selectable {
padding: 1rem 2.5rem; padding: 1rem 0;
color: var(--theme-caption-color); color: var(--theme-caption-color);
background-color: var(--accent-bg-color); border-top: 1px solid var(--divider-color);
border-top: 1px solid var(--theme-card-divider);
} }
} }

View File

@ -80,13 +80,16 @@
onMount(() => { onMount(() => {
dispatch('open', { ignoreKeys: ['comments', 'name', 'description', 'number'] }) dispatch('open', { ignoreKeys: ['comments', 'name', 'description', 'number'] })
}) })
let minimize: boolean = false
</script> </script>
{#if issue !== undefined} {#if issue !== undefined}
<Panel <Panel
object={issue} object={issue}
showHeader={false} bind:minimize
isSubtitle={true} isHeader
isAside={!minimize}
isSub={minimize}
bind:innerWidth bind:innerWidth
on:close={() => { on:close={() => {
dispatch('close') dispatch('close')
@ -106,12 +109,18 @@
</div> </div>
</div> </div>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="navigate-actions"> <svelte:fragment slot="navigator">
<Button icon={IconDownOutline} kind={'secondary'} size={'medium'} /> <Button icon={IconDownOutline} kind={'secondary'} size={'medium'} />
<Button icon={IconUpOutline} kind={'secondary'} size={'medium'} /> <Button icon={IconUpOutline} kind={'secondary'} size={'medium'} />
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="header">
<span class="fs-title">{issueLabel}</span>
</svelte:fragment>
<svelte:fragment slot="tools">
<Button icon={IconEdit} kind={'transparent'} size={'medium'} />
<Button icon={IconMoreH} kind={'transparent'} size={'medium'} />
</svelte:fragment>
<div class="flex-col flex-grow flex-no-shrink h-full mx-auto content">
<div class="mt-6"> <div class="mt-6">
<EditBox <EditBox
label={tracker.string.Title} label={tracker.string.Title}
@ -122,7 +131,7 @@
on:change={() => change('title', issue?.title)} on:change={() => change('title', issue?.title)}
/> />
</div> </div>
<div class="mt-6"> <div class="mt-6 mb-6">
<StyledTextBox <StyledTextBox
alwaysEdit alwaysEdit
bind:content={issue.description} bind:content={issue.description}
@ -130,14 +139,9 @@
on:value={(evt) => change('description', evt.detail)} on:value={(evt) => change('description', evt.detail)}
/> />
</div> </div>
</div>
<svelte:fragment slot="properties"> <span slot="actions-label">{issueLabel}</span>
{#if issue && currentTeam && issueStatuses} <svelte:fragment slot="actions">
<div class="flex-grow relative min-w-80 right-panel">
<div class="ac-header short divide header">
<span class="w-24 overflow-label">{issueLabel}</span>
<div class="buttons-group">
<Button <Button
icon={tracker.icon.Issue} icon={tracker.icon.Issue}
title={tracker.string.CopyIssueUrl} title={tracker.string.CopyIssueUrl}
@ -154,10 +158,11 @@
kind="transparent" kind="transparent"
on:click={() => issueLabel && copy(issueLabel)} on:click={() => issueLabel && copy(issueLabel)}
/> />
</div> </svelte:fragment>
</div>
<div class="content"> <svelte:fragment slot="custom-attributes" let:direction>
{#if issue && currentTeam && issueStatuses && direction === 'column'}
<div class="content mt-4">
<div class="flex-row-center mb-4"> <div class="flex-row-center mb-4">
<span class="label w-24"> <span class="label w-24">
<Label label={tracker.string.Status} /> <Label label={tracker.string.Status} />
@ -183,6 +188,8 @@
bind:value={issue.assignee} bind:value={issue.assignee}
allowDeselect allowDeselect
titleDeselect={tracker.string.Unassigned} titleDeselect={tracker.string.Unassigned}
size="large"
kind="link"
on:change={() => change('assignee', issue?.assignee)} on:change={() => change('assignee', issue?.assignee)}
/> />
</div> </div>
@ -194,9 +201,9 @@
<Button <Button
label={tracker.string.Labels} label={tracker.string.Labels}
icon={tracker.icon.Labels} icon={tracker.icon.Labels}
width="min-content" width="max-content"
size="small" size="large"
kind="no-border" kind="link"
/> />
</div> </div>
@ -206,6 +213,24 @@
<span class="label w-24"> <span class="label w-24">
<Label label={tracker.string.Project} /> <Label label={tracker.string.Project} />
</span> </span>
<Button
label={tracker.string.Project}
icon={tracker.icon.Projects}
width="fit-content"
size="large"
kind="link"
/>
</div>
</div>
{:else}
<div class="buttons-group small-gap">
<Button
label={tracker.string.Labels}
icon={tracker.icon.Labels}
width="min-content"
size="small"
kind="no-border"
/>
<Button <Button
label={tracker.string.Project} label={tracker.string.Project}
icon={tracker.icon.Projects} icon={tracker.icon.Projects}
@ -213,8 +238,6 @@
size="small" size="small"
kind="no-border" kind="no-border"
/> />
</div>
</div>
</div> </div>
{/if} {/if}
</svelte:fragment> </svelte:fragment>
@ -222,23 +245,10 @@
{/if} {/if}
<style lang="scss"> <style lang="scss">
.header {
max-width: 56.25rem;
width: calc(100% - 5rem);
justify-content: space-between;
padding: 0 1.25rem;
}
.content { .content {
width: 100%; width: 100%;
} }
.right-panel {
.header {
padding: 1rem 0;
margin: 0 1.5rem;
}
.content { .content {
position: absolute; position: absolute;
inset: 2.5rem 0 0; inset: 2.5rem 0 0;
@ -254,5 +264,4 @@
border-bottom: 1px solid var(--divider-color); border-bottom: 1px solid var(--divider-color);
margin: 0.75rem 1.5rem 1.25rem 0; margin: 0.75rem 1.5rem 1.25rem 0;
} }
}
</style> </style>

View File

@ -15,7 +15,7 @@
--> -->
<script lang="ts"> <script lang="ts">
import contact, { ChannelProvider, formatName } from '@anticrm/contact' import contact, { ChannelProvider, formatName } from '@anticrm/contact'
import core, { Class, ClassifierKind, Doc, getCurrentAccount, Hierarchy, Mixin, Obj, Ref, Space } from '@anticrm/core' import core, { Class, ClassifierKind, Doc, getCurrentAccount, Mixin, Obj, Ref, Space } from '@anticrm/core'
import notification from '@anticrm/notification' import notification from '@anticrm/notification'
import { Panel } from '@anticrm/panel' import { Panel } from '@anticrm/panel'
import { Asset, getResource, IntlString, translate } from '@anticrm/platform' import { Asset, getResource, IntlString, translate } from '@anticrm/platform'
@ -27,8 +27,7 @@
KeyedAttribute KeyedAttribute
} from '@anticrm/presentation' } from '@anticrm/presentation'
import setting, { IntegrationType } from '@anticrm/setting' import setting, { IntegrationType } from '@anticrm/setting'
import { AnyComponent, Button, Component, IconActivity } from '@anticrm/ui' import { AnyComponent, Component } from '@anticrm/ui'
import Tooltip from '@anticrm/ui/src/components/Tooltip.svelte'
import view from '@anticrm/view' import view from '@anticrm/view'
import { createEventDispatcher, onDestroy } from 'svelte' import { createEventDispatcher, onDestroy } from 'svelte'
import { getCollectionCounter } from '../utils' import { getCollectionCounter } from '../utils'
@ -38,7 +37,6 @@
export let _id: Ref<Doc> export let _id: Ref<Doc>
export let _class: Ref<Class<Doc>> export let _class: Ref<Class<Doc>>
export let rightSection: AnyComponent | undefined = undefined export let rightSection: AnyComponent | undefined = undefined
// export let position: PopupAlignment | undefined = undefined
let lastId: Ref<Doc> = _id let lastId: Ref<Doc> = _id
let lastClass: Ref<Class<Doc>> = _class let lastClass: Ref<Class<Doc>> = _class
@ -49,7 +47,6 @@
const client = getClient() const client = getClient()
const hierarchy = client.getHierarchy() const hierarchy = client.getHierarchy()
const notificationClient = getResource(notification.function.GetNotificationClient).then((res) => res()) const notificationClient = getResource(notification.function.GetNotificationClient).then((res) => res())
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys()) const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
$: read(_id) $: read(_id)
@ -76,18 +73,11 @@
$: if (object !== undefined) objectClass = hierarchy.getClass(object._class) $: if (object !== undefined) objectClass = hierarchy.getClass(object._class)
let selectedClass: Ref<Class<Doc>> | undefined
let prevSelected = selectedClass
let keys: KeyedAttribute[] = [] let keys: KeyedAttribute[] = []
let collectionEditors: { key: KeyedAttribute; editor: AnyComponent }[] = [] let collectionEditors: { key: KeyedAttribute; editor: AnyComponent }[] = []
let mixins: Mixin<Doc>[] = [] let mixins: Mixin<Doc>[] = []
$: if (object) {
$: if (object && prevSelected !== object._class) {
prevSelected = object._class
selectedClass = Hierarchy.mixinOrClass(object)
parentClass = getParentClass(object._class) parentClass = getParentClass(object._class)
getMixins() getMixins()
} }
@ -119,7 +109,11 @@
let ignoreMixins: Set<Ref<Mixin<Doc>>> = new Set<Ref<Mixin<Doc>>>() let ignoreMixins: Set<Ref<Mixin<Doc>>> = new Set<Ref<Mixin<Doc>>>()
async function updateKeys (): Promise<void> { async function updateKeys (): Promise<void> {
const filtredKeys = getFiltredKeys(selectedClass ?? object._class, ignoreKeys) let filtredKeys = getFiltredKeys(object._class, ignoreKeys)
for (const m of mixins) {
const mkeys = getFiltredKeys(m._id, ignoreKeys)
filtredKeys = filtredKeys.concat(mkeys).filter((it, idx, arr) => arr.indexOf(it) === idx)
}
keys = collectionsFilter(filtredKeys, false) keys = collectionsFilter(filtredKeys, false)
const collectionKeys = collectionsFilter(filtredKeys, true) const collectionKeys = collectionsFilter(filtredKeys, true)
@ -151,9 +145,7 @@
} }
let mainEditor: AnyComponent let mainEditor: AnyComponent
$: if (object) getEditorOrDefault(object._class, object._class)
$: if (object) getEditorOrDefault(selectedClass, object._class)
async function getEditorOrDefault (_class: Ref<Class<Doc>> | undefined, defaultClass: Ref<Class<Doc>>): Promise<void> { async function getEditorOrDefault (_class: Ref<Class<Doc>> | undefined, defaultClass: Ref<Class<Doc>>): Promise<void> {
let editor = _class !== undefined ? await getEditor(_class) : undefined let editor = _class !== undefined ? await getEditor(_class) : undefined
if (editor === undefined) { if (editor === undefined) {
@ -286,6 +278,8 @@
}) })
}) })
}) })
let minimize: boolean = false
</script> </script>
<ActionContext <ActionContext
@ -300,6 +294,9 @@
{title} {title}
{rightSection} {rightSection}
{object} {object}
bind:minimize
isHeader={false}
isAside={!minimize}
bind:panelWidth bind:panelWidth
bind:innerWidth bind:innerWidth
on:update={(ev) => _update(ev.detail)} on:update={(ev) => _update(ev.detail)}
@ -307,56 +304,20 @@
dispatch('close') dispatch('close')
}} }}
> >
<svelte:fragment slot="navigate-actions"> <svelte:fragment slot="navigator">
<UpDownNavigator element={object} /> <UpDownNavigator element={object} />
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="subtitle">
<svelte:fragment slot="attributes" let:direction={dir}>
{#if !headerLoading} {#if !headerLoading}
{#if headerEditor !== undefined} {#if headerEditor !== undefined}
<Component is={headerEditor} props={{ object, keys }} /> <Component is={headerEditor} props={{ object, keys, vertical: dir === 'column' }} />
{:else} {:else}
<AttributesBar {object} {keys} /> <AttributesBar {object} {keys} vertical={dir === 'column'} />
{/if} {/if}
{/if} {/if}
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="properties">
{#if !headerLoading}
<div class="p-4">
{#if headerEditor !== undefined}
<Component is={headerEditor} props={{ object, keys, vertical: true }} />
{:else}
<AttributesBar {object} {keys} vertical />
{/if}
</div>
{/if}
</svelte:fragment>
<svelte:fragment slot="actions">
{#if displayedIntegrations && displayedIntegrations.length > 0}
<div class="actions-divider" />
{#each displayedIntegrations as pr}
<Tooltip label={pr.label}>
<Button
icon={pr.icon}
size={'medium'}
kind={'transparent'}
highlight={rightSection === pr.presenter}
on:click={() => {
if (rightSection !== pr.presenter) rightSection = pr.presenter
}}
/>
</Tooltip>
{/each}
<Button
icon={IconActivity}
size={'medium'}
kind={'transparent'}
highlight={!rightSection}
on:click={() => {
if (rightSection) rightSection = undefined
}}
/>
{/if}
</svelte:fragment>
<div class="main-editor"> <div class="main-editor">
{#if mainEditor} {#if mainEditor}
<Component <Component
@ -376,7 +337,7 @@
</div> </div>
{#each collectionEditors as collection} {#each collectionEditors as collection}
{#if collection.editor} {#if collection.editor}
<div class="mt-14"> <div class="mt-6">
<Component <Component
is={collection.editor} is={collection.editor}
props={{ props={{
@ -400,12 +361,4 @@
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
} }
.actions-divider {
margin: 0 0.25rem;
min-width: 1px;
width: 1px;
height: 1.5rem;
background-color: var(--divider-color);
}
</style> </style>

View File

@ -298,12 +298,12 @@
} }
} }
let navFloat: boolean = !(window.innerWidth < 1100) let navFloat: boolean = window.innerWidth < 1024 ? false : true
const windowResize = (): void => { const windowResize = (): void => {
if (window.innerWidth < 1100 && !navFloat) { if (window.innerWidth < 1024 && !navFloat) {
visibileNav = false visibileNav = false
navFloat = true navFloat = true
} else if (window.innerWidth >= 1100 && navFloat) { } else if (window.innerWidth >= 1024 && navFloat) {
navFloat = false navFloat = false
visibileNav = true visibileNav = true
} }