mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 21:50:34 +03:00
Update EditStatuses, PDFViewer. Add full size for Panel. (#1797)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
8abadca450
commit
d277134032
@ -32,9 +32,18 @@
|
||||
export let isSub: boolean = true
|
||||
export let isAside: boolean = true
|
||||
export let isCustomAttr: boolean = true
|
||||
export let isFullSize: boolean = false
|
||||
</script>
|
||||
|
||||
<Panel bind:isAside isHeader={$$slots.header || isHeader} bind:panelWidth bind:innerWidth on:close>
|
||||
<Panel
|
||||
bind:isAside
|
||||
bind:isFullSize
|
||||
isHeader={$$slots.header || isHeader}
|
||||
bind:panelWidth
|
||||
bind:innerWidth
|
||||
on:close
|
||||
on:fullsize
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="popupPanel-title__content-container antiTitle">
|
||||
{#if $$slots.navigator}
|
||||
|
@ -4,7 +4,7 @@
|
||||
"Cancel": "Отменить",
|
||||
"Ok": "Ок",
|
||||
"Save": "Сохранить",
|
||||
"Download": "Загрузить",
|
||||
"Download": "Скачать",
|
||||
"Close": "Закрыть",
|
||||
"NotSelected": "Не выбрано",
|
||||
"Deselect": "Снять выделение",
|
||||
|
@ -13,43 +13,88 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Button, CircleButton, IconClose, ActionIcon } from '@anticrm/ui'
|
||||
import { Button, Tooltip, Panel } from '@anticrm/ui'
|
||||
import type { PopupOptions } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import presentation from '..'
|
||||
import { getFileUrl } from '../utils'
|
||||
import Avatar from './Avatar.svelte'
|
||||
import MaximizeH from './icons/MaximizeH.svelte'
|
||||
import MaximizeV from './icons/MaximizeV.svelte'
|
||||
import MaximizeO from './icons/MaximizeO.svelte'
|
||||
import Download from './icons/Download.svelte'
|
||||
|
||||
export let file: string
|
||||
export let name: string
|
||||
export let contentType: string | undefined
|
||||
export let options: PopupOptions
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let imgView: 'img-horizontal-fit' | 'img-vertical-fit' | 'img-original-fit' = 'img-horizontal-fit'
|
||||
|
||||
function iconLabel (name: string): string {
|
||||
const parts = name.split('.')
|
||||
const ext = parts[parts.length - 1]
|
||||
return ext.substring(0, 4).toUpperCase()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="antiOverlay"
|
||||
on:click={() => {
|
||||
<Panel
|
||||
isHeader={false}
|
||||
isAside={options && options.fullSize}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
<div class="antiDialogs antiComponent pdfviewer-container">
|
||||
<div class="ac-header short mirror">
|
||||
<div class="ac-header__wrap-title">
|
||||
<div class="ac-header__icon"><Avatar size={'medium'} /></div>
|
||||
<span class="ac-header__title">{name}</span>
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="antiTitle icon-wrapper">
|
||||
<div class="wrapped-icon">
|
||||
<div class="flex-center icon">
|
||||
{iconLabel(name)}
|
||||
</div>
|
||||
</div>
|
||||
<span class="wrapped-title">{name}</span>
|
||||
</div>
|
||||
<ActionIcon
|
||||
icon={IconClose}
|
||||
size={'medium'}
|
||||
action={(_) => {
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="utils">
|
||||
{#if contentType && contentType.startsWith('image/')}
|
||||
<Button
|
||||
icon={MaximizeH}
|
||||
kind={'transparent'}
|
||||
shape={'circle'}
|
||||
on:click={() => {
|
||||
imgView = 'img-horizontal-fit'
|
||||
}}
|
||||
selected={imgView === 'img-horizontal-fit'}
|
||||
/>
|
||||
<Button
|
||||
icon={MaximizeV}
|
||||
kind={'transparent'}
|
||||
shape={'circle'}
|
||||
on:click={() => {
|
||||
imgView = 'img-vertical-fit'
|
||||
}}
|
||||
selected={imgView === 'img-vertical-fit'}
|
||||
/>
|
||||
<Button
|
||||
icon={MaximizeO}
|
||||
kind={'transparent'}
|
||||
shape={'circle'}
|
||||
on:click={() => {
|
||||
imgView = 'img-original-fit'
|
||||
}}
|
||||
selected={imgView === 'img-original-fit'}
|
||||
/>
|
||||
<div class="buttons-divider" />
|
||||
{/if}
|
||||
<a class="no-line" href={getFileUrl(file)} download={name}>
|
||||
<Tooltip label={presentation.string.Download}>
|
||||
<Button icon={Download} kind={'transparent'} shape={'circle'} />
|
||||
</Tooltip>
|
||||
</a>
|
||||
</svelte:fragment>
|
||||
|
||||
{#if contentType && contentType.startsWith('image/')}
|
||||
<div class="pdfviewer-content">
|
||||
@ -58,55 +103,26 @@
|
||||
{:else}
|
||||
<iframe class="pdfviewer-content" src={getFileUrl(file)} title="" />
|
||||
{/if}
|
||||
|
||||
<div class="pdfviewer-footer">
|
||||
<div class="flex-row-reverse">
|
||||
<a class="no-line ml-4" href={getFileUrl(file)} download={name}
|
||||
><Button label={presentation.string.Download} kind={'primary'} /></a
|
||||
>
|
||||
<Button
|
||||
label={presentation.string.Close}
|
||||
on:click={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if contentType && contentType.startsWith('image/')}
|
||||
<div class="img-nav">
|
||||
<CircleButton
|
||||
icon={MaximizeH}
|
||||
on:click={() => {
|
||||
imgView = 'img-horizontal-fit'
|
||||
}}
|
||||
selected={imgView === 'img-horizontal-fit'}
|
||||
/>
|
||||
<CircleButton
|
||||
icon={MaximizeV}
|
||||
on:click={() => {
|
||||
imgView = 'img-vertical-fit'
|
||||
}}
|
||||
selected={imgView === 'img-vertical-fit'}
|
||||
/>
|
||||
<CircleButton
|
||||
icon={MaximizeO}
|
||||
on:click={() => {
|
||||
imgView = 'img-original-fit'
|
||||
}}
|
||||
selected={imgView === 'img-original-fit'}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<style lang="scss">
|
||||
.pdfviewer-container {
|
||||
left: 40%;
|
||||
.icon {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.625rem;
|
||||
color: var(--white-color);
|
||||
background-color: var(--primary-bg-color);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pdfviewer-content {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
margin: 0 1.5rem;
|
||||
margin: 1.5rem;
|
||||
border-style: none;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--theme-menu-color);
|
||||
@ -124,14 +140,12 @@
|
||||
.img-vertical-fit {
|
||||
height: 100%;
|
||||
}
|
||||
.pdfviewer-footer {
|
||||
.pdfviewer-header {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
padding: 0 2.25rem;
|
||||
height: 5.25rem;
|
||||
}
|
||||
.img-nav {
|
||||
display: grid;
|
||||
|
28
packages/presentation/src/components/icons/Download.svelte
Normal file
28
packages/presentation/src/components/icons/Download.svelte
Normal file
@ -0,0 +1,28 @@
|
||||
<!--
|
||||
// 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: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12,8.8c-0.2,0-0.4-0.1-0.5-0.4c-0.2-0.5-0.6-1-1-1.3v1.6c0,0.1,0.1,0.1,0.1,0.2c0.2,0.5,0.7,1,1.4,1c0.8,0,1.5,0.7,1.5,1.5 s-0.7,1.5-1.5,1.5H8H4c-0.8,0-1.5-0.7-1.5-1.5S3.2,9.8,4,9.8c0.7,0,1.2-0.5,1.4-1c0-0.1,0.1-0.1,0.1-0.2V7.1c-0.4,0.4-0.8,0.8-1,1.3 C4.4,8.7,4.2,8.8,4,8.8c-1.4,0-2.5,1.1-2.5,2.5s1.1,2.5,2.5,2.5h4h4c1.4,0,2.5-1.1,2.5-2.5S13.4,8.8,12,8.8z"
|
||||
/>
|
||||
<path
|
||||
d="M7.5,3.2v5.5c0,0.3,0.2,0.5,0.5,0.5c0.3,0,0.5-0.2,0.5-0.5V3.2L10.3,5L11,4.3L8.4,1.6L8,1.3L7.6,1.6L8,2l0,0L7.6,1.6L5,4.3 L5.7,5L7.5,3.2z"
|
||||
/>
|
||||
</svg>
|
@ -356,6 +356,7 @@ input.search {
|
||||
.mb-3 { margin-bottom: .75rem; }
|
||||
.mb-4 { margin-bottom: 1rem; }
|
||||
.mb-6 { margin-bottom: 1.5rem; }
|
||||
.mb-10 { margin-bottom: 2.5rem; }
|
||||
.mx-1 { margin: 0 .25rem; }
|
||||
.mx-2 { margin: 0 .5rem; }
|
||||
.mx-3 { margin: 0 .75rem; }
|
||||
|
@ -432,8 +432,7 @@
|
||||
|
||||
// THead background-color in Tooltip and Popups
|
||||
.popup-tooltip .antiTable .scroller-thead,
|
||||
.popup .antiTable .scroller-thead,
|
||||
.antiDialogs .antiTable .scroller-thead { background-color: var(--accent-bg-color); }
|
||||
.popup .antiTable .scroller-thead { background-color: var(--accent-bg-color); }
|
||||
|
||||
// Hide row menu in Tooltip
|
||||
.popup-tooltip .antiTable .antiTable-body__row:hover .antiTable-cells__firstCell .antiTable-cells__firstCell-menuRow { visibility: hidden; }
|
||||
|
@ -13,51 +13,6 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
/* Dialogs */
|
||||
.antiDialogs {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 32px;
|
||||
bottom: 1.25rem;
|
||||
left: 50%;
|
||||
right: 1rem;
|
||||
|
||||
height: calc(100% - 32px - 1.25rem);
|
||||
background: var(--popup-bg-color);
|
||||
border-radius: .5rem;
|
||||
|
||||
&.fullSize {
|
||||
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: 1.375rem;
|
||||
right: 2rem;
|
||||
}
|
||||
.tool {
|
||||
margin-left: 1rem;
|
||||
color: var(--theme-content-accent-color);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover { color: var(--theme-caption-color); }
|
||||
}
|
||||
}
|
||||
|
||||
/* Overlays */
|
||||
.antiOverlay {
|
||||
position: fixed;
|
||||
|
@ -212,3 +212,24 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Full size state
|
||||
.panel-instance.fullsize .panel-container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.panel-instance.fullsize .panel-container .popupPanel,
|
||||
.popup.fullsize .popupPanel {
|
||||
border-radius: 0;
|
||||
box-shadow: none !important;
|
||||
|
||||
.popupPanel-title,
|
||||
.popupPanel-body {
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
}
|
||||
.popupPanel-title { border-bottom: 1px solid var(--divider-color); }
|
||||
}
|
||||
.panel-instance.fullsize,
|
||||
.popup.fullsize {
|
||||
transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19) !important;
|
||||
}
|
||||
|
@ -14,12 +14,13 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Button, IconClose, IconDetails } from '..'
|
||||
import { Button, IconClose, IconDetails, IconExpand } from '..'
|
||||
|
||||
export let innerWidth: number = 0
|
||||
export let panelWidth: number = 0
|
||||
export let isHeader: boolean = true
|
||||
export let isAside: boolean = true
|
||||
export let isFullSize: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -30,8 +31,13 @@
|
||||
asideFloat = false
|
||||
asideShown = false
|
||||
}
|
||||
let docWidth: number
|
||||
let fullSize: boolean = false
|
||||
$: if (docWidth <= 900 && !fullSize) fullSize = true
|
||||
$: if (docWidth > 900 && fullSize) fullSize = false
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth={docWidth} />
|
||||
<div class="popupPanel" bind:clientWidth={panelWidth}>
|
||||
<div class="popupPanel-title">
|
||||
<Button
|
||||
@ -45,8 +51,8 @@
|
||||
<div class="popupPanel-title__content"><slot name="title" /></div>
|
||||
<div class="buttons-group xsmall-gap">
|
||||
<slot name="utils" />
|
||||
{#if $$slots.utils}<div class="buttons-divider" />{/if}
|
||||
{#if asideFloat && $$slots.aside && isAside}
|
||||
{#if $$slots.utils}<div class="buttons-divider" />{/if}
|
||||
<Button
|
||||
icon={IconDetails}
|
||||
kind={'transparent'}
|
||||
@ -57,6 +63,19 @@
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{#if isFullSize}
|
||||
<Button
|
||||
icon={IconExpand}
|
||||
kind={'transparent'}
|
||||
size={'medium'}
|
||||
selected={fullSize}
|
||||
disabled={docWidth <= 900}
|
||||
on:click={() => {
|
||||
fullSize = !fullSize
|
||||
dispatch('fullsize')
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="popupPanel-body" class:asideShown>
|
||||
|
@ -15,8 +15,9 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { afterUpdate } from 'svelte'
|
||||
import { AnySvelteComponent, Spinner } from '..'
|
||||
import { afterUpdate, onMount } from 'svelte'
|
||||
import { Spinner } from '..'
|
||||
import type { AnySvelteComponent, PopupOptions } from '..'
|
||||
import { closePanel, PanelProps, panelstore } from '../panelup'
|
||||
import { fitPopupElement, popupstore } from '../popups'
|
||||
|
||||
@ -24,11 +25,25 @@
|
||||
|
||||
let modalHTML: HTMLElement
|
||||
let componentInstance: any
|
||||
let docWidth: number
|
||||
let docSize: boolean = false
|
||||
let fullSize: boolean = false
|
||||
|
||||
let options: {
|
||||
show: boolean
|
||||
direction: string
|
||||
} = { show: false, direction: 'bottom' }
|
||||
let options: PopupOptions = {
|
||||
props: {
|
||||
top: '',
|
||||
bottom: '',
|
||||
left: '',
|
||||
right: '',
|
||||
width: '',
|
||||
height: '',
|
||||
maxWidth: '',
|
||||
maxHeight: '',
|
||||
minWidth: ''
|
||||
},
|
||||
showOverlay: false,
|
||||
direction: 'bottom'
|
||||
}
|
||||
|
||||
let component: AnySvelteComponent
|
||||
|
||||
@ -62,7 +77,17 @@
|
||||
|
||||
const fitPopup = (props: PanelProps, contentPanel: HTMLElement): void => {
|
||||
if (modalHTML) {
|
||||
options = fitPopupElement(modalHTML, props.element, contentPanel)
|
||||
if ((fullSize || docSize) && (props.element === 'float' || props.element === 'content')) {
|
||||
options = fitPopupElement(modalHTML, 'full')
|
||||
options.props.width = '100vw'
|
||||
options.props.maxHeight = 'max-content'
|
||||
options.showOverlay = false
|
||||
modalHTML.classList.add('fullsize')
|
||||
} else {
|
||||
options = fitPopupElement(modalHTML, props.element, contentPanel)
|
||||
modalHTML.classList.remove('fullsize')
|
||||
}
|
||||
options.fullSize = fullSize
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,9 +107,16 @@
|
||||
export function fitPopupInstance (): void {
|
||||
if (props) fitPopup(props, contentPanel)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (props) fitPopup(props, contentPanel)
|
||||
})
|
||||
$: if (docWidth <= 900 && !docSize) docSize = true
|
||||
$: if (docWidth > 900 && docSize) docSize = false
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
bind:innerWidth={docWidth}
|
||||
on:resize={() => {
|
||||
if (props) fitPopup(props, contentPanel)
|
||||
}}
|
||||
@ -97,7 +129,20 @@
|
||||
<Spinner />
|
||||
{:else}
|
||||
<slot name="panel-header" />
|
||||
<div class="panel-instance" class:bg={props.element === 'content'} bind:this={modalHTML}>
|
||||
<div
|
||||
class="panel-instance"
|
||||
class:bg={props.element === 'content'}
|
||||
bind:this={modalHTML}
|
||||
style:top={options.props.top}
|
||||
style:bottom={options.props.bottom}
|
||||
style:left={options.props.left}
|
||||
style:right={options.props.right}
|
||||
style:width={options.props.width}
|
||||
style:height={options.props.height}
|
||||
style:max-width={options.props.maxWidth}
|
||||
style:max-height={options.props.maxHeight}
|
||||
style:min-width={options.props.minWidth}
|
||||
>
|
||||
<div class="panel-container" class:padding={props.element === 'content'}>
|
||||
<svelte:component
|
||||
this={component}
|
||||
@ -106,15 +151,19 @@
|
||||
_class={props._class}
|
||||
rightSection={props.rightSection}
|
||||
position={props.element}
|
||||
bind:options
|
||||
on:close={_close}
|
||||
on:update={_update}
|
||||
on:fullsize={() => {
|
||||
fullSize = !fullSize
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if props.element !== 'content'}
|
||||
<div
|
||||
class="modal-overlay"
|
||||
class:show={options.show}
|
||||
class:show={options.showOverlay}
|
||||
on:click={() => escapeClose()}
|
||||
on:keydown={() => {}}
|
||||
on:keyup={() => {}}
|
||||
@ -128,6 +177,10 @@
|
||||
z-index: 401;
|
||||
position: fixed;
|
||||
background-color: transparent;
|
||||
will-change: top, bottom, left, right;
|
||||
transition-property: top, bottom, left, right, width, height;
|
||||
transition-duration: 0.15s;
|
||||
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
|
||||
&.bg {
|
||||
background-color: var(--body-color);
|
||||
@ -154,4 +207,7 @@
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
:global(.panel-instance.fullsize) {
|
||||
transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1) !important;
|
||||
}
|
||||
</style>
|
||||
|
@ -14,9 +14,9 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { afterUpdate } from 'svelte'
|
||||
import { afterUpdate, onMount } from 'svelte'
|
||||
import { fitPopupElement } from '../popups'
|
||||
import type { AnyComponent, AnySvelteComponent, PopupAlignment } from '../types'
|
||||
import type { AnyComponent, AnySvelteComponent, PopupAlignment, PopupOptions } from '../types'
|
||||
|
||||
export let is: AnyComponent | AnySvelteComponent
|
||||
export let props: object
|
||||
@ -29,12 +29,25 @@
|
||||
|
||||
let modalHTML: HTMLElement
|
||||
let componentInstance: any
|
||||
let height: number
|
||||
let docWidth: number
|
||||
let docSize: boolean = false
|
||||
let fullSize: boolean = false
|
||||
|
||||
let options: {
|
||||
show: boolean
|
||||
direction: string
|
||||
} = { show: false, direction: 'bottom' }
|
||||
let options: PopupOptions = {
|
||||
props: {
|
||||
top: '',
|
||||
bottom: '',
|
||||
left: '',
|
||||
right: '',
|
||||
width: '',
|
||||
height: '',
|
||||
maxWidth: '',
|
||||
maxHeight: '',
|
||||
minWidth: ''
|
||||
},
|
||||
showOverlay: false,
|
||||
direction: 'bottom'
|
||||
}
|
||||
|
||||
function _update (result: any): void {
|
||||
if (onUpdate !== undefined) onUpdate(result)
|
||||
@ -55,7 +68,14 @@
|
||||
|
||||
const fitPopup = (): void => {
|
||||
if (modalHTML) {
|
||||
options = fitPopupElement(modalHTML, element)
|
||||
if ((fullSize || docSize) && (element === 'float' || element === 'content')) {
|
||||
options = fitPopupElement(modalHTML, 'full')
|
||||
modalHTML.classList.add('fullsize')
|
||||
} else {
|
||||
options = fitPopupElement(modalHTML, element)
|
||||
modalHTML.classList.remove('fullsize')
|
||||
}
|
||||
options.fullSize = fullSize
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,27 +84,47 @@
|
||||
escapeClose()
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => fitPopup())
|
||||
$: if (docWidth <= 900 && !docSize) docSize = true
|
||||
$: if (docWidth > 900 && docSize) docSize = false
|
||||
|
||||
afterUpdate(() => fitPopup())
|
||||
$: if (height) fitPopup()
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={fitPopup} on:keydown={handleKeydown} />
|
||||
<svelte:window on:resize={fitPopup} on:keydown={handleKeydown} bind:innerWidth={docWidth} />
|
||||
|
||||
<div class="popup" bind:this={modalHTML} bind:clientHeight={height} style={`z-index: ${zIndex + 1};`}>
|
||||
<div
|
||||
class="popup"
|
||||
bind:this={modalHTML}
|
||||
style={`z-index: ${zIndex + 1};`}
|
||||
style:top={options.props.top}
|
||||
style:bottom={options.props.bottom}
|
||||
style:left={options.props.left}
|
||||
style:right={options.props.right}
|
||||
style:width={options.props.width}
|
||||
style:height={options.props.height}
|
||||
style:max-width={options.props.maxWidth}
|
||||
style:max-height={options.props.maxHeight}
|
||||
style:min-width={options.props.minWidth}
|
||||
>
|
||||
<svelte:component
|
||||
this={is}
|
||||
bind:this={componentInstance}
|
||||
{...props}
|
||||
direction={options.direction}
|
||||
bind:options
|
||||
on:update={(ev) => {
|
||||
_update(ev.detail)
|
||||
}}
|
||||
on:close={(ev) => _close(ev.detail)}
|
||||
on:fullsize={() => {
|
||||
fullSize = !fullSize
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="modal-overlay"
|
||||
class:antiOverlay={options.show}
|
||||
class:antiOverlay={options.showOverlay}
|
||||
style={`z-index: ${zIndex};`}
|
||||
on:click={() => escapeClose()}
|
||||
/>
|
||||
@ -97,6 +137,10 @@
|
||||
justify-content: center;
|
||||
max-height: calc(100vh - 2rem);
|
||||
background-color: transparent;
|
||||
will-change: top, bottom, left, right;
|
||||
transition-property: top, bottom, left, right, width, height;
|
||||
transition-duration: 0.15s;
|
||||
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { AnySvelteComponent, AnyComponent, PopupAlignment, PopupPositionElement } from './types'
|
||||
import type { AnySvelteComponent, AnyComponent, PopupAlignment, PopupPositionElement, PopupOptions } from './types'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
@ -117,7 +117,7 @@ export function fitPopupPositionedElement (
|
||||
modalHTML: HTMLElement,
|
||||
alignment: PopupPositionElement,
|
||||
newProps: Record<string, string | number>
|
||||
): { show: boolean, direction: string } {
|
||||
): PopupOptions {
|
||||
let direction: string = ''
|
||||
const rect = alignment.getBoundingClientRect()
|
||||
const rectPopup = modalHTML.getBoundingClientRect()
|
||||
@ -159,17 +159,17 @@ export function fitPopupPositionedElement (
|
||||
direction += '|right'
|
||||
}
|
||||
}
|
||||
return { show: false, direction }
|
||||
return { props: newProps, showOverlay: false, direction }
|
||||
}
|
||||
|
||||
function applyStyle (values: Record<string, string | number>, modalHTML: HTMLElement): void {
|
||||
for (const [k, v] of Object.entries(values)) {
|
||||
const old = (modalHTML.style as any)[k]
|
||||
if (old !== v) {
|
||||
;(modalHTML.style as any)[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
// function applyStyle (values: Record<string, string | number>, modalHTML: HTMLElement): void {
|
||||
// for (const [k, v] of Object.entries(values)) {
|
||||
// const old = (modalHTML.style as any)[k]
|
||||
// if (old !== v) {
|
||||
// ;(modalHTML.style as any)[k] = v
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -182,7 +182,7 @@ export function fitPopupElement (
|
||||
modalHTML: HTMLElement,
|
||||
element?: PopupAlignment,
|
||||
contentPanel?: HTMLElement
|
||||
): { show: boolean, direction: string } {
|
||||
): PopupOptions {
|
||||
let show = true
|
||||
const newProps: Record<string, string | number> = {}
|
||||
if (element != null) {
|
||||
@ -192,7 +192,7 @@ export function fitPopupElement (
|
||||
newProps.maxWidth = newProps.width = newProps.minWidth = ''
|
||||
if (typeof element !== 'string') {
|
||||
const result = fitPopupPositionedElement(modalHTML, element, newProps)
|
||||
applyStyle(newProps, modalHTML)
|
||||
// applyStyle(newProps, modalHTML)
|
||||
return result
|
||||
} else if (element === 'right' && contentPanel !== undefined) {
|
||||
const rect = contentPanel.getBoundingClientRect()
|
||||
@ -209,13 +209,20 @@ export function fitPopupElement (
|
||||
} else if (element === 'float') {
|
||||
newProps.top = 'calc(var(--status-bar-height) + .25rem)'
|
||||
newProps.bottom = '.25rem'
|
||||
newProps.minWidth = '40rem'
|
||||
newProps.width = '40%'
|
||||
newProps.left = '60%'
|
||||
newProps.right = '.25rem'
|
||||
show = true
|
||||
} else if (element === 'account') {
|
||||
newProps.bottom = '2.75rem'
|
||||
newProps.left = '5rem'
|
||||
} else if (element === 'full' && contentPanel === undefined) {
|
||||
newProps.top = '0'
|
||||
newProps.bottom = '0'
|
||||
newProps.left = '0'
|
||||
newProps.right = '0'
|
||||
// newProps.width = '100vw'
|
||||
newProps.height = '100vh'
|
||||
show = false
|
||||
} else if (element === 'full' && contentPanel !== undefined) {
|
||||
const rect = contentPanel.getBoundingClientRect()
|
||||
newProps.top = `${rect.top + 1}px`
|
||||
@ -248,8 +255,8 @@ export function fitPopupElement (
|
||||
newProps.transform = 'translate(-50%, -50%)'
|
||||
show = true
|
||||
}
|
||||
applyStyle(newProps, modalHTML)
|
||||
return { show, direction: '' }
|
||||
// applyStyle(newProps, modalHTML)
|
||||
return { props: newProps, showOverlay: show, direction: '' }
|
||||
}
|
||||
|
||||
export function eventToHTMLElement (evt: MouseEvent): HTMLElement {
|
||||
|
@ -110,3 +110,10 @@ export interface DropdownIntlItem {
|
||||
id: string
|
||||
label: IntlString
|
||||
}
|
||||
|
||||
export interface PopupOptions {
|
||||
props: Record<string, string | number>
|
||||
showOverlay: boolean
|
||||
direction: string
|
||||
fullSize?: boolean
|
||||
}
|
||||
|
@ -42,7 +42,7 @@
|
||||
|
||||
function openAttachment () {
|
||||
closeTooltip()
|
||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
|
||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'float')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -46,7 +46,7 @@
|
||||
class="flex-center icon"
|
||||
on:click={() => {
|
||||
closeTooltip()
|
||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
|
||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'float')
|
||||
}}
|
||||
>
|
||||
{iconLabel(value.name)}
|
||||
@ -86,7 +86,7 @@
|
||||
class="name"
|
||||
on:click={() => {
|
||||
closeTooltip()
|
||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
|
||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'float')
|
||||
}}
|
||||
>
|
||||
{trimFilename(value.name)}
|
||||
|
@ -33,7 +33,7 @@
|
||||
class="content flex-center buttonContainer cursor-pointer"
|
||||
on:click={() => {
|
||||
closeTooltip()
|
||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
|
||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'float')
|
||||
}}
|
||||
>
|
||||
<img src={getFileUrl(value.file)} alt={value.name} />
|
||||
|
@ -88,7 +88,7 @@
|
||||
const el: HTMLElement = ev.currentTarget as HTMLElement
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' })
|
||||
if (item !== undefined) {
|
||||
showPopup(PDFViewer, { file: item.file, name: item.name, contentType: item.type }, 'right')
|
||||
showPopup(PDFViewer, { file: item.file, name: item.name, contentType: item.type }, 'float')
|
||||
} else {
|
||||
inputFile.click()
|
||||
}
|
||||
|
@ -88,6 +88,8 @@
|
||||
{object}
|
||||
isHeader={false}
|
||||
isAside={true}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<svelte:fragment slot="navigator">
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
function handleClick () {
|
||||
closeTooltip()
|
||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
|
||||
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'float')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -64,6 +64,8 @@
|
||||
<Panel
|
||||
isHeader={false}
|
||||
isAside={_class === chunter.class.Channel}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
|
@ -28,13 +28,14 @@
|
||||
} from '@anticrm/ui'
|
||||
import IconCopy from './icons/Copy.svelte'
|
||||
import { FocusHandler } from '@anticrm/ui'
|
||||
import type { PopupOptions } from '@anticrm/ui'
|
||||
import plugin from '../plugin'
|
||||
|
||||
export let value: string = ''
|
||||
export let placeholder: IntlString
|
||||
export let editable: boolean | undefined = undefined
|
||||
export let openable: boolean = false
|
||||
export let direction: string = 'bottom'
|
||||
export let options: PopupOptions
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let input: HTMLInputElement
|
||||
@ -75,11 +76,12 @@
|
||||
input.addEventListener('focus', updateFocus, { once: true })
|
||||
}
|
||||
|
||||
let dir: string = 'bottom'
|
||||
const vDir = (d: string): string => d.split('|')[0]
|
||||
const fitEditor = (): void => {
|
||||
dir = vDir(direction)
|
||||
if (options) dir = vDir(options.direction)
|
||||
}
|
||||
$: dir = vDir(direction)
|
||||
$: if (options) dir = vDir(options.direction)
|
||||
afterUpdate(() => {
|
||||
fitEditor()
|
||||
})
|
||||
|
@ -76,6 +76,8 @@
|
||||
<Panel
|
||||
isHeader={true}
|
||||
isAside={false}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
|
@ -25,6 +25,7 @@
|
||||
"GotoLeadApplication": "Switch to Lead Application",
|
||||
"IssueDescriptionPlaceholder": "Add description...",
|
||||
"CreateCustomer": "Create Customer",
|
||||
"CreateCustomerLabel": "New Customer"
|
||||
"CreateCustomerLabel": "New Customer",
|
||||
"NoLeadsForDocument": "No leads for document"
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
"GotoLeadApplication": "Открыть приложение Сделки",
|
||||
"IssueDescriptionPlaceholder": "Добавить описание...",
|
||||
"CreateCustomer": "Добавить Клиента",
|
||||
"CreateCustomerLabel": "Новый Клиент"
|
||||
"CreateCustomerLabel": "Новый Клиент",
|
||||
"NoLeadsForDocument": "Нет потенциальных клиентов для документа"
|
||||
}
|
||||
}
|
@ -538,7 +538,7 @@
|
||||
focusIndex={103}
|
||||
icon={FileIcon}
|
||||
on:click={() => {
|
||||
showPopup(PDFViewer, { file: resume.uuid, name: resume.name }, 'right')
|
||||
showPopup(PDFViewer, { file: resume.uuid, name: resume.name }, 'float')
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="content">
|
||||
|
@ -58,6 +58,8 @@
|
||||
isHeader={false}
|
||||
isAside={true}
|
||||
{object}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
|
@ -54,6 +54,8 @@
|
||||
isHeader={false}
|
||||
isAside={true}
|
||||
{object}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
|
@ -20,7 +20,7 @@
|
||||
import type { DoneState, Kanban, SpaceWithStates, State } from '@anticrm/task'
|
||||
import task from '../../plugin'
|
||||
import KanbanEditor from '../kanban/KanbanEditor.svelte'
|
||||
import { Icon, IconClose, Label, showPopup, ActionIcon, ScrollBox } from '@anticrm/ui'
|
||||
import { Icon, Label, showPopup, Panel, Scroller } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import workbench from '@anticrm/workbench'
|
||||
|
||||
@ -91,39 +91,35 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="antiOverlay"
|
||||
on:click={() => {
|
||||
<Panel
|
||||
isHeader={false}
|
||||
isAside={false}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
<div class="antiDialogs antiComponent">
|
||||
<div class="ac-header short mirror divide">
|
||||
<div class="ac-header__wrap-description">
|
||||
<div class="ac-header__wrap-title">
|
||||
<div class="ac-header__icon"><Icon icon={task.icon.ManageStatuses} size={'small'} /></div>
|
||||
<span class="ac-header__title">
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="antiTitle icon-wrapper">
|
||||
<div class="wrapped-icon">
|
||||
<Icon icon={task.icon.ManageStatuses} size={'small'} />
|
||||
</div>
|
||||
<div class="title-wrapper">
|
||||
<span class="wrapped-title">
|
||||
<Label label={task.string.ManageStatusesWithin} />
|
||||
{#if spaceClassInstance}<Label label={spaceClassInstance?.label} />{:else}...{/if}
|
||||
</span>
|
||||
{#if spaceInstance?.name}<span class="wrapped-subtitle">{spaceInstance?.name}</span>{/if}
|
||||
</div>
|
||||
{#if spaceInstance?.name}<span class="ac-header__description">{spaceInstance?.name}</span>{/if}
|
||||
</div>
|
||||
<div class="tool">
|
||||
<ActionIcon
|
||||
icon={IconClose}
|
||||
size={'small'}
|
||||
action={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-10 flex-grow">
|
||||
<ScrollBox vertical stretch>
|
||||
</svelte:fragment>
|
||||
|
||||
<Scroller>
|
||||
<div class="popupPanel-body__main-content py-10 clear-mins">
|
||||
{#if kanban !== undefined}
|
||||
<KanbanEditor {kanban} on:delete={(e) => deleteState(e.detail)} />
|
||||
{/if}
|
||||
</ScrollBox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Scroller>
|
||||
</Panel>
|
||||
|
@ -75,68 +75,66 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="flex-no-shrink flex-between trans-title uppercase">
|
||||
<Label label={task.string.ActiveStates} />
|
||||
<CircleButton
|
||||
icon={IconAdd}
|
||||
size={'medium'}
|
||||
on:click={() => {
|
||||
onAdd(task.class.State)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
{#each states as state, i}
|
||||
{#if state}
|
||||
<div class="flex-no-shrink flex-between trans-title uppercase">
|
||||
<Label label={task.string.ActiveStates} />
|
||||
<CircleButton
|
||||
icon={IconAdd}
|
||||
size={'medium'}
|
||||
on:click={() => {
|
||||
onAdd(task.class.State)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
{#each states as state, i}
|
||||
{#if state}
|
||||
<div
|
||||
bind:this={elements[i]}
|
||||
class="flex-between states"
|
||||
draggable={true}
|
||||
on:dragover|preventDefault={(ev) => {
|
||||
dragover(ev, i)
|
||||
}}
|
||||
on:drop|preventDefault={() => {
|
||||
onMove(i)
|
||||
}}
|
||||
on:dragstart={() => {
|
||||
selected = i
|
||||
dragState = states[i]._id
|
||||
}}
|
||||
on:dragend={() => {
|
||||
selected = undefined
|
||||
}}
|
||||
>
|
||||
<div class="bar"><Circles /></div>
|
||||
<div
|
||||
bind:this={elements[i]}
|
||||
class="flex-between states"
|
||||
draggable={true}
|
||||
on:dragover|preventDefault={(ev) => {
|
||||
dragover(ev, i)
|
||||
class="color"
|
||||
style="background-color: {getPlatformColor(state.color)}"
|
||||
on:click={() => {
|
||||
showPopup(ColorsPopup, {}, elements[i], onColorChange(state))
|
||||
}}
|
||||
on:drop|preventDefault={() => {
|
||||
onMove(i)
|
||||
}}
|
||||
on:dragstart={() => {
|
||||
selected = i
|
||||
dragState = states[i]._id
|
||||
}}
|
||||
on:dragend={() => {
|
||||
selected = undefined
|
||||
}}
|
||||
>
|
||||
<div class="bar"><Circles /></div>
|
||||
<div
|
||||
class="color"
|
||||
style="background-color: {getPlatformColor(state.color)}"
|
||||
on:click={() => {
|
||||
showPopup(ColorsPopup, {}, elements[i], onColorChange(state))
|
||||
}}
|
||||
/>
|
||||
<div class="flex-grow caption-color">
|
||||
<AttributeEditor maxWidth={'20rem'} _class={state._class} object={state} key="title" />
|
||||
</div>
|
||||
{#if states.length > 1}
|
||||
<div
|
||||
class="tool hover-trans"
|
||||
on:click={(ev) => {
|
||||
showPopup(
|
||||
StatusesPopup,
|
||||
{ onDelete: () => dispatch('delete', { state }) },
|
||||
eventToHTMLElement(ev),
|
||||
() => {}
|
||||
)
|
||||
}}
|
||||
>
|
||||
<IconMoreH size={'medium'} />
|
||||
</div>
|
||||
{/if}
|
||||
/>
|
||||
<div class="flex-grow caption-color">
|
||||
<AttributeEditor maxWidth={'20rem'} _class={state._class} object={state} key="title" />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{#if states.length > 1}
|
||||
<div
|
||||
class="tool hover-trans"
|
||||
on:click={(ev) => {
|
||||
showPopup(
|
||||
StatusesPopup,
|
||||
{ onDelete: () => dispatch('delete', { state }) },
|
||||
eventToHTMLElement(ev),
|
||||
() => {}
|
||||
)
|
||||
}}
|
||||
>
|
||||
<IconMoreH size={'medium'} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="mt-9">
|
||||
<div class="flex-no-shrink flex-between trans-title uppercase">
|
||||
@ -189,7 +187,7 @@
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="mt-4 mb-10">
|
||||
{#each lostStates as state}
|
||||
{#if state}
|
||||
<div class="states flex-row-center">
|
||||
@ -222,10 +220,10 @@
|
||||
<style lang="scss">
|
||||
.states {
|
||||
padding: 0.5rem 1rem;
|
||||
color: var(--theme-caption-color);
|
||||
background-color: var(--theme-button-bg-enabled);
|
||||
border: 1px solid var(--theme-bg-accent-color);
|
||||
border-radius: 0.75rem;
|
||||
color: var(--caption-color);
|
||||
background-color: var(--button-bg-color);
|
||||
border: 1px solid var(--button-border-color);
|
||||
border-radius: 0.5rem;
|
||||
user-select: none;
|
||||
|
||||
.bar {
|
||||
|
@ -43,7 +43,7 @@ import AssignedTasks from './components/AssignedTasks.svelte'
|
||||
import task from './plugin'
|
||||
|
||||
async function editStatuses (object: SpaceWithStates): Promise<void> {
|
||||
showPopup(EditStatuses, { _id: object._id, spaceClass: object._class }, 'right')
|
||||
showPopup(EditStatuses, { _id: object._id, spaceClass: object._class }, 'float')
|
||||
}
|
||||
|
||||
export async function queryTask<D extends Task> (
|
||||
|
@ -188,6 +188,8 @@
|
||||
<Panel
|
||||
isHeader={true}
|
||||
isAside={false}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
|
@ -139,6 +139,8 @@
|
||||
isSub={false}
|
||||
withoutActivity={isEditing}
|
||||
bind:innerWidth
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<svelte:fragment slot="subtitle">
|
||||
|
@ -228,6 +228,8 @@
|
||||
isAside={true}
|
||||
bind:panelWidth
|
||||
bind:innerWidth
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:update={(ev) => _update(ev.detail)}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
|
@ -55,6 +55,8 @@
|
||||
<Panel
|
||||
isHeader={false}
|
||||
isAside={false}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
|
Loading…
Reference in New Issue
Block a user