From 981cbada6ac242587cda771b8dc0f18fc353688d Mon Sep 17 00:00:00 2001 From: Denis Bykhov <bykhov.denis@gmail.com> Date: Wed, 12 Apr 2023 14:20:01 +0600 Subject: [PATCH] Inbox as popup (#2957) Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com> --- .../src/components/MentionList.svelte | 8 ++-- packages/ui/src/components/Popup.svelte | 3 ++ .../ui/src/components/PopupInstance.svelte | 15 ++++--- packages/ui/src/location.ts | 2 + packages/ui/src/popups.ts | 14 ++++-- .../src/components/add-card/AddCard.svelte | 8 ++-- .../src/components/Inbox.svelte | 8 ++-- .../components/issues/edit/EditIssue.svelte | 6 ++- .../src/components/SavedView.svelte | 3 +- .../src/components/Workbench.svelte | 43 ++++++++++++++----- 10 files changed, 76 insertions(+), 34 deletions(-) diff --git a/packages/text-editor/src/components/MentionList.svelte b/packages/text-editor/src/components/MentionList.svelte index e2dfd06bd0..b5908641c9 100644 --- a/packages/text-editor/src/components/MentionList.svelte +++ b/packages/text-editor/src/components/MentionList.svelte @@ -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 { diff --git a/packages/ui/src/components/Popup.svelte b/packages/ui/src/components/Popup.svelte index 166a06665a..7e7aa42952 100644 --- a/packages/ui/src/components/Popup.svelte +++ b/packages/ui/src/components/Popup.svelte @@ -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} diff --git a/packages/ui/src/components/PopupInstance.svelte b/packages/ui/src/components/PopupInstance.svelte index fea646f88c..b40b5e4aa9 100644 --- a/packages/ui/src/components/PopupInstance.svelte +++ b/packages/ui/src/components/PopupInstance.svelte @@ -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> diff --git a/packages/ui/src/location.ts b/packages/ui/src/location.ts index c893be3407..172e167df9 100644 --- a/packages/ui/src/location.ts +++ b/packages/ui/src/location.ts @@ -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) { diff --git a/packages/ui/src/popups.ts b/packages/ui/src/popups.ts index 2853ebdb7e..7433b58c96 100644 --- a/packages/ui/src/popups.ts +++ b/packages/ui/src/popups.ts @@ -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 { diff --git a/plugins/board-resources/src/components/add-card/AddCard.svelte b/plugins/board-resources/src/components/add-card/AddCard.svelte index 148578542a..67cb4d9554 100644 --- a/plugins/board-resources/src/components/add-card/AddCard.svelte +++ b/plugins/board-resources/src/components/add-card/AddCard.svelte @@ -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) => { diff --git a/plugins/notification-resources/src/components/Inbox.svelte b/plugins/notification-resources/src/components/Inbox.svelte index 0a13538bcc..e513ef8245 100644 --- a/plugins/notification-resources/src/components/Inbox.svelte +++ b/plugins/notification-resources/src/components/Inbox.svelte @@ -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> diff --git a/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte b/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte index bbef924c82..786c365cd6 100644 --- a/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte +++ b/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte @@ -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} diff --git a/plugins/workbench-resources/src/components/SavedView.svelte b/plugins/workbench-resources/src/components/SavedView.svelte index 997c2c09f4..89d3a176fa 100644 --- a/plugins/workbench-resources/src/components/SavedView.svelte +++ b/plugins/workbench-resources/src/components/SavedView.svelte @@ -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) } diff --git a/plugins/workbench-resources/src/components/Workbench.svelte b/plugins/workbench-resources/src/components/Workbench.svelte index 6a5a0455ff..f42cae13ae 100644 --- a/plugins/workbench-resources/src/components/Workbench.svelte +++ b/plugins/workbench-resources/src/components/Workbench.svelte @@ -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>