Popups and Tooltips in full screen mode (#8933)

Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
Alexander Platov
2025-05-21 11:41:37 +07:00
committed by GitHub
parent 8870224864
commit e201abcc91
3 changed files with 162 additions and 181 deletions
+11 -2
View File
@@ -19,6 +19,7 @@
import PopupInstance from './PopupInstance.svelte'
export let contentPanel: HTMLElement | undefined = undefined
export let fullScreen: boolean = false
const instances: PopupInstance[] = []
@@ -26,13 +27,21 @@
instances.forEach((p) => p.fitPopupInstance())
}
$: instances.length = $popups.filter((p) => p.dock !== true).length
const shouldDisplayPopup = (popup: any): boolean => {
return (
(fullScreen && document.fullscreenElement != null && popup.element !== 'full-centered') ||
(!fullScreen && document.fullscreenElement != null && popup.element === 'full-centered') ||
(!fullScreen && document.fullscreenElement == null)
)
}
$: instances.length = $popups.filter((p) => p.dock !== true && shouldDisplayPopup(p)).length
</script>
{#if $popups.length > 0}
<slot name="popup-header" />
{/if}
{#each $popups.filter((p) => p.dock !== true) as popup, i (popup.id)}
{#each $popups.filter((p) => p.dock !== true && shouldDisplayPopup(p)) as popup, i (popup.id)}
<PopupInstance
bind:this={instances[i]}
is={popup.is}
+142 -129
View File
@@ -23,6 +23,8 @@
import { capitalizeFirstLetter, formatKey } from '../utils'
import { testing } from '..'
export let fullScreen: boolean = false
let tooltipHTML: HTMLElement
let nubHTML: HTMLElement
let dir: TooltipAlignment
@@ -64,6 +66,8 @@
visibility: 'hidden',
classList: ''
}
const shouldHideTooltip = (): boolean =>
(fullScreen && document.fullscreenElement == null) || (!fullScreen && document.fullscreenElement != null)
const clearStyles = (): void => {
shown = false
@@ -248,6 +252,7 @@
}
const hideTooltip = (): void => {
if (shouldHideTooltip()) return
if (tooltipHTML) options.visibility = 'hidden'
shown = false
closeTooltip()
@@ -256,7 +261,7 @@
$: shownTooltip = $tooltip.element && tooltipHTML
const whileShow = (ev: MouseEvent): void => {
if (!$tooltip.element) return
if (!$tooltip.element || shouldHideTooltip()) return
const rectP = tooltipHTML.getBoundingClientRect()
rect = $tooltip.element.getBoundingClientRect()
const dT: number = dir === 'bottom' && $tooltip.kind !== 'submenu' ? 12 : 0
@@ -270,12 +275,13 @@
}
}
$: if (kind === 'submenu') {
$: if (kind === 'submenu' && !shouldHideTooltip()) {
options = fitSubmenu()
} else {
} else if (!shouldHideTooltip()) {
options = fitTooltip(tooltipHTML, clWidth)
}
afterUpdate(() => {
if (shouldHideTooltip()) return
if (kind === 'submenu') {
options = fitSubmenu()
} else {
@@ -287,18 +293,6 @@
})
</script>
{#if $tooltip.kind === 'popup'}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="modal-overlay antiOverlay"
style:z-index={($modals.findIndex((t) => t.type === 'tooltip') ?? 1) + 10000}
on:click|stopPropagation|preventDefault={() => {
closeTooltip()
}}
on:keydown|stopPropagation|preventDefault={() => {}}
/>
{/if}
<svelte:window
bind:innerWidth={docWidth}
bind:innerHeight={docHeight}
@@ -318,48 +312,8 @@
}
}}
/>
{#if $tooltip.component && $tooltip.kind !== 'submenu'}
<div
class="popup-tooltip {options.classList} {$tooltip.style}"
class:testing
class:shown
class:doublePadding={$tooltip.label}
use:resizeObserver={(element) => {
clWidth = element.clientWidth
options = fitTooltip(tooltipHTML, clWidth)
}}
style:top={options.top}
style:bottom={options.bottom}
style:left={options.left}
style:right={options.right}
style:width={options.width}
style:height={options.height}
style:transform={options.transform}
style:z-index={($modals.findIndex((t) => t.type === 'tooltip') ?? 1) + 10000}
bind:this={tooltipHTML}
>
{#if $tooltip.label}
<div class="fs-title mb-4">
<Label label={$tooltip.label} params={$tooltip.props ?? {}} />
</div>
{/if}
{#if typeof $tooltip.component === 'string'}
<Component
is={$tooltip.component}
props={$tooltip.props}
on:update={onUpdate !== undefined ? onUpdate : async () => {}}
/>
{:else}
<svelte:component
this={$tooltip.component}
{...$tooltip.props}
on:tooltip={(evt) => {
$modals = [...$modals.filter((t) => t.type !== 'tooltip'), { ...$tooltip, ...evt.detail }]
}}
on:update={onUpdate !== undefined ? onUpdate : async () => {}}
/>
{/if}
</div>
{#if !fullScreen}
<svg class="svg-mask">
<clipPath id="nub-bg">
<path
@@ -372,83 +326,142 @@
/>
</clipPath>
</svg>
{#if !$tooltip.noArrow}
{/if}
{#if (fullScreen && document.fullscreenElement != null) || (!fullScreen && document.fullscreenElement == null)}
{#if $tooltip.kind === 'popup'}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
bind:this={nubHTML}
class="modal-overlay antiOverlay"
style:z-index={($modals.findIndex((t) => t.type === 'tooltip') ?? 1) + 10000}
class="nub {nubDirection ?? ''}"
class:testing
class:shown
on:click|stopPropagation|preventDefault={() => {
closeTooltip()
}}
on:keydown|stopPropagation|preventDefault={() => {}}
/>
{/if}
{:else if $tooltip.label && $tooltip.kind !== 'submenu'}
<div
class="tooltip {dir ?? ''} {options.classList}"
bind:this={tooltipHTML}
style:top={options.top}
style:bottom={options.bottom}
style:left={options.left}
style:right={options.right}
style:width={options.width}
style:height={options.height}
style:max-width={options.maxWidth}
style:transform={options.transform}
style:visibility={options.visibility}
style:z-index={($modals.findIndex((t) => t.type === 'tooltip') ?? 1) + 10000}
>
<span class="label">
<Label label={$tooltip.label} params={$tooltip.props ?? {}} />
</span>
{#if $tooltip.keys !== undefined}
<div class="keys">
{#each $tooltip.keys as key, i}
{#if i !== 0}
<div class="mr-1 ml-1">/</div>
{/if}
{#each formatKey(key) as k, jj}
<div class="key">
{#each k as kk, j}
{#if j !== 0}
+
{/if}
{capitalizeFirstLetter(kk.trim())}
{/each}
</div>
{#if $tooltip.component && $tooltip.kind !== 'submenu'}
<div
class="popup-tooltip {options.classList} {$tooltip.style}"
class:testing
class:shown
class:doublePadding={$tooltip.label}
use:resizeObserver={(element) => {
clWidth = element.clientWidth
options = fitTooltip(tooltipHTML, clWidth)
}}
style:top={options.top}
style:bottom={options.bottom}
style:left={options.left}
style:right={options.right}
style:width={options.width}
style:height={options.height}
style:transform={options.transform}
style:z-index={($modals.findIndex((t) => t.type === 'tooltip') ?? 1) + 10000}
bind:this={tooltipHTML}
>
{#if $tooltip.label}
<div class="fs-title mb-4">
<Label label={$tooltip.label} params={$tooltip.props ?? {}} />
</div>
{/if}
{#if typeof $tooltip.component === 'string'}
<Component
is={$tooltip.component}
props={$tooltip.props}
on:update={onUpdate !== undefined ? onUpdate : async () => {}}
/>
{:else}
<svelte:component
this={$tooltip.component}
{...$tooltip.props}
on:tooltip={(evt) => {
$modals = [...$modals.filter((t) => t.type !== 'tooltip'), { ...$tooltip, ...evt.detail }]
}}
on:update={onUpdate !== undefined ? onUpdate : async () => {}}
/>
{/if}
</div>
{#if !$tooltip.noArrow}
<div
bind:this={nubHTML}
style:z-index={($modals.findIndex((t) => t.type === 'tooltip') ?? 1) + 10000}
class="nub {nubDirection ?? ''}"
class:testing
class:shown
/>
{/if}
{:else if $tooltip.label && $tooltip.kind !== 'submenu'}
<div
class="tooltip {dir ?? ''} {options.classList}"
bind:this={tooltipHTML}
style:top={options.top}
style:bottom={options.bottom}
style:left={options.left}
style:right={options.right}
style:width={options.width}
style:height={options.height}
style:max-width={options.maxWidth}
style:transform={options.transform}
style:visibility={options.visibility}
style:z-index={($modals.findIndex((t) => t.type === 'tooltip') ?? 1) + 10000}
>
<span class="label">
<Label label={$tooltip.label} params={$tooltip.props ?? {}} />
</span>
{#if $tooltip.keys !== undefined}
<div class="keys">
{#each $tooltip.keys as key, i}
{#if i !== 0}
<div class="mr-1 ml-1">/</div>
{/if}
{#each formatKey(key) as k, jj}
<div class="key">
{#each k as kk, j}
{#if j !== 0}
+
{/if}
{capitalizeFirstLetter(kk.trim())}
{/each}
</div>
{/each}
{/each}
{/each}
</div>
{/if}
</div>
{:else if $tooltip.kind === 'submenu'}
<div
class="submenu-container {dir ?? ''} {options.classList}"
use:resizeObserver={(element) => {
clWidth = element.clientWidth
}}
style:top={options.top}
style:bottom={options.bottom}
style:left={options.left}
style:right={options.right}
style:width={options.width}
style:height={options.height}
style:transform={options.transform}
style:z-index={($modals.findIndex((t) => t.type === 'tooltip') ?? 1) + 10000}
bind:this={tooltipHTML}
>
{#if typeof $tooltip.component === 'string'}
<Component
is={$tooltip.component}
props={$tooltip.props}
on:update={onUpdate !== undefined ? onUpdate : async () => {}}
/>
{:else}
<svelte:component
this={$tooltip.component}
{...$tooltip.props}
on:update={onUpdate !== undefined ? onUpdate : async () => {}}
/>
{/if}
</div>
</div>
{/if}
</div>
{:else if $tooltip.kind === 'submenu'}
<div
class="submenu-container {dir ?? ''} {options.classList}"
use:resizeObserver={(element) => {
clWidth = element.clientWidth
}}
style:top={options.top}
style:bottom={options.bottom}
style:left={options.left}
style:right={options.right}
style:width={options.width}
style:height={options.height}
style:transform={options.transform}
style:z-index={($modals.findIndex((t) => t.type === 'tooltip') ?? 1) + 10000}
bind:this={tooltipHTML}
>
{#if typeof $tooltip.component === 'string'}
<Component
is={$tooltip.component}
props={$tooltip.props}
on:update={onUpdate !== undefined ? onUpdate : async () => {}}
/>
{:else}
<svelte:component
this={$tooltip.component}
{...$tooltip.props}
on:update={onUpdate !== undefined ? onUpdate : async () => {}}
/>
{/if}
</div>
{/if}
{/if}
<style lang="scss">
@@ -25,12 +25,12 @@
IconMoreV,
IconUpOutline,
ModernButton,
PopupInstance,
Popup,
SplitButton,
eventToHTMLElement,
showPopup,
type AnySvelteComponent,
type CompAndProps
TooltipInstance
} from '@hcengineering/ui'
import view, { Action } from '@hcengineering/view'
import { getActions } from '@hcengineering/view-resources'
@@ -76,7 +76,6 @@
let allowCam: boolean = false
const allowShare: boolean = true
let allowLeave: boolean = false
let popup: CompAndProps | undefined = undefined
let noLabel: boolean = false
$: allowCam = $currentRoom?.type === RoomType.Video
@@ -100,50 +99,21 @@
await leaveRoom($myInfo, $myOffice)
}
function getPopup (component: AnySvelteComponent, e: MouseEvent, props: any = {}): CompAndProps {
return {
id: 'fsPopup',
is: component,
props,
element: eventToHTMLElement(e),
options: { category: 'popup', overlay: true },
close: () => {
popup = undefined
}
}
}
function micSettings (e: MouseEvent): void {
if (fullScreen) {
popup = getPopup(MicSettingPopup, e)
} else {
showPopup(MicSettingPopup, {}, eventToHTMLElement(e))
}
showPopup(MicSettingPopup, {}, eventToHTMLElement(e))
}
function camSettings (e: MouseEvent): void {
if (fullScreen) {
popup = getPopup(CamSettingPopup, e)
} else {
showPopup(CamSettingPopup, {}, eventToHTMLElement(e))
}
showPopup(CamSettingPopup, {}, eventToHTMLElement(e))
}
function shareSettings (e: MouseEvent): void {
if (fullScreen) {
popup = getPopup(ShareSettingPopup, e)
} else {
showPopup(ShareSettingPopup, {}, eventToHTMLElement(e))
}
showPopup(ShareSettingPopup, {}, eventToHTMLElement(e))
}
function setAccess (e: MouseEvent): void {
if (isOffice(room) && room.person !== me) return
if (fullScreen) {
popup = getPopup(RoomAccessPopup, e, { room })
} else {
showPopup(RoomAccessPopup, { room }, eventToHTMLElement(e))
}
showPopup(RoomAccessPopup, { room }, eventToHTMLElement(e))
}
const me = getCurrentEmployee()
@@ -337,20 +307,9 @@
</svelte:fragment>
<svelte:fragment slot="extra">
{#if popup && fullScreen}
<PopupInstance
is={popup.is}
props={popup.props}
element={popup.element}
onClose={popup.onClose}
onUpdate={popup.onUpdate}
zIndex={1}
top={true}
close={popup.close}
overlay={popup.options.overlay}
contentPanel={undefined}
{popup}
/>
{#if fullScreen}
<Popup fullScreen />
<TooltipInstance fullScreen />
{/if}
</svelte:fragment>
</ControlBarContainer>