diff --git a/packages/hub-ui/src/client/components/dock/Dock.vue b/packages/hub-ui/src/client/components/dock/Dock.vue index 4acb1284..83716027 100644 --- a/packages/hub-ui/src/client/components/dock/Dock.vue +++ b/packages/hub-ui/src/client/components/dock/Dock.vue @@ -2,7 +2,7 @@ import type { DocksContext } from '@devframes/hub/client' import type { CSSProperties } from 'vue' import type { DockLayout } from './dock-layout' -import { useEventListener, useScreenSafeArea, whenever } from '@vueuse/core' +import { useEventListener, whenever } from '@vueuse/core' import { computed, onMounted, reactive, ref, useTemplateRef, watch } from 'vue' import { BUILTIN_ENTRY_CLIENT_AUTH_NOTICE } from '../../constants' import { t } from '../../i18n' @@ -10,6 +10,7 @@ import { docksSplitGroupsWithCapacity, getEntryGroup, resolveNextRecentDockIds, import { setDocksOverflowPanel, useDocksGroupPanel } from '../../state/floating-tooltip' import { useSettings } from '../../state/settings-defaults' import { useIsRpcTrusted } from '../../utils/useIsRpcTrusted' +import { useSafeArea } from '../../utils/useSafeArea' import BrandMark from '../icons/BrandMark.vue' import { dockLayoutCssVars, @@ -39,10 +40,12 @@ const layout = computed(() => resolveDockLayout(props.layout)) const isSafari = navigator.userAgent.includes('Safari') && !navigator.userAgent.includes('Chrome') -const safeArea = useScreenSafeArea() +const anchorEl = useTemplateRef('anchorEl') + +const safeArea = useSafeArea(anchorEl) function toNumber(value: string) { - const num = +value + const num = Number.parseFloat(value) if (Number.isNaN(num)) return 0 return num @@ -61,7 +64,6 @@ const panelMargins = computed(() => resolveViewportMargins( )) const dockEl = useTemplateRef('dockEl') -const anchorEl = useTemplateRef('anchorEl') const recalculateCounter = ref(0) const isHovering = ref(false) diff --git a/packages/hub-ui/src/client/style.css b/packages/hub-ui/src/client/style.css index 73a81dc4..ff9da9c3 100644 --- a/packages/hub-ui/src/client/style.css +++ b/packages/hub-ui/src/client/style.css @@ -28,6 +28,10 @@ --app-scrollbar-radius: 1px; --app-scrollbar-thumb: #8884; --app-scrollbar-thumb-hover: #8885; + --devframes-safe-area-top: env(safe-area-inset-top, 0px); + --devframes-safe-area-right: env(safe-area-inset-right, 0px); + --devframes-safe-area-bottom: env(safe-area-inset-bottom, 0px); + --devframes-safe-area-left: env(safe-area-inset-left, 0px); } ::-webkit-scrollbar { diff --git a/packages/hub-ui/src/client/utils/useSafeArea.ts b/packages/hub-ui/src/client/utils/useSafeArea.ts new file mode 100644 index 00000000..0195f6e6 --- /dev/null +++ b/packages/hub-ui/src/client/utils/useSafeArea.ts @@ -0,0 +1,30 @@ +import type { MaybeElementRef } from '@vueuse/core' +import { tryOnMounted, unrefElement, useDebounceFn, useEventListener } from '@vueuse/core' +import { shallowRef } from 'vue' + +/** + * Ported from VueUse's `useScreenSafeArea` for now: it always sets its CSS variables on + * `` and has no option for a custom element, which breaks hydration on SSR hosts. + */ +export function useSafeArea(target: MaybeElementRef) { + const top = shallowRef('') + const right = shallowRef('') + const bottom = shallowRef('') + const left = shallowRef('') + + function update() { + const element = unrefElement(target) + if (!element) + return + const style = getComputedStyle(element) + top.value = style.getPropertyValue('--devframes-safe-area-top') + right.value = style.getPropertyValue('--devframes-safe-area-right') + bottom.value = style.getPropertyValue('--devframes-safe-area-bottom') + left.value = style.getPropertyValue('--devframes-safe-area-left') + } + + tryOnMounted(update) + useEventListener('resize', useDebounceFn(update), { passive: true }) + + return { top, right, bottom, left, update } +}