From f57711e9e9ee79f1cd9db3288b2ff6b62a2a8d0c Mon Sep 17 00:00:00 2001
From: Alexander Platov <sas_lord@mail.ru>
Date: Wed, 1 Sep 2021 19:31:05 +0300
Subject: [PATCH] Replace Modal with Popup (#119)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
---
 .../ui/src/components/PopupInstance.svelte    | 37 +++++----
 packages/ui/src/index.ts                      | 14 +---
 packages/ui/src/types.ts                      |  2 +
 .../src/components/DialogHeader.svelte        |  4 +-
 .../src/components/EditCandidate.svelte       |  2 +-
 .../src/components/TableView.svelte           |  2 +-
 .../src/components/Modal.svelte               | 83 -------------------
 .../src/components/Workbench.svelte           |  2 -
 .../src/components/navigator/SpacesNav.svelte |  4 +-
 9 files changed, 32 insertions(+), 118 deletions(-)
 delete mode 100644 plugins/workbench-resources/src/components/Modal.svelte

diff --git a/packages/ui/src/components/PopupInstance.svelte b/packages/ui/src/components/PopupInstance.svelte
index 13ae9a3ec3..f919cfac0b 100644
--- a/packages/ui/src/components/PopupInstance.svelte
+++ b/packages/ui/src/components/PopupInstance.svelte
@@ -16,12 +16,12 @@
 
 <script lang="ts">
 import Component from './Component.svelte'
-import type { AnySvelteComponent, AnyComponent } from '../types'
+import type { AnySvelteComponent, AnyComponent, PopupAlignment } from '../types'
 import { closePopup } from '..'
 
 export let is: AnyComponent | AnySvelteComponent
 export let props: object
-export let element: HTMLElement | undefined
+export let element: PopupAlignment | undefined
 export let onClose: (result: any) => void | undefined
 export let zIndex: number
 
@@ -37,27 +37,32 @@ function close(ev: CustomEvent) {
 $: {
   if (modalHTML) {
     if (element) {
-      const rect = element.getBoundingClientRect()
-      if (rect.top > document.body.clientHeight - rect.bottom) {
-        modalHTML.style.bottom = `calc(${document.body.clientHeight - rect.top}px + .75rem)`
-      } else {
-        modalHTML.style.top = `calc(${rect.bottom}px + .75rem)`
-      }
-      if (rect.left > document.body.clientWidth - rect.right) {
-        modalHTML.style.right = document.body.clientWidth - rect.right + 'px'
-      } else {
-        modalHTML.style.left = rect.left + 'px'
+      if (typeof element !== 'string') {
+        const rect = element.getBoundingClientRect()
+        if (rect.top > document.body.clientHeight - rect.bottom) {
+          modalHTML.style.bottom = `calc(${document.body.clientHeight - rect.top}px + .75rem)`
+        } else {
+          modalHTML.style.top = `calc(${rect.bottom}px + .75rem)`
+        }
+        if (rect.left > document.body.clientWidth - rect.right) {
+          modalHTML.style.right = document.body.clientWidth - rect.right + 'px'
+        } else {
+          modalHTML.style.left = rect.left + 'px'
+        }
+      } else if (element === 'right') {
+        modalHTML.style.top = '4rem'
+        modalHTML.style.bottom = '4rem'
+        modalHTML.style.right = '4rem'
       }
     } else {
-      modalHTML.style.top = '4rem'
-      modalHTML.style.bottom = '4rem'
-      modalHTML.style.right = '4rem'
+      modalHTML.style.top = '50%'
+      modalHTML.style.left = '50%'
+      modalHTML.style.transform = 'translate(-50%, -50%)'
     }
   }
 }
 </script>
 
-
 <div class="popup" bind:this={modalHTML} style={`z-index: ${zIndex + 1};`}>
   {#if typeof(is) === 'string'}
     <Component is={is} props={props} on:close={close}/>
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
index bbd2c405a3..31029db1d7 100644
--- a/packages/ui/src/index.ts
+++ b/packages/ui/src/index.ts
@@ -61,7 +61,7 @@ export { default as IconComments } from './components/icons/Comments.svelte'
 
 export * from './utils'
 
-import type { AnySvelteComponent, AnyComponent } from './types'
+import type { AnySvelteComponent, AnyComponent, PopupAlignment } from './types'
 import { writable } from 'svelte/store'
 
 export function createApp (target: HTMLElement): SvelteComponent {
@@ -71,7 +71,7 @@ export function createApp (target: HTMLElement): SvelteComponent {
 interface CompAndProps {
   is: AnySvelteComponent | AnyComponent | undefined
   props: any
-  element?: HTMLElement
+  element?: PopupAlignment
   onClose?: (result: any) => void
 }
 
@@ -80,17 +80,9 @@ export const store = writable<CompAndProps>({
   props: {},
 })
 
-export function showModal (component: AnySvelteComponent | AnyComponent, props: any, element?: HTMLElement): void {
-  store.set({ is: component, props, element: element })
-}
-
-export function closeModal (): void {
-  store.set({ is: undefined, props: {}, element: undefined })
-}
-
 export const popupstore = writable<CompAndProps[]>([])
 
-export function showPopup (component: AnySvelteComponent | AnyComponent, props: any, element?: HTMLElement, onClose?: (result: any) => void): void {
+export function showPopup (component: AnySvelteComponent | AnyComponent, props: any, element?: PopupAlignment, onClose?: (result: any) => void): void {
   popupstore.update(popups => {
     popups.push({ is: component, props, element, onClose })
     return popups
diff --git a/packages/ui/src/types.ts b/packages/ui/src/types.ts
index b785966c46..20dd740b20 100644
--- a/packages/ui/src/types.ts
+++ b/packages/ui/src/types.ts
@@ -53,3 +53,5 @@ export interface Tab {
 }
 
 export type TabModel = Tab[]
+
+export type PopupAlignment = HTMLElement | 'right'
diff --git a/plugins/recruit-resources/src/components/DialogHeader.svelte b/plugins/recruit-resources/src/components/DialogHeader.svelte
index 710a06d1bc..07708ce5e6 100644
--- a/plugins/recruit-resources/src/components/DialogHeader.svelte
+++ b/plugins/recruit-resources/src/components/DialogHeader.svelte
@@ -19,7 +19,7 @@
   
   import type { Ref, Space, Doc } from '@anticrm/core'
   import { generateId } from '@anticrm/core'
-  import { EditBox, Button, CircleButton, Grid, Label, showModal, Link, showPopup } from '@anticrm/ui'
+  import { EditBox, Button, CircleButton, Grid, Label, Link, showPopup } from '@anticrm/ui'
   import type { AnyComponent } from '@anticrm/ui'
   import { getClient } from '@anticrm/presentation'
 
@@ -117,7 +117,7 @@
     on:dragleave={ () => { dragover = false } }
     on:drop|preventDefault|stopPropagation={drop}>
   <div class="flex-row-center main-content">
-    <div class="avatar" on:click|stopPropagation={() => showModal(AvatarEditor, { label: 'Profile photo' })}><User /></div>
+    <div class="avatar" on:click|stopPropagation={() => showPopup(AvatarEditor, { label: 'Profile photo' })}><User /></div>
     <div class="flex-col">
       <div class="name">
         <EditBox placeholder="John" bind:value={newValue.firstName} on:input={isChanged} focus={create}/>
diff --git a/plugins/recruit-resources/src/components/EditCandidate.svelte b/plugins/recruit-resources/src/components/EditCandidate.svelte
index 6d007c8a8e..391fbb4a28 100644
--- a/plugins/recruit-resources/src/components/EditCandidate.svelte
+++ b/plugins/recruit-resources/src/components/EditCandidate.svelte
@@ -16,7 +16,7 @@
 <script lang="ts">
   import { createEventDispatcher } from 'svelte'
   import type { Ref, Space, Doc } from '@anticrm/core'
-  import { Dialog, Tabs } from '@anticrm/ui'
+  import { Tabs } from '@anticrm/ui'
   import { getClient } from '@anticrm/presentation'
   import type { Candidate } from '@anticrm/recruit'
   import DialogHeader from './DialogHeader.svelte'
diff --git a/plugins/view-resources/src/components/TableView.svelte b/plugins/view-resources/src/components/TableView.svelte
index bbe8db5f6b..a962826127 100644
--- a/plugins/view-resources/src/components/TableView.svelte
+++ b/plugins/view-resources/src/components/TableView.svelte
@@ -50,7 +50,7 @@
   const client = getClient()
 
   function onClick(object: Doc) {
-    showPopup(open, { object, space })
+    showPopup(open, { object, space }, 'right')
   }
 </script>
 
diff --git a/plugins/workbench-resources/src/components/Modal.svelte b/plugins/workbench-resources/src/components/Modal.svelte
deleted file mode 100644
index 5c57403f55..0000000000
--- a/plugins/workbench-resources/src/components/Modal.svelte
+++ /dev/null
@@ -1,83 +0,0 @@
-<!--
-// Copyright © 2020 Anticrm Platform Contributors.
-//
-// 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 { store as modal } from '@anticrm/ui'
-  import { Component } from '@anticrm/ui'
-
-  let modalHTML: HTMLElement
-  let modalOHTML: HTMLElement
-
-  function close () {
-    modalHTML.style.animationDirection = modalOHTML.style.animationDirection = 'reverse'
-    modalHTML.style.animationDuration = modalOHTML.style.animationDuration = '.2s'
-    modal.set({ is: undefined, props: {}, element: undefined })
-  }
-
-  function handleKeydown (ev: KeyboardEvent) {
-    if (ev.key === 'Escape' && $modal.is) {
-      close()
-    }
-  }
-  
-  function getStyle (element: HTMLElement | undefined) {
-    if (element) {
-      const rect = element.getBoundingClientRect()
-      return `top: ${rect.top + rect.height + 2}px; left: ${rect.left}px;`
-    } else {
-      return 'top: 50%; left: 50%; transform: translate(-50%, -50%);'
-    }
-  }
-</script>
-
-<svelte:window on:keydown={handleKeydown} />
-
-{#if $modal.is}
-  <div class="modal" class:top-arrow={$modal.element} bind:this={modalHTML} style={getStyle($modal.element)}>
-    {#if typeof($modal.is) === 'string'}
-      <Component is={$modal.is} props={$modal.props} on:close={close}/>
-    {:else}
-      <svelte:component this={$modal.is} {...$modal.props} on:close={close} />
-    {/if}
-  </div>
-  <div bind:this={modalOHTML} class="modal-overlay" />
-{/if}
-
-<style lang="scss">
-  @keyframes show {
-    from { opacity: 0; filter: blur(3px); }
-    99% { opacity: 1; filter: blur(0px); }
-    to { filter: none; }
-  }
-  @keyframes showOverlay {
-    from { backdrop-filter: blur(0px); }
-    to { backdrop-filter: blur(1px); }
-  }
-  .modal {
-    position: fixed;
-    background: transparent;
-    z-index: 1001;
-    animation: show .2s ease-in-out forwards;
-  }
-  .modal-overlay {
-    z-index: 1000;
-    background: rgba(0, 0, 0, .5);
-    position: fixed;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    animation: showOverlay .2s ease-in-out forwards;
-  }
-</style>
\ No newline at end of file
diff --git a/plugins/workbench-resources/src/components/Workbench.svelte b/plugins/workbench-resources/src/components/Workbench.svelte
index 1fda75cd60..99cf468c30 100644
--- a/plugins/workbench-resources/src/components/Workbench.svelte
+++ b/plugins/workbench-resources/src/components/Workbench.svelte
@@ -26,7 +26,6 @@
   import workbench from '@anticrm/workbench'
 
   import Navigator from './Navigator.svelte'
-  import Modal from './Modal.svelte'
   import SpaceHeader from './SpaceHeader.svelte'
   import SpaceView from './SpaceView.svelte'
   
@@ -92,7 +91,6 @@
     </div>
     <!-- <div class="aside"><Chat thread/></div> -->
   </div>
-  <Modal />
   <Popup />
 {:else}
   No client
diff --git a/plugins/workbench-resources/src/components/navigator/SpacesNav.svelte b/plugins/workbench-resources/src/components/navigator/SpacesNav.svelte
index 05091c969b..94c289fa6c 100644
--- a/plugins/workbench-resources/src/components/navigator/SpacesNav.svelte
+++ b/plugins/workbench-resources/src/components/navigator/SpacesNav.svelte
@@ -23,7 +23,7 @@
 
   import { IconAdd } from '@anticrm/ui'
   import { getClient, createQuery } from '@anticrm/presentation'
-  import { showModal } from '@anticrm/ui'
+  import { showPopup } from '@anticrm/ui'
 
   import { classIcon } from '../../utils'
 
@@ -42,7 +42,7 @@
     label: model.addSpaceLabel,
     icon: IconAdd,
     action: async (): Promise<void> => {
-      showModal(model.createComponent, {})
+      showPopup(model.createComponent, {})
     }
   }