From 7710ff8fa19e5ef8d3fc64f507c68574e89c8b91 Mon Sep 17 00:00:00 2001
From: Alexander Platov <alexander.platov@hardcoreeng.com>
Date: Fri, 1 Nov 2024 13:53:56 +0300
Subject: [PATCH] Mobile layout fixes (#7077)

Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
---
 packages/theme/styles/_layouts.scss           |  1 +
 packages/ui/src/components/Separator.svelte   | 19 ++++++++---
 packages/ui/src/resize.ts                     |  2 +-
 .../src/components/ControlBar.svelte          | 34 ++++++++++++++++---
 .../love-resources/src/components/Room.svelte | 21 ++++++++++--
 .../src/components/Workbench.svelte           | 12 +++++++
 .../src/components/WorkbenchTabs.svelte       | 26 +++++++-------
 7 files changed, 90 insertions(+), 25 deletions(-)

diff --git a/packages/theme/styles/_layouts.scss b/packages/theme/styles/_layouts.scss
index 3247cfbf48..54f7d76568 100644
--- a/packages/theme/styles/_layouts.scss
+++ b/packages/theme/styles/_layouts.scss
@@ -1053,6 +1053,7 @@ a.no-line {
 .bottom-divider { border-bottom: 1px solid var(--theme-divider-color); }
 .left-divider { border-left: 1px solid var(--theme-divider-color); }
 .right-divider { border-right: 1px solid var(--theme-divider-color); }
+.right-navpanel-border { border-right: 1px solid var(--theme-navpanel-border); }
 .bottom-highlight-select { border-bottom: 1px solid var(--highlight-select); }
 
 
diff --git a/packages/ui/src/components/Separator.svelte b/packages/ui/src/components/Separator.svelte
index 613177ce66..7ab8e37f72 100644
--- a/packages/ui/src/components/Separator.svelte
+++ b/packages/ui/src/components/Separator.svelte
@@ -38,6 +38,8 @@
 
   let sState: SeparatorState
   $: sState = typeof float === 'string' ? SeparatorState.FLOAT : float ? SeparatorState.HIDDEN : SeparatorState.NORMAL
+  const checkFullWidth = (): boolean =>
+    sState === SeparatorState.FLOAT && $deviceInfo.isMobile && $deviceInfo.isPortrait
 
   const direction: 'horizontal' | 'vertical' = 'horizontal'
   let separators: SeparatedItem[] | null = null
@@ -67,6 +69,10 @@
   let disabled: boolean = false
   let side: 'start' | 'end' | undefined = undefined
 
+  $: fs = $deviceInfo.fontSize
+  const remToPx = (rem: number): number => rem * fs
+  const pxToRem = (px: number): number => px / fs
+
   const fetchSeparators = (): void => {
     const res = getSeparators(name, float)
     if (res !== null && !Array.isArray(res)) panel = res
@@ -90,10 +96,6 @@
     checkSizes()
   }
 
-  $: fs = $deviceInfo.fontSize
-  const remToPx = (rem: number): number => rem * fs
-  const pxToRem = (px: number): number => px / fs
-
   const convertSize = (prop: TSeparatedItem): string => (typeof prop === 'number' ? `${prop}px` : '')
 
   const setSize = (element: HTMLElement, size: TSeparatedItem, next: boolean = false): void => {
@@ -229,6 +231,12 @@
 
   const checkSizes = (): void => {
     if (sState === SeparatorState.FLOAT) {
+      if (checkFullWidth() && panel != null) {
+        const s = pxToRem(window.innerWidth)
+        panel.size = s
+        panel.maxSize = s
+        panel.minSize = s
+      }
       if (parentElement != null && panel != null) initSize(parentElement, panel)
     } else if (sState === SeparatorState.NORMAL) {
       if (prevElement != null && prevElSize != null) initSize(prevElement, prevElSize)
@@ -442,12 +450,13 @@
       }
     } else if (sState === SeparatorState.FLOAT && parentElement != null) {
       parentElement.style.pointerEvents = 'all'
-      saveSeparator(name, float, panel)
+      if (!checkFullWidth()) saveSeparator(name, float, panel)
     }
     document.body.style.cursor = ''
   }
 
   function pointerDown (event: PointerEvent): void {
+    if (checkFullWidth()) return
     prepareSeparation(event)
     document.addEventListener('pointermove', pointerMove)
     document.addEventListener('pointerup', pointerUp)
diff --git a/packages/ui/src/resize.ts b/packages/ui/src/resize.ts
index 7ebae9e983..29fd80c43d 100644
--- a/packages/ui/src/resize.ts
+++ b/packages/ui/src/resize.ts
@@ -148,7 +148,7 @@ export function saveSeparator (
 }
 
 export const panelSeparators: DefSeparators = [
-  { minSize: 30, size: 'auto', maxSize: 'auto' },
+  { minSize: 20, size: 'auto', maxSize: 'auto' },
   { minSize: 17, size: 25, maxSize: 35, float: 'aside' }
 ]
 
diff --git a/plugins/love-resources/src/components/ControlBar.svelte b/plugins/love-resources/src/components/ControlBar.svelte
index c834f3b508..ef0541396c 100644
--- a/plugins/love-resources/src/components/ControlBar.svelte
+++ b/plugins/love-resources/src/components/ControlBar.svelte
@@ -28,7 +28,8 @@
     getCurrentLocation,
     showPopup,
     type AnySvelteComponent,
-    type CompAndProps
+    type CompAndProps,
+    resizeObserver
   } from '@hcengineering/ui'
   import view from '@hcengineering/view'
   import plugin from '../plugin'
@@ -51,6 +52,7 @@
   import CamSettingPopup from './CamSettingPopup.svelte'
   import MicSettingPopup from './MicSettingPopup.svelte'
   import RoomAccessPopup from './RoomAccessPopup.svelte'
+  import { afterUpdate } from 'svelte'
 
   export let room: Room
   export let fullScreen: boolean = false
@@ -59,6 +61,11 @@
   const allowShare: boolean = true
   let allowLeave: boolean = false
   let popup: CompAndProps | undefined = undefined
+  let grow: HTMLElement
+  let leftPanel: HTMLElement
+  let leftPanelSize: number = 0
+  let noLabel: boolean = false
+  let combinePanel: boolean = false
 
   $: allowCam = $currentRoom?.type === RoomType.Video
   $: allowLeave = $myInfo?.room !== ($myOffice?._id ?? plugin.ids.Reception)
@@ -149,9 +156,22 @@
 
   const camKeys = client.getModel().findAllSync(view.class.Action, { _id: plugin.action.ToggleVideo })?.[0]?.keyBinding
   const micKeys = client.getModel().findAllSync(view.class.Action, { _id: plugin.action.ToggleMic })?.[0]?.keyBinding
+
+  const checkBar = (): void => {
+    if (grow === undefined || leftPanel === undefined) return
+    if (!noLabel && leftPanel.clientWidth > leftPanelSize) leftPanelSize = leftPanel.clientWidth
+    if (grow.clientWidth - 16 < leftPanel.clientWidth && !noLabel && !combinePanel) noLabel = true
+    else if (grow.clientWidth - 16 < leftPanel.clientWidth && noLabel && !combinePanel) combinePanel = true
+    else if (grow.clientWidth * 2 - 32 > leftPanel.clientWidth && noLabel && combinePanel) combinePanel = false
+    else if (grow.clientWidth - 32 >= leftPanelSize && noLabel && !combinePanel) noLabel = false
+  }
+  afterUpdate(() => {
+    checkBar()
+  })
 </script>
 
-<div class="bar w-full flex-center flex-gap-2 flex-no-shrink">
+<div class="bar w-full flex-center flex-gap-2 flex-no-shrink" class:combinePanel use:resizeObserver={checkBar}>
+  <div bind:this={grow} class="flex-grow" />
   {#if room._id !== plugin.ids.Reception}
     <ModernButton
       icon={roomAccessIcon[room.access]}
@@ -205,7 +225,7 @@
       />
     {/if}
   {/if}
-  <div class="bar__left-panel flex-gap-2 flex-center">
+  <div bind:this={leftPanel} class="bar__left-panel flex-gap-2 flex-center">
     {#if $isConnected}
       <ModernButton
         icon={$isFullScreen ? love.icon.ExitFullScreen : love.icon.FullScreen}
@@ -232,7 +252,7 @@
     {#if allowLeave}
       <ModernButton
         icon={plugin.icon.LeaveRoom}
-        label={plugin.string.LeaveRoom}
+        label={noLabel ? undefined : plugin.string.LeaveRoom}
         tooltip={{ label: plugin.string.LeaveRoom, direction: 'top' }}
         kind={'negative'}
         size={'large'}
@@ -240,6 +260,7 @@
       />
     {/if}
   </div>
+  <div class="flex-grow" />
   {#if popup && fullScreen}
     <PopupInstance
       is={popup.is}
@@ -259,6 +280,7 @@
 
 <style lang="scss">
   .bar {
+    overflow-x: auto;
     position: relative;
     padding: 1rem;
     border-top: 1px solid var(--theme-divider-color);
@@ -270,5 +292,9 @@
       right: 1rem;
       height: 100%;
     }
+
+    &.combinePanel .bar__left-panel {
+      position: static;
+    }
   }
 </style>
diff --git a/plugins/love-resources/src/components/Room.svelte b/plugins/love-resources/src/components/Room.svelte
index e1d2bd92d4..93f8f6d093 100644
--- a/plugins/love-resources/src/components/Room.svelte
+++ b/plugins/love-resources/src/components/Room.svelte
@@ -17,7 +17,7 @@
   import { personByIdStore } from '@hcengineering/contact-resources'
   import { Room as TypeRoom } from '@hcengineering/love'
   import { getMetadata } from '@hcengineering/platform'
-  import { Label, Loading, resizeObserver } from '@hcengineering/ui'
+  import { Label, Loading, resizeObserver, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
   import {
     LocalParticipant,
     LocalTrackPublication,
@@ -333,7 +333,7 @@
   $: if (((document.fullscreenElement && !$isFullScreen) || $isFullScreen) && roomEl) toggleFullscreen()
 </script>
 
-<div bind:this={roomEl} class="flex-col-center w-full h-full" class:theme-dark={$isFullScreen}>
+<div bind:this={roomEl} class="flex-col-center w-full h-full right-navpanel-border" class:theme-dark={$isFullScreen}>
   {#if $isConnected && !$isCurrentInstanceConnected}
     <div class="flex justify-center error h-full w-full clear-mins">
       <Label label={love.string.AnotherWindowError} />
@@ -345,7 +345,13 @@
   {:else if loading}
     <Loading />
   {/if}
-  <div class="room-container" class:sharing={$screenSharing} class:many={columns > 3} class:hidden={loading}>
+  <div
+    class="room-container"
+    class:sharing={$screenSharing}
+    class:many={columns > 3}
+    class:hidden={loading}
+    class:mobile={$deviceInfo.isMobile}
+  >
     <div class="screenContainer">
       <video class="screen" bind:this={screen}></video>
     </div>
@@ -452,6 +458,15 @@
         gap: 0.5rem;
       }
     }
+
+    &.mobile {
+      padding: var(--spacing-0_5);
+
+      &:not(.sharing) .videoGrid,
+      &.sharing {
+        gap: var(--spacing-0_5);
+      }
+    }
   }
   .hidden {
     display: none;
diff --git a/plugins/workbench-resources/src/components/Workbench.svelte b/plugins/workbench-resources/src/components/Workbench.svelte
index 5a05d4788e..24f5ad5b49 100644
--- a/plugins/workbench-resources/src/components/Workbench.svelte
+++ b/plugins/workbench-resources/src/components/Workbench.svelte
@@ -647,6 +647,18 @@
   const checkOnHide = (): void => {
     if ($deviceInfo.navigator.visible && $deviceInfo.docWidth <= 1024) $deviceInfo.navigator.visible = false
   }
+  let oldNavVisible: boolean = $deviceInfo.navigator.visible
+  let oldASideVisible: boolean = $deviceInfo.aside.visible
+  $: if (oldNavVisible !== $deviceInfo.navigator.visible || oldASideVisible !== $deviceInfo.aside.visible) {
+    if ($deviceInfo.isMobile && $deviceInfo.isPortrait && $deviceInfo.navigator.float) {
+      if ($deviceInfo.navigator.visible && $deviceInfo.aside.visible) {
+        if (oldNavVisible) $deviceInfo.navigator.visible = false
+        else $deviceInfo.aside.visible = false
+      }
+    }
+    oldNavVisible = $deviceInfo.navigator.visible
+    oldASideVisible = $deviceInfo.aside.visible
+  }
   $: $deviceInfo.navigator.direction = $deviceInfo.isMobile && $deviceInfo.isPortrait ? 'horizontal' : 'vertical'
   let appsMini: boolean
   $: appsMini =
diff --git a/plugins/workbench-resources/src/components/WorkbenchTabs.svelte b/plugins/workbench-resources/src/components/WorkbenchTabs.svelte
index e7c950a24e..6a32f54119 100644
--- a/plugins/workbench-resources/src/components/WorkbenchTabs.svelte
+++ b/plugins/workbench-resources/src/components/WorkbenchTabs.svelte
@@ -57,23 +57,25 @@
       </div>
     </div>
   {:else if !mini}
-    <ScrollerBar bind:scroller padding={'.25rem 0'}>
+    <div class="flex-row-center gap-1 py-1 overflow-x-auto">
       {#each $tabsStore as tab (tab._id)}
         <WorkbenchTabPresenter {tab} />
       {/each}
-    </ScrollerBar>
+    </div>
   {:else if selectedTab !== undefined}
     <WorkbenchTabPresenter tab={selectedTab} />
-    <ButtonIcon
-      bind:element
-      icon={IconMoreH}
-      iconProps={{ fill: 'var(--theme-dark-color)' }}
-      size={'extra-small'}
-      kind={'tertiary'}
-      hasMenu
-      {pressed}
-      on:click={showTabs}
-    />
+    {#if $tabsStore.length > 1}
+      <ButtonIcon
+        bind:element
+        icon={IconMoreH}
+        iconProps={{ fill: 'var(--theme-dark-color)' }}
+        size={'extra-small'}
+        kind={'tertiary'}
+        hasMenu
+        {pressed}
+        on:click={showTabs}
+      />
+    {/if}
   {/if}
   {#if !popup}
     <ButtonIcon