Add additional panel display modes (#1279)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2022-04-05 11:15:14 +07:00 committed by GitHub
parent 859640ca5e
commit 0b93f86dfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 374 additions and 146 deletions

View File

@ -15,12 +15,12 @@
--> -->
<script lang="ts"> <script lang="ts">
import activity from '@anticrm/activity' import activity from '@anticrm/activity'
import calendar from '@anticrm/calendar'
import type { Doc } from '@anticrm/core' import type { Doc } from '@anticrm/core'
import notification from '@anticrm/notification' import notification from '@anticrm/notification'
import calendar from '@anticrm/calendar'
import type { Asset } from '@anticrm/platform' import type { Asset } from '@anticrm/platform'
import { ActionIcon,AnyComponent,AnySvelteComponent,Component,Icon,IconClose,IconExpand,IconMoreH,Scroller } from '@anticrm/ui' import { ActionIcon, AnyComponent, AnySvelteComponent, Component, IconExpand, Panel, Scroller } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte' import { PopupAlignment } from '@anticrm/ui'
export let title: string export let title: string
export let subtitle: string | undefined = undefined export let subtitle: string | undefined = undefined
@ -28,72 +28,53 @@
export let fullSize: boolean = true export let fullSize: boolean = true
export let rightSection: AnyComponent | undefined = undefined export let rightSection: AnyComponent | undefined = undefined
export let object: Doc export let object: Doc
export let position: PopupAlignment | undefined = undefined
const dispatch = createEventDispatcher() let innerWidth = 0
$: allowFullSize = innerWidth > 900 && position === 'full'
$: isFullSize = allowFullSize && fullSize
</script> </script>
<svelte:window bind:innerWidth />
<div class="antiOverlay" on:click={() => { dispatch('close') }} /> <Panel {title} {subtitle} {icon} on:close rightSection={isFullSize}>
<div class="antiDialogs antiComponent" class:fullSize> <svelte:fragment slot="subtitle">
{#if fullSize} <slot name="subtitle" />
<div class="ad-section-50 divide"> </svelte:fragment>
<div class="ac-header short mirror divide"> <svelte:fragment slot="commands">
<div class="ac-header__wrap-title">
{#if icon }<div class="ac-header__icon"><Icon {icon} size={'large'}/></div>{/if}
<div class="ac-header__wrap-description">
<span class="ac-header__title">{title}</span>
{#if subtitle }<span class="ac-header__description">{subtitle}</span>{/if}
</div>
</div>
<div class="flex">
<Component is={calendar.component.DocReminder} props={{ value: object, title }} /> <Component is={calendar.component.DocReminder} props={{ value: object, title }} />
<div class="ml-2"> <div class="ml-2">
<Component is={notification.component.LastViewEditor} props={{ value: object }} /> <Component is={notification.component.LastViewEditor} props={{ value: object }} />
</div> </div>
</div> </svelte:fragment>
</div>
{#if $$slots.subtitle} <svelte:fragment slot="rightSection">
<div class="ac-subtitle"> {#if isFullSize}
<div class="ac-subtitle-content"> <div class="ad-section-50">
<slot name="subtitle" /> <Component is={rightSection ?? activity.component.Activity} props={{ object, fullSize: isFullSize }} />
</div>
</div> </div>
{/if} {/if}
</svelte:fragment>
<svelte:fragment slot="actions">
{#if allowFullSize}
<div class="tool">
<ActionIcon
icon={IconExpand}
size={'medium'}
action={() => {
fullSize = !fullSize
}}
/>
</div>
{/if}
</svelte:fragment>
{#if isFullSize}
<Scroller> <Scroller>
<div class="p-10"><slot /></div> <div class="p-10"><slot /></div>
</Scroller> </Scroller>
</div>
<div class="ad-section-50">
<Component is={rightSection ?? activity.component.Activity} props={{ object, fullSize }} />
</div>
{:else} {:else}
<div class="ac-header short mirror-tool divide"> <Component is={activity.component.Activity} props={{ object, fullSize: isFullSize }}>
<div class="ac-header__wrap-title">
{#if icon }<div class="ac-header__icon"><Icon {icon} size={'large'}/></div>{/if}
<div class="ac-header__wrap-description">
<span class="ac-header__title">{title}</span>
{#if subtitle }<span class="ac-header__description">{subtitle}</span>{/if}
</div>
</div>
<Component is={notification.component.LastViewEditor} props={{ value: object }} />
</div>
{#if $$slots.subtitle}
<div class="ac-subtitle">
<div class="ac-subtitle-content">
<slot name="subtitle" />
</div>
</div>
{/if}
<Component is={activity.component.Activity} props={{ object, fullSize }}>
<slot /> <slot />
</Component> </Component>
{/if} {/if}
</Panel>
<div class="ad-tools">
<div class="tool">
<ActionIcon icon={IconExpand} size={'medium'} action={() => { fullSize = !fullSize }} />
</div>
<div class="tool">
<ActionIcon icon={IconClose} size={'medium'} action={() => { dispatch('close') }} />
</div>
</div>
</div>

View File

@ -20,6 +20,7 @@
@import "./dialogs.scss"; @import "./dialogs.scss";
@import "./popups.scss"; @import "./popups.scss";
@import "./mixins.scss"; @import "./mixins.scss";
@import "./panel.scss";
@font-face { @font-face {
font-family: 'IBM Plex Sans'; font-family: 'IBM Plex Sans';

View File

@ -0,0 +1,59 @@
//
// Copyright © 2022 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
/* Dialogs */
.antiPanel {
overflow: hidden;
min-width: 400px;
background: var(--popup-bg-color);
border-radius: .5rem;
flex-direction: row;
// left: 1rem;
.ac-header.divide { border-bottom: 1px solid var(--theme-card-divider); }
.ad-section-50 {
display: flex;
flex-direction: column;
flex-basis: 50%;
min-height: 0;
width: 50%;
&.divide { border-right: 1px solid var(--theme-card-divider); }
}
.ad-tools {
position: absolute;
display: flex;
top: 1rem;
right: 2rem;
&.grow-reverse {
left: 0px;
display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
align-items: center;
}
}
.tool {
margin-left: 1rem;
color: var(--theme-content-accent-color);
cursor: pointer;
&:hover { color: var(--theme-caption-color); }
}
}

View File

@ -0,0 +1,91 @@
<!--
// Copyright © 2022 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import type { Asset } from '@anticrm/platform'
import { createEventDispatcher } from 'svelte'
import { AnySvelteComponent } from '../types'
import ActionIcon from './ActionIcon.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 reverseCommands = false
const dispatch = createEventDispatcher()
</script>
<div class="antiPanel antiComponent">
<div class:panel-content={!rightSection} class:ad-section-50={rightSection} class:divide={rightSection}>
<div class="ac-header short mirror divide">
<div class="ac-header__wrap-title">
{#if icon}<div class="ac-header__icon"><Icon {icon} size={'large'} /></div>{/if}
<div class="ac-header__wrap-description">
{#if title}
<span class="ac-header__title">{title}</span>
{/if}
{#if subtitle}<span class="ac-header__description">{subtitle}</span>{/if}
</div>
</div>
{#if rightSection}
<div class="flex">
<slot name="commands" />
</div>
{/if}
</div>
{#if $$slots.subtitle}
<div class="ac-subtitle">
<div class="ac-subtitle-content">
<slot name="subtitle" />
</div>
</div>
{/if}
<div class='flex-col'>
<slot />
</div>
</div>
{#if rightSection}
<slot name='rightSection'/>
{/if}
<div class="ad-tools" class:grow-reverse={reverseCommands}>
{#if !rightSection && $$slots.commands}
<div class="flex">
<slot name="commands" />
</div>
{/if}
<slot name='actions'/>
<div class="tool">
<ActionIcon
icon={IconClose}
size={'medium'}
action={() => {
dispatch('close')
}}
/>
</div>
</div>
</div>
<style lang="scss">
.panel-content {
display: flex;
flex-grow: 1;
flex-direction: column;
align-content: stretch;
}
</style>

View File

@ -17,9 +17,11 @@
import { getResource } from '@anticrm/platform' import { getResource } from '@anticrm/platform'
import { afterUpdate } from 'svelte' import { afterUpdate } from 'svelte'
import { AnySvelteComponent, Spinner } from '..' import { AnySvelteComponent, Spinner } from '..'
import { closePanel, PanelProps, panelstore as modal } from '../panelup' import { closePanel, PanelProps, panelstore } from '../panelup'
import { popupstore } from '../popups' import { popupstore } from '../popups'
export let contentPanel: HTMLElement
let modalHTML: HTMLElement let modalHTML: HTMLElement
let componentInstance: any let componentInstance: any
let show: boolean = false let show: boolean = false
@ -31,7 +33,7 @@
closePanel() closePanel()
} }
$: props = $modal.panel $: props = $panelstore.panel
$: if (props !== undefined) { $: if (props !== undefined) {
getResource(props.component).then((r) => { getResource(props.component).then((r) => {
@ -51,7 +53,7 @@
_close() _close()
} }
const fitPopup = (props: PanelProps): void => { const fitPopup = (props: PanelProps, contentPanel: HTMLElement): void => {
if (modalHTML) { if (modalHTML) {
if (props.element) { if (props.element) {
show = false show = false
@ -76,10 +78,11 @@
} else { } else {
modalHTML.style.left = rect.left + 'px' modalHTML.style.left = rect.left + 'px'
} }
} else if (props.element === 'right') { } else if (props.element === 'right' && contentPanel !== undefined) {
modalHTML.style.top = '0' const rect = contentPanel.getBoundingClientRect()
modalHTML.style.bottom = '0' modalHTML.style.top = `calc(${rect.top}px + 0.5rem)`
modalHTML.style.right = '0' modalHTML.style.bottom = '0.75rem'
modalHTML.style.right = '0.75rem'
} else if (props.element === 'float') { } else if (props.element === 'float') {
modalHTML.style.top = '4rem' modalHTML.style.top = '4rem'
modalHTML.style.bottom = '4rem' modalHTML.style.bottom = '4rem'
@ -87,11 +90,24 @@
} else if (props.element === 'account') { } else if (props.element === 'account') {
modalHTML.style.bottom = '2.75rem' modalHTML.style.bottom = '2.75rem'
modalHTML.style.left = '5rem' modalHTML.style.left = '5rem'
} else if (props.element === 'full') { } else if (props.element === 'full' && contentPanel !== undefined) {
modalHTML.style.top = '0' const rect = contentPanel.getBoundingClientRect()
modalHTML.style.bottom = '0' modalHTML.style.top = `calc(${rect.top}px + 0.5rem)`
modalHTML.style.left = '0' modalHTML.style.bottom = '0.75rem'
modalHTML.style.right = '0' modalHTML.style.left = '0.75rem'
modalHTML.style.right = '0.75rem'
} else if (props.element === 'content' && contentPanel !== undefined) {
const rect = contentPanel.getBoundingClientRect()
modalHTML.style.top = `calc(${rect.top}px + 0.5rem)`
modalHTML.style.bottom = '0.75rem'
modalHTML.style.left = `calc(${rect.left}px + 0.5rem)`
modalHTML.style.right = '0.75rem'
} else if (props.element === 'middle' && contentPanel !== undefined) {
const rect = contentPanel.getBoundingClientRect()
modalHTML.style.top = `calc(${rect.top}px + 0.5rem)`
modalHTML.style.bottom = '0.75rem'
modalHTML.style.left = '50%'
modalHTML.style.transform = 'translateX(-50%)'
} }
} else { } else {
modalHTML.style.top = '50%' modalHTML.style.top = '50%'
@ -108,13 +124,13 @@
} }
} }
afterUpdate(() => { afterUpdate(() => {
if (props) fitPopup(props) if (props) fitPopup(props, contentPanel)
}) })
</script> </script>
<svelte:window <svelte:window
on:resize={() => { on:resize={() => {
if (props) fitPopup(props) if (props) fitPopup(props, contentPanel)
}} }}
on:keydown={(evt) => { on:keydown={(evt) => {
if (props) handleKeydown(evt) if (props) handleKeydown(evt)
@ -124,28 +140,32 @@
{#if !component} {#if !component}
<Spinner /> <Spinner />
{:else} {:else}
<div class="popup" bind:this={modalHTML} style={'z-index: 401'}> <div class="antiPanel panel-instance" bind:this={modalHTML}>
<svelte:component <svelte:component
this={component} this={component}
bind:this={componentInstance} bind:this={componentInstance}
_id={props._id} _id={props._id}
_class={props._class} _class={props._class}
rightSection={props.rightSection} rightSection={props.rightSection}
on:update={fitPopup} position={props.element }
on:close={_close} on:close={_close}
on:update={() => {
if (props) {
fitPopup(props, contentPanel)
}
}}
/> />
</div> </div>
{#if props.element !== 'content'}
<div class="modal-overlay" class:show style={'z-index: 400'} on:click={() => escapeClose()} /> <div class="modal-overlay" class:show style={'z-index: 400'} on:click={() => escapeClose()} />
{/if} {/if}
{/if}
{/if} {/if}
<style lang="scss"> <style lang="scss">
.popup { .panel-instance {
z-index: 401;
position: fixed; position: fixed;
display: flex;
flex-direction: column;
justify-content: center;
max-height: calc(100vh - 2rem);
background-color: transparent; background-color: transparent;
} }
.modal-overlay { .modal-overlay {

View File

@ -20,7 +20,7 @@ import { readable } from 'svelte/store'
import Root from './components/internal/Root.svelte' import Root from './components/internal/Root.svelte'
export type { AnyComponent, AnySvelteComponent, Action, LabelAndProps, TooltipAligment, AnySvelteComponentWithProps, Location } from './types' export type { AnyComponent, AnySvelteComponent, Action, LabelAndProps, TooltipAligment, AnySvelteComponentWithProps, Location, PopupAlignment } from './types'
// export { applicationShortcutKey } from './utils' // export { applicationShortcutKey } from './utils'
export { getCurrentLocation, locationToUrl, navigate, location } from './location' export { getCurrentLocation, locationToUrl, navigate, location } from './location'
@ -97,6 +97,7 @@ export { default as IconCheck } from './components/icons/Check.svelte'
export { default as IconArrowLeft } from './components/icons/ArrowLeft.svelte' export { default as IconArrowLeft } from './components/icons/ArrowLeft.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 * from './utils' export * from './utils'
export * from './popups' export * from './popups'

View File

@ -63,7 +63,7 @@ export interface Tab {
export type TabModel = Tab[] export type TabModel = Tab[]
export type PopupAlignment = HTMLElement | EventTarget | null | 'right' | 'float' | 'account' | 'full' export type PopupAlignment = HTMLElement | EventTarget | null | 'right' | 'float' | 'account' | 'full' | 'content' | 'middle'
export type TooltipAligment = 'top' | 'bottom' | 'left' | 'right' export type TooltipAligment = 'top' | 'bottom' | 'left' | 'right'

View File

@ -23,7 +23,7 @@
export let inline: boolean = false export let inline: boolean = false
async function show () { async function show () {
showPanel(view.component.EditDoc, value._id, value._class, 'full') showPanel(view.component.EditDoc, value._id, value._class, 'middle')
} }
</script> </script>

View File

@ -33,7 +33,7 @@
} }
function showLead () { function showLead () {
showPanel(view.component.EditDoc, object._id, object._class, 'full') showPanel(view.component.EditDoc, object._id, object._class, 'middle')
} }
</script> </script>

View File

@ -48,6 +48,7 @@
"@anticrm/contact": "~0.6.5", "@anticrm/contact": "~0.6.5",
"@anticrm/view-resources": "~0.6.0", "@anticrm/view-resources": "~0.6.0",
"lexorank": "~1.0.4", "lexorank": "~1.0.4",
"@anticrm/text-editor": "~0.6.0" "@anticrm/text-editor": "~0.6.0",
"@anticrm/panel": "~0.6.0"
} }
} }

View File

@ -15,37 +15,72 @@
--> -->
<script lang="ts"> <script lang="ts">
import contact from '@anticrm/contact' import contact from '@anticrm/contact'
import { getClient, UserBox } from '@anticrm/presentation' import { Class, Ref } from '@anticrm/core'
import type { Issue } from '@anticrm/tracker' import { createQuery, getClient, UserBox } from '@anticrm/presentation'
import { StyledTextBox } from '@anticrm/text-editor' import { StyledTextBox } from '@anticrm/text-editor'
import { EditBox, Grid } from '@anticrm/ui' import type { Issue, Team } from '@anticrm/tracker'
import { AnyComponent, Button, EditBox, Grid, IconDown, IconUp } from '@anticrm/ui'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import tracker from '../../plugin' import tracker from '../../plugin'
import Card from '../Card.svelte' // import Card from '../Card.svelte'
import { Panel } from '@anticrm/ui'
import IssuePresenter from './IssuePresenter.svelte'
export let object: Issue export let _id: Ref<Issue>
export let _class: Ref<Class<Issue>>
export let rightSection: AnyComponent | undefined = undefined
let object: Issue | undefined
let currentTeam: Team | undefined
const query = createQuery()
$: _id &&
_class &&
query.query(_class, { _id }, async (result) => {
object = result[0]
})
$: if (object !== undefined) {
client.findOne(tracker.class.Team, { _id: object.space }).then((r) => {
currentTeam = r
})
}
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const client = getClient() const client = getClient()
function change (field: string, value: any) { function change (field: string, value: any) {
if (object !== undefined) {
client.update(object, { [field]: value }) client.update(object, { [field]: value })
} }
}
onMount(() => { onMount(() => {
dispatch('open', { ignoreKeys: ['comments', 'name', 'description', 'number'] }) dispatch('open', { ignoreKeys: ['comments', 'name', 'description', 'number'] })
}) })
</script> </script>
<Card {#if object !== undefined}
label={tracker.string.NewIssue} <Panel
okAction={() => {}} reverseCommands={true}
canSave={true} useOverlay={false}
rightSection={rightSection !== undefined}
on:close={() => { on:close={() => {
dispatch('close') dispatch('close')
}} }}
> >
{#if object !== undefined} <svelte:fragment slot="subtitle">
{#if currentTeam}
<IssuePresenter value={object} {currentTeam} />
{/if}
</svelte:fragment>
<svelte:fragment slot="actions">
<div class="tool flex gap-1">
<Button icon={IconDown}/>
<Button icon={IconUp}/>
</div>
</svelte:fragment>
<div class="p-10">
<Grid column={1} rowGap={1.5}> <Grid column={1} rowGap={1.5}>
<EditBox <EditBox
label={tracker.string.Title} label={tracker.string.Title}
@ -53,10 +88,14 @@
placeholder={tracker.string.IssueTitlePlaceholder} placeholder={tracker.string.IssueTitlePlaceholder}
maxWidth={'16rem'} maxWidth={'16rem'}
focus focus
on:change={() => change('title', object.title) } on:change={() => change('title', object?.title)}
/>
<StyledTextBox
alwaysEdit
bind:content={object.description}
placeholder={tracker.string.IssueDescriptionPlaceholder}
on:value={(evt) => change('description', evt.detail)}
/> />
<StyledTextBox alwaysEdit bind:content={object.description} placeholder={tracker.string.IssueDescriptionPlaceholder}
on:value={(evt) => change('description', evt.detail) }/>
<UserBox <UserBox
_class={contact.class.Employee} _class={contact.class.Employee}
title={tracker.string.Assignee} title={tracker.string.Assignee}
@ -64,17 +103,19 @@
bind:value={object.assignee} bind:value={object.assignee}
allowDeselect allowDeselect
titleDeselect={tracker.string.TaskUnAssign} titleDeselect={tracker.string.TaskUnAssign}
on:change={() => change('assignee', object.assignee) } on:change={() => change('assignee', object?.assignee)}
/> />
</Grid> </Grid>
</div>
</Panel>
{/if} {/if}
</Card>
<style lang="scss"> <style lang="scss">
.description { .description {
display: flex; display: flex;
padding: 1rem; padding: 1rem;
height: 12rem; height: 12rem;
border-radius: .25rem; border-radius: 0.25rem;
background-color: var(--theme-bg-accent-color); background-color: var(--theme-bg-accent-color);
border: 1px solid var(--theme-bg-accent-color); border: 1px solid var(--theme-bg-accent-color);
} }

View File

@ -16,7 +16,7 @@
<script lang="ts"> <script lang="ts">
import { getClient } from '@anticrm/presentation' import { getClient } from '@anticrm/presentation'
import type { Issue, Team } from '@anticrm/tracker' import type { Issue, Team } from '@anticrm/tracker'
import { Icon, showPopup } from '@anticrm/ui' import { Icon, showPanel } from '@anticrm/ui'
import tracker from '../../plugin' import tracker from '../../plugin'
export let value: Issue export let value: Issue
@ -27,7 +27,7 @@
const shortLabel = client.getHierarchy().getClass(value._class).shortLabel const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
function show (evt: Event) { function show (evt: Event) {
showPopup(tracker.component.EditIssue, { object: value }, evt.currentTarget) showPanel(tracker.component.EditIssue, value._id, value._class, 'content')
} }
</script> </script>

View File

@ -34,6 +34,8 @@
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
let object: Doc let object: Doc
@ -244,6 +246,7 @@
{rightSection} {rightSection}
{fullSize} {fullSize}
{object} {object}
{position}
on:close={() => { on:close={() => {
dispatch('close') dispatch('close')
}} }}

View File

@ -21,13 +21,17 @@
import { getMetadata, IntlString } from '@anticrm/platform' import { getMetadata, IntlString } from '@anticrm/platform'
import { Avatar, createQuery, setClient } from '@anticrm/presentation' import { Avatar, createQuery, setClient } from '@anticrm/presentation'
import { import {
AnyComponent, closePopup, AnyComponent,
closePanel,
closePopup,
closeTooltip, closeTooltip,
Component, getCurrentLocation, Component,
getCurrentLocation,
location, location,
Location, Location,
navigate, navigate,
PanelInstance, PanelInstance,
panelstore,
Popup, Popup,
showPopup, showPopup,
TooltipInstance TooltipInstance
@ -44,7 +48,6 @@
import Navigator from './Navigator.svelte' import Navigator from './Navigator.svelte'
import SpaceView from './SpaceView.svelte' import SpaceView from './SpaceView.svelte'
export let client: Client export let client: Client
setClient(client) setClient(client)
@ -91,8 +94,11 @@
async function updateSpace (spaceId?: Ref<Space>, spaceSpecial?: string): Promise<void> { async function updateSpace (spaceId?: Ref<Space>, spaceSpecial?: string): Promise<void> {
if (spaceId === currentSpace) { if (spaceId === currentSpace) {
// Check if we need update location. // Check if we need update location.
const loc = getCurrentLocation()
if (spaceSpecial !== currentSpecial && spaceSpecial !== asideId) { if (spaceSpecial !== currentSpecial && spaceSpecial !== asideId) {
closePopup()
closePanel()
closeTooltip()
const loc = getCurrentLocation()
if (spaceSpecial !== undefined) { if (spaceSpecial !== undefined) {
setSpaceSpecial(loc, spaceSpecial) setSpaceSpecial(loc, spaceSpecial)
} else { } else {
@ -120,6 +126,9 @@
createItemDialog = currentView?.createItemDialog ?? undefined createItemDialog = currentView?.createItemDialog ?? undefined
createItemLabel = currentView?.createItemLabel ?? undefined createItemLabel = currentView?.createItemLabel ?? undefined
closePopup()
closePanel()
closeTooltip()
const loc = getCurrentLocation() const loc = getCurrentLocation()
loc.path[2] = spaceId loc.path[2] = spaceId
loc.path.length = 3 loc.path.length = 3
@ -160,6 +169,9 @@
loc.path[2] = id loc.path[2] = id
loc.path.length = 3 loc.path.length = 3
navigate(loc) navigate(loc)
closePopup()
closePanel()
closeTooltip()
} }
} }
@ -234,6 +246,10 @@
createItemDialog = undefined createItemDialog = undefined
createItemLabel = undefined createItemLabel = undefined
closePanel()
closeTooltip()
closePopup()
const loc = getCurrentLocation() const loc = getCurrentLocation()
loc.path[1] = app._id loc.path[1] = app._id
loc.path.length = 2 loc.path.length = 2
@ -263,21 +279,29 @@
navigate(loc) navigate(loc)
asideId = undefined asideId = undefined
} }
let contentPanel: HTMLElement
</script> </script>
{#if client} {#if client}
<svg class="svg-mask"> <svg class="svg-mask">
<clipPath id="notify-normal"> <clipPath id="notify-normal">
<path d="M0,0v52.5h52.5V0H0z M34,23.2c-3.2,0-5.8-2.6-5.8-5.8c0-3.2,2.6-5.8,5.8-5.8c3.2,0,5.8,2.6,5.8,5.8 C39.8,20.7,37.2,23.2,34,23.2z" /> <path
d="M0,0v52.5h52.5V0H0z M34,23.2c-3.2,0-5.8-2.6-5.8-5.8c0-3.2,2.6-5.8,5.8-5.8c3.2,0,5.8,2.6,5.8,5.8 C39.8,20.7,37.2,23.2,34,23.2z"
/>
</clipPath> </clipPath>
<clipPath id="notify-small"> <clipPath id="notify-small">
<path d="M0,0v45h45V0H0z M29.5,20c-2.8,0-5-2.2-5-5s2.2-5,5-5s5,2.2,5,5S32.3,20,29.5,20z" /> <path d="M0,0v45h45V0H0z M29.5,20c-2.8,0-5-2.2-5-5s2.2-5,5-5s5,2.2,5,5S32.3,20,29.5,20z" />
</clipPath> </clipPath>
<clipPath id="nub-bg"> <clipPath id="nub-bg">
<path d="M7.3.6 4.2 4.3C2.9 5.4 1.5 6 0 6v1h18V6c-1.5 0-2.9-.6-4.2-1.7L10.7.6C9.9-.1 8.5-.2 7.5.4c0 .1-.1.1-.2.2z" /> <path
d="M7.3.6 4.2 4.3C2.9 5.4 1.5 6 0 6v1h18V6c-1.5 0-2.9-.6-4.2-1.7L10.7.6C9.9-.1 8.5-.2 7.5.4c0 .1-.1.1-.2.2z"
/>
</clipPath> </clipPath>
<clipPath id="nub-border"> <clipPath id="nub-border">
<path d="M4.8 5.1 8 1.3s.1 0 .1-.1c.5-.3 1.4-.3 1.9.1L13.1 5l.1.1 1.2.9H18c-1.5 0-2.9-.6-4.2-1.7L10.7.6C9.9-.1 8.5-.2 7.5.4c0 .1-.1.1-.2.2L4.2 4.3C2.9 5.4 1.5 6 0 6h3.6l1.2-.9z" /> <path
d="M4.8 5.1 8 1.3s.1 0 .1-.1c.5-.3 1.4-.3 1.9.1L13.1 5l.1.1 1.2.9H18c-1.5 0-2.9-.6-4.2-1.7L10.7.6C9.9-.1 8.5-.2 7.5.4c0 .1-.1.1-.2.2L4.2 4.3C2.9 5.4 1.5 6 0 6h3.6l1.2-.9z"
/>
</clipPath> </clipPath>
</svg> </svg>
<div class="workbench-container"> <div class="workbench-container">
@ -319,7 +343,8 @@
notify={hasNotification} notify={hasNotification}
/> />
<div class="flex-center"> <div class="flex-center">
<div id="profile-button" <div
id="profile-button"
class="cursor-pointer" class="cursor-pointer"
on:click|stopPropagation={(el) => { on:click|stopPropagation={(el) => {
showPopup(AccountPopup, {}, 'account') showPopup(AccountPopup, {}, 'account')
@ -337,7 +362,7 @@
{#if currentApplication} {#if currentApplication}
<NavHeader label={currentApplication.label} /> <NavHeader label={currentApplication.label} />
{#if currentApplication.navHeaderComponent} {#if currentApplication.navHeaderComponent}
<Component is={currentApplication.navHeaderComponent} props={{ currentSpace }}/> <Component is={currentApplication.navHeaderComponent} props={{ currentSpace }} />
{/if} {/if}
{/if} {/if}
<Navigator <Navigator
@ -349,24 +374,29 @@
on:archive={(evt) => selectArchive()} on:archive={(evt) => selectArchive()}
/> />
{#if currentApplication.navFooterComponent} {#if currentApplication.navFooterComponent}
<Component is={currentApplication.navFooterComponent} props={{ currentSpace }}/> <Component is={currentApplication.navFooterComponent} props={{ currentSpace }} />
{/if} {/if}
</div> </div>
{/if} {/if}
<div class="antiPanel-component antiComponent border-left"> <div class="antiPanel-component antiComponent border-left" bind:this={contentPanel}>
{#if currentApplication && currentApplication.component} {#if currentApplication && currentApplication.component}
<Component is={currentApplication.component} props={{ currentSpace }}/> <Component is={currentApplication.component} props={{ currentSpace }} />
{:else if specialComponent} {:else if specialComponent}
<Component is={specialComponent.component} props={{ model: navigatorModel, ...specialComponent.componentProps, currentSpace }} /> <Component
is={specialComponent.component}
props={{ model: navigatorModel, ...specialComponent.componentProps, currentSpace }}
/>
{:else} {:else}
<SpaceView {currentSpace} {currentView} {createItemDialog} {createItemLabel} /> <SpaceView {currentSpace} {currentView} {createItemDialog} {createItemLabel} />
{/if} {/if}
</div> </div>
{#if asideId && navigatorModel?.aside !== undefined} {#if asideId && navigatorModel?.aside !== undefined}
<div class="antiPanel-component indent antiComponent filled"><Component is={navigatorModel.aside} props={{ currentSpace, _id: asideId }} on:close={closeAside} /></div> <div class="antiPanel-component indent antiComponent filled">
<Component is={navigatorModel.aside} props={{ currentSpace, _id: asideId }} on:close={closeAside} />
</div>
{/if} {/if}
</div> </div>
<PanelInstance /> <PanelInstance {contentPanel} />
<Popup /> <Popup />
<TooltipInstance /> <TooltipInstance />
{:else} {:else}