Merge branch 'feature/initial-viewer-ui-updates' of https://github.com/specklesystems/speckle-server into feature/initial-viewer-ui-updates
This commit is contained in:
@@ -56,7 +56,7 @@
|
||||
<template v-if="showControls">
|
||||
<ViewerControlsLeft />
|
||||
<ViewerControlsBottom />
|
||||
<ViewerControlsRight v-if="!isMobile" />
|
||||
<ViewerControlsRight v-if="isMobile" />
|
||||
</template>
|
||||
|
||||
<ViewerLimitsDialog
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
hide-text
|
||||
color="subtle"
|
||||
:icon-left="settingsIcon"
|
||||
:class="showVisibilityOptions ? '!text-primary-focus !bg-info-lighter' : ''"
|
||||
:class="
|
||||
showVisibilityOptions
|
||||
? '!text-primary-focus !dark:text-foreground-on-primary !bg-info-lighter'
|
||||
: ''
|
||||
"
|
||||
@click="showVisibilityOptions = !showVisibilityOptions"
|
||||
/>
|
||||
</LayoutMenu>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<aside>
|
||||
<ViewerControlsButtonGroup
|
||||
v-show="activePanel === 'none'"
|
||||
class="absolute left-1/2 -translate-x-1/2 bottom-4 z-40"
|
||||
class="absolute left-1/2 -translate-x-1/2 bottom-4 z-50"
|
||||
>
|
||||
<ViewerControlsButtonToggle
|
||||
v-for="panel in panels"
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<ViewerLayoutPanel
|
||||
v-if="activePanel !== 'none'"
|
||||
class="absolute left-1/2 -translate-x-1/2 bottom-4 z-30 flex p-1 items-center justify-between w-80"
|
||||
class="absolute left-1/2 -translate-x-1/2 bottom-4 z-50 flex p-1 items-center justify-between w-80"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
<component :is="panels[activePanel].icon" class="h-4 w-4 ml-1 mr-1.5" />
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
|
||||
<template>
|
||||
<aside
|
||||
class="absolute left-2 lg:left-0 top-[3.5rem] z-20 flex rounded-lg border border-outline-2 bg-foundation px-1 overflow-visible"
|
||||
class="absolute left-2 lg:left-0 top-[3.5rem] z-40 flex rounded-lg border border-outline-2 bg-foundation px-1 overflow-visible lg:h-full"
|
||||
:class="[
|
||||
isEmbedEnabled
|
||||
? ''
|
||||
: 'lg:top-[3rem] lg:rounded-none lg:px-2 lg:max-h-[calc(100dvh-3rem)] lg:border-l-0 lg:border-t-0 lg:border-b-0',
|
||||
hasActivePanel && 'h-full max-h-[calc(100dvh-7.875rem)] rounded-r-none'
|
||||
hasActivePanel && 'h-full max-h-[calc(100dvh-8rem)] rounded-r-none'
|
||||
]"
|
||||
>
|
||||
<div class="flex flex-col gap-2 py-1" :class="isEmbedEnabled ? '' : 'lg:py-2'">
|
||||
@@ -99,15 +99,14 @@
|
||||
|
||||
<!-- Scrollable controls container -->
|
||||
<div
|
||||
v-show="activePanel !== 'none'"
|
||||
ref="scrollableControlsContainer"
|
||||
:class="[
|
||||
'simple-scrollbar bg-foundation absolute z-10 left-[calc(2.5rem+1px)] top-[-1px] bottom-[-1px] overflow-y-auto overflow-x-visible border-outline-2 border border-l-0 rounded-lg rounded-tl-none rounded-bl-none',
|
||||
'simple-scrollbar overflow-x-hidden bg-foundation absolute z-10 left-[calc(2.5rem+1px)] top-[-1px] bottom-[-1px] overflow-y-auto border-outline-2 border border-l-0 rounded-lg rounded-tl-none rounded-bl-none ',
|
||||
hasActivePanel ? 'opacity-100' : 'opacity-0',
|
||||
isEmbedEnabled ? '' : 'lg:left-[calc(3rem+1px)] lg:border-none lg:rounded-none'
|
||||
]"
|
||||
:style="`width: ${
|
||||
isMobile ? 'calc(100vw - 3.75rem)' : `${width + 4}px`
|
||||
}; overflow-x: visible !important;`"
|
||||
:style="`width: ${isMobile ? 'calc(100vw - 3.6rem)' : `${width + 4}px`};`"
|
||||
>
|
||||
<ViewerModelsPanel
|
||||
v-if="resourceItems.length !== 0 && activePanel === 'models'"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<aside class="absolute top-[3.75rem] z-20" :style="dynamicStyles">
|
||||
<ViewerControlsButtonGroup direction="vertical">
|
||||
<aside
|
||||
ref="buttonContainer"
|
||||
class="absolute top-[3.75rem] z-20"
|
||||
:style="dynamicStyles"
|
||||
>
|
||||
<ViewerControlsButtonGroup ref="buttonContainer" direction="vertical">
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="
|
||||
getTooltipProps(getShortcutDisplayText(shortcuts.ZoomExtentsOrSelection), {
|
||||
@@ -49,6 +53,7 @@ const { getTooltipProps } = useSmartTooltipDelay()
|
||||
|
||||
const activePanel = ref<ActivePanel>('none')
|
||||
const menuContainer = ref<Nullable<HTMLElement>>(null)
|
||||
const buttonContainer = ref<Nullable<HTMLElement>>(null)
|
||||
|
||||
const dynamicStyles = computed(() => {
|
||||
if (props.sidebarOpen) {
|
||||
@@ -75,7 +80,13 @@ registerShortcuts({
|
||||
ZoomExtentsOrSelection: () => trackAndzoomExtentsOrSelection()
|
||||
})
|
||||
|
||||
onClickOutside(menuContainer, () => {
|
||||
activePanel.value = 'none'
|
||||
})
|
||||
onClickOutside(
|
||||
menuContainer,
|
||||
() => {
|
||||
activePanel.value = 'none'
|
||||
},
|
||||
{
|
||||
ignore: [buttonContainer]
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -63,7 +63,8 @@
|
||||
<button
|
||||
class="size-6 flex items-center justify-center rounded-md"
|
||||
:class="[
|
||||
showSettings && 'text-primary-focus bg-info-lighter',
|
||||
showSettings &&
|
||||
'text-primary-focus bg-info-lighter dark:text-foreground-on-primary',
|
||||
!showSettings && 'text-foreground hover:bg-foundation-2'
|
||||
]"
|
||||
@click="showSettings = !showSettings"
|
||||
|
||||
@@ -79,7 +79,8 @@
|
||||
<button
|
||||
class="size-6 flex items-center justify-center rounded-md"
|
||||
:class="[
|
||||
showSettings && 'text-primary-focus bg-info-lighter',
|
||||
showSettings &&
|
||||
'text-primary-focus bg-info-lighter dark:text-foreground-on-primary',
|
||||
!showSettings && 'text-foreground hover:bg-foundation-2'
|
||||
]"
|
||||
@click="showSettings = !showSettings"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { MaybeNullOrUndefined } from '@speckle/shared'
|
||||
import { useBreakpoints } from '@vueuse/core'
|
||||
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
|
||||
|
||||
/**
|
||||
* Smart tooltip delay composable
|
||||
@@ -7,9 +9,13 @@ import type { MaybeNullOrUndefined } from '@speckle/shared'
|
||||
* - First tooltip shows after a configurable delay (default 1 second)
|
||||
* - Subsequent tooltips show instantly once user has shown intent
|
||||
* - State resets after a period of inactivity (default 3 seconds)
|
||||
* - Only shows tooltips on non-mobile devices
|
||||
*/
|
||||
|
||||
export function useSmartTooltipDelay() {
|
||||
const breakpoints = useBreakpoints(TailwindBreakpoints)
|
||||
const isMobile = breakpoints.smaller('sm')
|
||||
|
||||
const initialDelay = 1000
|
||||
const resetAfter = 3000
|
||||
|
||||
@@ -19,20 +25,31 @@ export function useSmartTooltipDelay() {
|
||||
const getTooltipProps = (
|
||||
content?: MaybeNullOrUndefined<string>,
|
||||
additionalProps: Record<string, unknown> = {}
|
||||
) => ({
|
||||
content,
|
||||
delay: hasShownAny.value ? 0 : initialDelay,
|
||||
onShow: () => {
|
||||
hasShownAny.value = true
|
||||
if (resetTimer.value) {
|
||||
clearTimeout(resetTimer.value)
|
||||
) => {
|
||||
// Don't show tooltips on mobile devices
|
||||
if (isMobile.value) {
|
||||
return {
|
||||
content: null,
|
||||
disabled: true,
|
||||
...additionalProps
|
||||
}
|
||||
resetTimer.value = setTimeout(() => {
|
||||
hasShownAny.value = false
|
||||
}, resetAfter)
|
||||
},
|
||||
...additionalProps
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
content,
|
||||
delay: hasShownAny.value ? 0 : initialDelay,
|
||||
onShow: () => {
|
||||
hasShownAny.value = true
|
||||
if (resetTimer.value) {
|
||||
clearTimeout(resetTimer.value)
|
||||
}
|
||||
resetTimer.value = setTimeout(() => {
|
||||
hasShownAny.value = false
|
||||
}, resetAfter)
|
||||
},
|
||||
...additionalProps
|
||||
}
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
if (resetTimer.value) {
|
||||
|
||||
Reference in New Issue
Block a user