Inbox as popup (#2957)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-04-12 14:20:01 +06:00 committed by GitHub
parent 3c0fdc9698
commit 981cbada6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 76 additions and 34 deletions

View File

@ -15,7 +15,7 @@
-->
<script lang="ts">
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 DummyPopup from './DummyPopup.svelte'
@ -25,10 +25,10 @@
export let close: () => void
let popup: HTMLDivElement
let popupClose: () => void
let dummyPopup: PopupResult
onMount(() => {
popupClose = showPopup(
dummyPopup = showPopup(
DummyPopup,
{},
undefined,
@ -39,7 +39,7 @@
})
onDestroy(() => {
popupClose()
dummyPopup.close()
})
function dispatchItem (item: ObjectSearchResult): void {

View File

@ -15,6 +15,8 @@
<script lang="ts">
import { popupstore as modal } from '../popups'
import PopupInstance from './PopupInstance.svelte'
export let contentPanel: HTMLElement
</script>
{#if $modal.length > 0}
@ -31,6 +33,7 @@
zIndex={(i + 1) * 500}
top={$modal.length - 1 === i}
close={popup.close}
{contentPanel}
overlay={popup.options.overlay}
/>
{/key}

View File

@ -27,6 +27,7 @@
export let zIndex: number
export let top: boolean
export let close: () => void
export let contentPanel: HTMLElement
let modalHTML: HTMLElement
let componentInstance: any
@ -67,13 +68,13 @@
_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')) {
options = fitPopupElement(modalHTML, 'full')
options = fitPopupElement(modalHTML, 'full', contentPanel)
options.props.maxHeight = '100vh'
if (!modalHTML.classList.contains('fullsize')) modalHTML.classList.add('fullsize')
} else {
options = fitPopupElement(modalHTML, element)
options = fitPopupElement(modalHTML, element, contentPanel)
if (modalHTML.classList.contains('fullsize')) modalHTML.classList.remove('fullsize')
}
options.fullSize = fullSize
@ -103,7 +104,7 @@
$: if (modalHTML !== undefined && oldModalHTML !== modalHTML) {
oldModalHTML = modalHTML
fitPopup(modalHTML, element)
fitPopup(modalHTML, element, contentPanel)
showing = true
modalHTML.addEventListener(
'transitionend',
@ -121,7 +122,7 @@
<svelte:window
on:resize={() => {
if (modalHTML) {
fitPopup(modalHTML, element)
fitPopup(modalHTML, element, contentPanel)
}
}}
on:keydown={handleKeydown}
@ -155,10 +156,10 @@
on:close={(ev) => _close(ev?.detail)}
on:fullsize={() => {
fullSize = !fullSize
fitPopup(modalHTML, element)
fitPopup(modalHTML, element, contentPanel)
}}
on:changeContent={() => {
fitPopup(modalHTML, element)
fitPopup(modalHTML, element, contentPanel)
}}
/>
</div>

View File

@ -15,6 +15,7 @@
import { writable, derived } from 'svelte/store'
import { Location as PlatformLocation } from './types'
import { closePopup } from './popups'
export function locationToUrl (location: PlatformLocation): string {
let result = '/'
@ -110,6 +111,7 @@ window.addEventListener('popstate', () => {
export const location = derived(locationWritable, (loc) => loc)
export function navigate (location: PlatformLocation, store = true): void {
closePopup()
const url = locationToUrl(location)
if (locationToUrl(getCurrentLocation()) !== url) {
if (store) {

View File

@ -12,7 +12,7 @@ import type {
} from './types'
import { ComponentType } from 'svelte'
interface CompAndProps {
export interface CompAndProps {
id: string
is: AnySvelteComponent | ComponentType
props: any
@ -26,6 +26,11 @@ interface CompAndProps {
}
}
export interface PopupResult {
id: string
close: () => void
}
export const popupstore = writable<CompAndProps[]>([])
function addPopup (props: CompAndProps): void {
@ -45,7 +50,7 @@ export function showPopup (
category: string
overlay: boolean
} = { category: 'popup', overlay: true }
): () => void {
): PopupResult {
const id = `${popupId++}`
const closePopupOp = (): void => {
popupstore.update((popups) => {
@ -66,7 +71,10 @@ export function showPopup (
} else {
addPopup({ id, is: component, props, element: _element, onClose, onUpdate, close: closePopupOp, options })
}
return closePopupOp
return {
id,
close: closePopupOp
}
}
export function closePopup (category?: string): void {

View File

@ -71,21 +71,21 @@
return new Promise<'single' | 'multiple' | 'close'>((resolve) => {
const popupOpts = {
onAddSingle: () => {
closePopup()
popup.close()
resolve('single')
},
onAddMultiple: () => {
closePopup()
popup.close()
resolve('multiple')
},
onClose: () => {
closePopup()
popup.close()
resolve('close')
},
cardsNumber: splittedTitle.length
}
const closePopup = showPopup(AddMultipleCardsPopup, popupOpts, anchorRef, () => resolve('close'))
const popup = showPopup(AddMultipleCardsPopup, popupOpts, anchorRef, () => resolve('close'))
}).then((value) => {
if (value === 'single' || value === 'close') {
return addCard(title.replace('\n', ' ')).then((res) => {

View File

@ -96,10 +96,12 @@
</div>
</div>
{/if}
{#if loading}
<Loading />
{:else if component && _id && _class}
{#if component && _id && _class}
<Component is={component} props={{ embedded: true, _id, _class }} />
{:else}
<div class="antiPanel-component filled w-full">
<Loading />
</div>
{/if}
</div>

View File

@ -83,6 +83,10 @@
_class,
{ _id },
async (result) => {
if (saveTrigger !== undefined) {
clearTimeout(saveTrigger)
await save()
}
;[issue] = result
title = issue.title
description = issue.description
@ -177,7 +181,7 @@
{/if}
<EditBox bind:value={title} placeholder={tracker.string.IssueTitlePlaceholder} kind="large-style" on:blur={save} />
<div class="w-full mt-6">
{#key _id}
{#key issue._id}
<AttachmentStyledBox
bind:this={descriptionBox}
useAttachmentPreview={true}

View File

@ -47,7 +47,6 @@
}
let selectedId: Ref<FilteredView> | undefined = undefined
let selectedFW: FilteredView[] | undefined = undefined
async function load (fv: FilteredView): Promise<void> {
if (fv.viewletId !== undefined && fv.viewletId !== null) {
const viewlet = await client.findOne(view.class.Viewlet, { _id: fv.viewletId })
@ -66,7 +65,7 @@
}
const clearSelection = () => {
selectedId = selectedFW = undefined
selectedId = undefined
dispatch('select', false)
}

View File

@ -24,6 +24,7 @@
import request, { RequestStatus } from '@hcengineering/request'
import {
AnyComponent,
CompAndProps,
Component,
DatePickerPopup,
Label,
@ -33,6 +34,7 @@
Popup,
PopupAlignment,
PopupPosAlignment,
PopupResult,
ResolvedLocation,
TooltipInstance,
areLocationsEqual,
@ -43,6 +45,7 @@
location,
navigate,
openPanel,
popupstore,
resizeObserver,
showPopup
} 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>
{#if employee?.active === true}
@ -568,17 +582,26 @@
<AppItem
icon={notification.icon.Notifications}
label={notification.string.Inbox}
selected={currentAppAlias === notificationId}
selected={currentAppAlias === notificationId || inboxPopup !== undefined}
on:click={(e) => {
if (currentAppAlias === notificationId) {
e.preventDefault()
e.stopPropagation()
if (prevLoc !== undefined) {
navigate(prevLoc)
}
if (e.metaKey || e.ctrlKey) return
if (inboxPopup) {
inboxPopup.close()
} else {
prevLoc = $location
inboxPopup = showPopup(
notification.component.Inbox,
{ visibileNav: true },
'content',
undefined,
undefined,
{
category: 'popup',
overlay: false
}
)
}
e.preventDefault()
e.stopPropagation()
}}
notify={hasNotification}
/>
@ -672,7 +695,7 @@
<ActionContext context={{ mode: 'panel' }} />
</svelte:fragment>
</PanelInstance>
<Popup>
<Popup {contentPanel}>
<svelte:fragment slot="popup-header">
<ActionContext context={{ mode: 'popup' }} />
</svelte:fragment>