mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-29 19:55:20 +00:00
Add additional panel display modes (#1279)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
859640ca5e
commit
0b93f86dfd
@ -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>
|
|
||||||
|
@ -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';
|
||||||
|
59
packages/theme/styles/panel.scss
Normal file
59
packages/theme/styles/panel.scss
Normal 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); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
91
packages/ui/src/components/Panel.svelte
Normal file
91
packages/ui/src/components/Panel.svelte
Normal 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>
|
@ -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 {
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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')
|
||||||
}}
|
}}
|
||||||
|
@ -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}
|
||||||
|
Loading…
Reference in New Issue
Block a user