Mobile layout fixes (#7077)

Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
Alexander Platov 2024-11-01 13:53:56 +03:00 committed by GitHub
parent da0da6b452
commit 7710ff8fa1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 90 additions and 25 deletions

View File

@ -1053,6 +1053,7 @@ a.no-line {
.bottom-divider { border-bottom: 1px solid var(--theme-divider-color); } .bottom-divider { border-bottom: 1px solid var(--theme-divider-color); }
.left-divider { border-left: 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-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); } .bottom-highlight-select { border-bottom: 1px solid var(--highlight-select); }

View File

@ -38,6 +38,8 @@
let sState: SeparatorState let sState: SeparatorState
$: sState = typeof float === 'string' ? SeparatorState.FLOAT : float ? SeparatorState.HIDDEN : SeparatorState.NORMAL $: 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' const direction: 'horizontal' | 'vertical' = 'horizontal'
let separators: SeparatedItem[] | null = null let separators: SeparatedItem[] | null = null
@ -67,6 +69,10 @@
let disabled: boolean = false let disabled: boolean = false
let side: 'start' | 'end' | undefined = undefined 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 fetchSeparators = (): void => {
const res = getSeparators(name, float) const res = getSeparators(name, float)
if (res !== null && !Array.isArray(res)) panel = res if (res !== null && !Array.isArray(res)) panel = res
@ -90,10 +96,6 @@
checkSizes() 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 convertSize = (prop: TSeparatedItem): string => (typeof prop === 'number' ? `${prop}px` : '')
const setSize = (element: HTMLElement, size: TSeparatedItem, next: boolean = false): void => { const setSize = (element: HTMLElement, size: TSeparatedItem, next: boolean = false): void => {
@ -229,6 +231,12 @@
const checkSizes = (): void => { const checkSizes = (): void => {
if (sState === SeparatorState.FLOAT) { 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) if (parentElement != null && panel != null) initSize(parentElement, panel)
} else if (sState === SeparatorState.NORMAL) { } else if (sState === SeparatorState.NORMAL) {
if (prevElement != null && prevElSize != null) initSize(prevElement, prevElSize) if (prevElement != null && prevElSize != null) initSize(prevElement, prevElSize)
@ -442,12 +450,13 @@
} }
} else if (sState === SeparatorState.FLOAT && parentElement != null) { } else if (sState === SeparatorState.FLOAT && parentElement != null) {
parentElement.style.pointerEvents = 'all' parentElement.style.pointerEvents = 'all'
saveSeparator(name, float, panel) if (!checkFullWidth()) saveSeparator(name, float, panel)
} }
document.body.style.cursor = '' document.body.style.cursor = ''
} }
function pointerDown (event: PointerEvent): void { function pointerDown (event: PointerEvent): void {
if (checkFullWidth()) return
prepareSeparation(event) prepareSeparation(event)
document.addEventListener('pointermove', pointerMove) document.addEventListener('pointermove', pointerMove)
document.addEventListener('pointerup', pointerUp) document.addEventListener('pointerup', pointerUp)

View File

@ -148,7 +148,7 @@ export function saveSeparator (
} }
export const panelSeparators: DefSeparators = [ export const panelSeparators: DefSeparators = [
{ minSize: 30, size: 'auto', maxSize: 'auto' }, { minSize: 20, size: 'auto', maxSize: 'auto' },
{ minSize: 17, size: 25, maxSize: 35, float: 'aside' } { minSize: 17, size: 25, maxSize: 35, float: 'aside' }
] ]

View File

@ -28,7 +28,8 @@
getCurrentLocation, getCurrentLocation,
showPopup, showPopup,
type AnySvelteComponent, type AnySvelteComponent,
type CompAndProps type CompAndProps,
resizeObserver
} from '@hcengineering/ui' } from '@hcengineering/ui'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import plugin from '../plugin' import plugin from '../plugin'
@ -51,6 +52,7 @@
import CamSettingPopup from './CamSettingPopup.svelte' import CamSettingPopup from './CamSettingPopup.svelte'
import MicSettingPopup from './MicSettingPopup.svelte' import MicSettingPopup from './MicSettingPopup.svelte'
import RoomAccessPopup from './RoomAccessPopup.svelte' import RoomAccessPopup from './RoomAccessPopup.svelte'
import { afterUpdate } from 'svelte'
export let room: Room export let room: Room
export let fullScreen: boolean = false export let fullScreen: boolean = false
@ -59,6 +61,11 @@
const allowShare: boolean = true const allowShare: boolean = true
let allowLeave: boolean = false let allowLeave: boolean = false
let popup: CompAndProps | undefined = undefined 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 $: allowCam = $currentRoom?.type === RoomType.Video
$: allowLeave = $myInfo?.room !== ($myOffice?._id ?? plugin.ids.Reception) $: 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 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 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> </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} {#if room._id !== plugin.ids.Reception}
<ModernButton <ModernButton
icon={roomAccessIcon[room.access]} icon={roomAccessIcon[room.access]}
@ -205,7 +225,7 @@
/> />
{/if} {/if}
{/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} {#if $isConnected}
<ModernButton <ModernButton
icon={$isFullScreen ? love.icon.ExitFullScreen : love.icon.FullScreen} icon={$isFullScreen ? love.icon.ExitFullScreen : love.icon.FullScreen}
@ -232,7 +252,7 @@
{#if allowLeave} {#if allowLeave}
<ModernButton <ModernButton
icon={plugin.icon.LeaveRoom} icon={plugin.icon.LeaveRoom}
label={plugin.string.LeaveRoom} label={noLabel ? undefined : plugin.string.LeaveRoom}
tooltip={{ label: plugin.string.LeaveRoom, direction: 'top' }} tooltip={{ label: plugin.string.LeaveRoom, direction: 'top' }}
kind={'negative'} kind={'negative'}
size={'large'} size={'large'}
@ -240,6 +260,7 @@
/> />
{/if} {/if}
</div> </div>
<div class="flex-grow" />
{#if popup && fullScreen} {#if popup && fullScreen}
<PopupInstance <PopupInstance
is={popup.is} is={popup.is}
@ -259,6 +280,7 @@
<style lang="scss"> <style lang="scss">
.bar { .bar {
overflow-x: auto;
position: relative; position: relative;
padding: 1rem; padding: 1rem;
border-top: 1px solid var(--theme-divider-color); border-top: 1px solid var(--theme-divider-color);
@ -270,5 +292,9 @@
right: 1rem; right: 1rem;
height: 100%; height: 100%;
} }
&.combinePanel .bar__left-panel {
position: static;
}
} }
</style> </style>

View File

@ -17,7 +17,7 @@
import { personByIdStore } from '@hcengineering/contact-resources' import { personByIdStore } from '@hcengineering/contact-resources'
import { Room as TypeRoom } from '@hcengineering/love' import { Room as TypeRoom } from '@hcengineering/love'
import { getMetadata } from '@hcengineering/platform' import { getMetadata } from '@hcengineering/platform'
import { Label, Loading, resizeObserver } from '@hcengineering/ui' import { Label, Loading, resizeObserver, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
import { import {
LocalParticipant, LocalParticipant,
LocalTrackPublication, LocalTrackPublication,
@ -333,7 +333,7 @@
$: if (((document.fullscreenElement && !$isFullScreen) || $isFullScreen) && roomEl) toggleFullscreen() $: if (((document.fullscreenElement && !$isFullScreen) || $isFullScreen) && roomEl) toggleFullscreen()
</script> </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} {#if $isConnected && !$isCurrentInstanceConnected}
<div class="flex justify-center error h-full w-full clear-mins"> <div class="flex justify-center error h-full w-full clear-mins">
<Label label={love.string.AnotherWindowError} /> <Label label={love.string.AnotherWindowError} />
@ -345,7 +345,13 @@
{:else if loading} {:else if loading}
<Loading /> <Loading />
{/if} {/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"> <div class="screenContainer">
<video class="screen" bind:this={screen}></video> <video class="screen" bind:this={screen}></video>
</div> </div>
@ -452,6 +458,15 @@
gap: 0.5rem; gap: 0.5rem;
} }
} }
&.mobile {
padding: var(--spacing-0_5);
&:not(.sharing) .videoGrid,
&.sharing {
gap: var(--spacing-0_5);
}
}
} }
.hidden { .hidden {
display: none; display: none;

View File

@ -647,6 +647,18 @@
const checkOnHide = (): void => { const checkOnHide = (): void => {
if ($deviceInfo.navigator.visible && $deviceInfo.docWidth <= 1024) $deviceInfo.navigator.visible = false 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' $: $deviceInfo.navigator.direction = $deviceInfo.isMobile && $deviceInfo.isPortrait ? 'horizontal' : 'vertical'
let appsMini: boolean let appsMini: boolean
$: appsMini = $: appsMini =

View File

@ -57,23 +57,25 @@
</div> </div>
</div> </div>
{:else if !mini} {: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)} {#each $tabsStore as tab (tab._id)}
<WorkbenchTabPresenter {tab} /> <WorkbenchTabPresenter {tab} />
{/each} {/each}
</ScrollerBar> </div>
{:else if selectedTab !== undefined} {:else if selectedTab !== undefined}
<WorkbenchTabPresenter tab={selectedTab} /> <WorkbenchTabPresenter tab={selectedTab} />
<ButtonIcon {#if $tabsStore.length > 1}
bind:element <ButtonIcon
icon={IconMoreH} bind:element
iconProps={{ fill: 'var(--theme-dark-color)' }} icon={IconMoreH}
size={'extra-small'} iconProps={{ fill: 'var(--theme-dark-color)' }}
kind={'tertiary'} size={'extra-small'}
hasMenu kind={'tertiary'}
{pressed} hasMenu
on:click={showTabs} {pressed}
/> on:click={showTabs}
/>
{/if}
{/if} {/if}
{#if !popup} {#if !popup}
<ButtonIcon <ButtonIcon