mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-17 22:03:09 +00:00
Inbox as popup (#2957)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
3c0fdc9698
commit
981cbada6a
@ -15,7 +15,7 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ObjectSearchPopup, ObjectSearchResult } from '@hcengineering/presentation'
|
import { ObjectSearchPopup, ObjectSearchResult } from '@hcengineering/presentation'
|
||||||
import { showPopup, resizeObserver, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
import { showPopup, resizeObserver, deviceOptionsStore as deviceInfo, PopupResult } from '@hcengineering/ui'
|
||||||
import { onDestroy, onMount } from 'svelte'
|
import { onDestroy, onMount } from 'svelte'
|
||||||
import DummyPopup from './DummyPopup.svelte'
|
import DummyPopup from './DummyPopup.svelte'
|
||||||
|
|
||||||
@ -25,10 +25,10 @@
|
|||||||
export let close: () => void
|
export let close: () => void
|
||||||
|
|
||||||
let popup: HTMLDivElement
|
let popup: HTMLDivElement
|
||||||
let popupClose: () => void
|
let dummyPopup: PopupResult
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
popupClose = showPopup(
|
dummyPopup = showPopup(
|
||||||
DummyPopup,
|
DummyPopup,
|
||||||
{},
|
{},
|
||||||
undefined,
|
undefined,
|
||||||
@ -39,7 +39,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
popupClose()
|
dummyPopup.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
function dispatchItem (item: ObjectSearchResult): void {
|
function dispatchItem (item: ObjectSearchResult): void {
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { popupstore as modal } from '../popups'
|
import { popupstore as modal } from '../popups'
|
||||||
import PopupInstance from './PopupInstance.svelte'
|
import PopupInstance from './PopupInstance.svelte'
|
||||||
|
|
||||||
|
export let contentPanel: HTMLElement
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $modal.length > 0}
|
{#if $modal.length > 0}
|
||||||
@ -31,6 +33,7 @@
|
|||||||
zIndex={(i + 1) * 500}
|
zIndex={(i + 1) * 500}
|
||||||
top={$modal.length - 1 === i}
|
top={$modal.length - 1 === i}
|
||||||
close={popup.close}
|
close={popup.close}
|
||||||
|
{contentPanel}
|
||||||
overlay={popup.options.overlay}
|
overlay={popup.options.overlay}
|
||||||
/>
|
/>
|
||||||
{/key}
|
{/key}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
export let zIndex: number
|
export let zIndex: number
|
||||||
export let top: boolean
|
export let top: boolean
|
||||||
export let close: () => void
|
export let close: () => void
|
||||||
|
export let contentPanel: HTMLElement
|
||||||
|
|
||||||
let modalHTML: HTMLElement
|
let modalHTML: HTMLElement
|
||||||
let componentInstance: any
|
let componentInstance: any
|
||||||
@ -67,13 +68,13 @@
|
|||||||
_close(undefined)
|
_close(undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fitPopup = (modalHTML: HTMLElement, element: PopupAlignment | undefined): void => {
|
const fitPopup = (modalHTML: HTMLElement, element: PopupAlignment | undefined, contentPanel: HTMLElement): void => {
|
||||||
if ((fullSize || docSize) && (element === 'float' || element === 'centered')) {
|
if ((fullSize || docSize) && (element === 'float' || element === 'centered')) {
|
||||||
options = fitPopupElement(modalHTML, 'full')
|
options = fitPopupElement(modalHTML, 'full', contentPanel)
|
||||||
options.props.maxHeight = '100vh'
|
options.props.maxHeight = '100vh'
|
||||||
if (!modalHTML.classList.contains('fullsize')) modalHTML.classList.add('fullsize')
|
if (!modalHTML.classList.contains('fullsize')) modalHTML.classList.add('fullsize')
|
||||||
} else {
|
} else {
|
||||||
options = fitPopupElement(modalHTML, element)
|
options = fitPopupElement(modalHTML, element, contentPanel)
|
||||||
if (modalHTML.classList.contains('fullsize')) modalHTML.classList.remove('fullsize')
|
if (modalHTML.classList.contains('fullsize')) modalHTML.classList.remove('fullsize')
|
||||||
}
|
}
|
||||||
options.fullSize = fullSize
|
options.fullSize = fullSize
|
||||||
@ -103,7 +104,7 @@
|
|||||||
|
|
||||||
$: if (modalHTML !== undefined && oldModalHTML !== modalHTML) {
|
$: if (modalHTML !== undefined && oldModalHTML !== modalHTML) {
|
||||||
oldModalHTML = modalHTML
|
oldModalHTML = modalHTML
|
||||||
fitPopup(modalHTML, element)
|
fitPopup(modalHTML, element, contentPanel)
|
||||||
showing = true
|
showing = true
|
||||||
modalHTML.addEventListener(
|
modalHTML.addEventListener(
|
||||||
'transitionend',
|
'transitionend',
|
||||||
@ -121,7 +122,7 @@
|
|||||||
<svelte:window
|
<svelte:window
|
||||||
on:resize={() => {
|
on:resize={() => {
|
||||||
if (modalHTML) {
|
if (modalHTML) {
|
||||||
fitPopup(modalHTML, element)
|
fitPopup(modalHTML, element, contentPanel)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
on:keydown={handleKeydown}
|
on:keydown={handleKeydown}
|
||||||
@ -155,10 +156,10 @@
|
|||||||
on:close={(ev) => _close(ev?.detail)}
|
on:close={(ev) => _close(ev?.detail)}
|
||||||
on:fullsize={() => {
|
on:fullsize={() => {
|
||||||
fullSize = !fullSize
|
fullSize = !fullSize
|
||||||
fitPopup(modalHTML, element)
|
fitPopup(modalHTML, element, contentPanel)
|
||||||
}}
|
}}
|
||||||
on:changeContent={() => {
|
on:changeContent={() => {
|
||||||
fitPopup(modalHTML, element)
|
fitPopup(modalHTML, element, contentPanel)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
import { writable, derived } from 'svelte/store'
|
import { writable, derived } from 'svelte/store'
|
||||||
import { Location as PlatformLocation } from './types'
|
import { Location as PlatformLocation } from './types'
|
||||||
|
import { closePopup } from './popups'
|
||||||
|
|
||||||
export function locationToUrl (location: PlatformLocation): string {
|
export function locationToUrl (location: PlatformLocation): string {
|
||||||
let result = '/'
|
let result = '/'
|
||||||
@ -110,6 +111,7 @@ window.addEventListener('popstate', () => {
|
|||||||
export const location = derived(locationWritable, (loc) => loc)
|
export const location = derived(locationWritable, (loc) => loc)
|
||||||
|
|
||||||
export function navigate (location: PlatformLocation, store = true): void {
|
export function navigate (location: PlatformLocation, store = true): void {
|
||||||
|
closePopup()
|
||||||
const url = locationToUrl(location)
|
const url = locationToUrl(location)
|
||||||
if (locationToUrl(getCurrentLocation()) !== url) {
|
if (locationToUrl(getCurrentLocation()) !== url) {
|
||||||
if (store) {
|
if (store) {
|
||||||
|
@ -12,7 +12,7 @@ import type {
|
|||||||
} from './types'
|
} from './types'
|
||||||
import { ComponentType } from 'svelte'
|
import { ComponentType } from 'svelte'
|
||||||
|
|
||||||
interface CompAndProps {
|
export interface CompAndProps {
|
||||||
id: string
|
id: string
|
||||||
is: AnySvelteComponent | ComponentType
|
is: AnySvelteComponent | ComponentType
|
||||||
props: any
|
props: any
|
||||||
@ -26,6 +26,11 @@ interface CompAndProps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PopupResult {
|
||||||
|
id: string
|
||||||
|
close: () => void
|
||||||
|
}
|
||||||
|
|
||||||
export const popupstore = writable<CompAndProps[]>([])
|
export const popupstore = writable<CompAndProps[]>([])
|
||||||
|
|
||||||
function addPopup (props: CompAndProps): void {
|
function addPopup (props: CompAndProps): void {
|
||||||
@ -45,7 +50,7 @@ export function showPopup (
|
|||||||
category: string
|
category: string
|
||||||
overlay: boolean
|
overlay: boolean
|
||||||
} = { category: 'popup', overlay: true }
|
} = { category: 'popup', overlay: true }
|
||||||
): () => void {
|
): PopupResult {
|
||||||
const id = `${popupId++}`
|
const id = `${popupId++}`
|
||||||
const closePopupOp = (): void => {
|
const closePopupOp = (): void => {
|
||||||
popupstore.update((popups) => {
|
popupstore.update((popups) => {
|
||||||
@ -66,7 +71,10 @@ export function showPopup (
|
|||||||
} else {
|
} else {
|
||||||
addPopup({ id, is: component, props, element: _element, onClose, onUpdate, close: closePopupOp, options })
|
addPopup({ id, is: component, props, element: _element, onClose, onUpdate, close: closePopupOp, options })
|
||||||
}
|
}
|
||||||
return closePopupOp
|
return {
|
||||||
|
id,
|
||||||
|
close: closePopupOp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function closePopup (category?: string): void {
|
export function closePopup (category?: string): void {
|
||||||
|
@ -71,21 +71,21 @@
|
|||||||
return new Promise<'single' | 'multiple' | 'close'>((resolve) => {
|
return new Promise<'single' | 'multiple' | 'close'>((resolve) => {
|
||||||
const popupOpts = {
|
const popupOpts = {
|
||||||
onAddSingle: () => {
|
onAddSingle: () => {
|
||||||
closePopup()
|
popup.close()
|
||||||
resolve('single')
|
resolve('single')
|
||||||
},
|
},
|
||||||
onAddMultiple: () => {
|
onAddMultiple: () => {
|
||||||
closePopup()
|
popup.close()
|
||||||
resolve('multiple')
|
resolve('multiple')
|
||||||
},
|
},
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
closePopup()
|
popup.close()
|
||||||
resolve('close')
|
resolve('close')
|
||||||
},
|
},
|
||||||
cardsNumber: splittedTitle.length
|
cardsNumber: splittedTitle.length
|
||||||
}
|
}
|
||||||
|
|
||||||
const closePopup = showPopup(AddMultipleCardsPopup, popupOpts, anchorRef, () => resolve('close'))
|
const popup = showPopup(AddMultipleCardsPopup, popupOpts, anchorRef, () => resolve('close'))
|
||||||
}).then((value) => {
|
}).then((value) => {
|
||||||
if (value === 'single' || value === 'close') {
|
if (value === 'single' || value === 'close') {
|
||||||
return addCard(title.replace('\n', ' ')).then((res) => {
|
return addCard(title.replace('\n', ' ')).then((res) => {
|
||||||
|
@ -96,10 +96,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if loading}
|
{#if component && _id && _class}
|
||||||
<Loading />
|
|
||||||
{:else if component && _id && _class}
|
|
||||||
<Component is={component} props={{ embedded: true, _id, _class }} />
|
<Component is={component} props={{ embedded: true, _id, _class }} />
|
||||||
|
{:else}
|
||||||
|
<div class="antiPanel-component filled w-full">
|
||||||
|
<Loading />
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -83,6 +83,10 @@
|
|||||||
_class,
|
_class,
|
||||||
{ _id },
|
{ _id },
|
||||||
async (result) => {
|
async (result) => {
|
||||||
|
if (saveTrigger !== undefined) {
|
||||||
|
clearTimeout(saveTrigger)
|
||||||
|
await save()
|
||||||
|
}
|
||||||
;[issue] = result
|
;[issue] = result
|
||||||
title = issue.title
|
title = issue.title
|
||||||
description = issue.description
|
description = issue.description
|
||||||
@ -177,7 +181,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<EditBox bind:value={title} placeholder={tracker.string.IssueTitlePlaceholder} kind="large-style" on:blur={save} />
|
<EditBox bind:value={title} placeholder={tracker.string.IssueTitlePlaceholder} kind="large-style" on:blur={save} />
|
||||||
<div class="w-full mt-6">
|
<div class="w-full mt-6">
|
||||||
{#key _id}
|
{#key issue._id}
|
||||||
<AttachmentStyledBox
|
<AttachmentStyledBox
|
||||||
bind:this={descriptionBox}
|
bind:this={descriptionBox}
|
||||||
useAttachmentPreview={true}
|
useAttachmentPreview={true}
|
||||||
|
@ -47,7 +47,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let selectedId: Ref<FilteredView> | undefined = undefined
|
let selectedId: Ref<FilteredView> | undefined = undefined
|
||||||
let selectedFW: FilteredView[] | undefined = undefined
|
|
||||||
async function load (fv: FilteredView): Promise<void> {
|
async function load (fv: FilteredView): Promise<void> {
|
||||||
if (fv.viewletId !== undefined && fv.viewletId !== null) {
|
if (fv.viewletId !== undefined && fv.viewletId !== null) {
|
||||||
const viewlet = await client.findOne(view.class.Viewlet, { _id: fv.viewletId })
|
const viewlet = await client.findOne(view.class.Viewlet, { _id: fv.viewletId })
|
||||||
@ -66,7 +65,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const clearSelection = () => {
|
const clearSelection = () => {
|
||||||
selectedId = selectedFW = undefined
|
selectedId = undefined
|
||||||
dispatch('select', false)
|
dispatch('select', false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
import request, { RequestStatus } from '@hcengineering/request'
|
import request, { RequestStatus } from '@hcengineering/request'
|
||||||
import {
|
import {
|
||||||
AnyComponent,
|
AnyComponent,
|
||||||
|
CompAndProps,
|
||||||
Component,
|
Component,
|
||||||
DatePickerPopup,
|
DatePickerPopup,
|
||||||
Label,
|
Label,
|
||||||
@ -33,6 +34,7 @@
|
|||||||
Popup,
|
Popup,
|
||||||
PopupAlignment,
|
PopupAlignment,
|
||||||
PopupPosAlignment,
|
PopupPosAlignment,
|
||||||
|
PopupResult,
|
||||||
ResolvedLocation,
|
ResolvedLocation,
|
||||||
TooltipInstance,
|
TooltipInstance,
|
||||||
areLocationsEqual,
|
areLocationsEqual,
|
||||||
@ -43,6 +45,7 @@
|
|||||||
location,
|
location,
|
||||||
navigate,
|
navigate,
|
||||||
openPanel,
|
openPanel,
|
||||||
|
popupstore,
|
||||||
resizeObserver,
|
resizeObserver,
|
||||||
showPopup
|
showPopup
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
@ -500,7 +503,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let prevLoc: Location | undefined = undefined
|
function checkInbox (popups: CompAndProps[]) {
|
||||||
|
if (inboxPopup !== undefined) {
|
||||||
|
const exists = popups.find((p) => p.id === inboxPopup?.id)
|
||||||
|
if (!exists) {
|
||||||
|
inboxPopup = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: checkInbox($popupstore)
|
||||||
|
|
||||||
|
let inboxPopup: PopupResult | undefined = undefined
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if employee?.active === true}
|
{#if employee?.active === true}
|
||||||
@ -568,17 +582,26 @@
|
|||||||
<AppItem
|
<AppItem
|
||||||
icon={notification.icon.Notifications}
|
icon={notification.icon.Notifications}
|
||||||
label={notification.string.Inbox}
|
label={notification.string.Inbox}
|
||||||
selected={currentAppAlias === notificationId}
|
selected={currentAppAlias === notificationId || inboxPopup !== undefined}
|
||||||
on:click={(e) => {
|
on:click={(e) => {
|
||||||
if (currentAppAlias === notificationId) {
|
if (e.metaKey || e.ctrlKey) return
|
||||||
e.preventDefault()
|
if (inboxPopup) {
|
||||||
e.stopPropagation()
|
inboxPopup.close()
|
||||||
if (prevLoc !== undefined) {
|
|
||||||
navigate(prevLoc)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
prevLoc = $location
|
inboxPopup = showPopup(
|
||||||
|
notification.component.Inbox,
|
||||||
|
{ visibileNav: true },
|
||||||
|
'content',
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
category: 'popup',
|
||||||
|
overlay: false
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
}}
|
}}
|
||||||
notify={hasNotification}
|
notify={hasNotification}
|
||||||
/>
|
/>
|
||||||
@ -672,7 +695,7 @@
|
|||||||
<ActionContext context={{ mode: 'panel' }} />
|
<ActionContext context={{ mode: 'panel' }} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</PanelInstance>
|
</PanelInstance>
|
||||||
<Popup>
|
<Popup {contentPanel}>
|
||||||
<svelte:fragment slot="popup-header">
|
<svelte:fragment slot="popup-header">
|
||||||
<ActionContext context={{ mode: 'popup' }} />
|
<ActionContext context={{ mode: 'popup' }} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
Loading…
Reference in New Issue
Block a user