Updated tooltips

This commit is contained in:
Mike Tasset
2025-07-29 15:59:44 +02:00
parent a9c3b7c83e
commit 3f49ced8b2
6 changed files with 88 additions and 35 deletions
@@ -7,7 +7,7 @@
<ViewerControlsButtonToggle
v-for="panel in panels"
:key="panel.id"
v-tippy="panel.tooltip"
v-tippy="getTooltipProps(panel.tooltip)"
:active="activePanel === panel.id"
:icon="panel.icon"
@click="toggleActivePanel(panel.id)"
@@ -64,6 +64,7 @@ const { toggleSectionBox } = useSectionBoxUtilities()
const { getActiveMeasurement, removeMeasurement, enableMeasurements } =
useMeasurementUtilities()
const { resetExplode } = useFilterUtilities()
const { getTooltipProps } = useSmartTooltipDelay()
const activePanel = ref<ActivePanel>(ActivePanel.none)
const panels = shallowRef({
@@ -10,37 +10,36 @@
]"
>
<div class="flex flex-col gap-2 py-1" :class="isEmbedEnabled ? '' : 'lg:py-2'">
<!-- Models -->
<ViewerControlsButtonToggle
v-tippy="{
content: getShortcutDisplayText(shortcuts.ToggleModels),
placement: 'right'
}"
v-tippy="
getTooltipProps(getShortcutDisplayText(shortcuts.ToggleModels), {
placement: 'right'
})
"
:active="activePanel === 'models'"
:icon="'IconViewerModels'"
@click="toggleActivePanel('models')"
/>
<!-- Filters -->
<ViewerControlsButtonToggle
v-tippy="getShortcutDisplayText(shortcuts.ToggleFilters)"
v-tippy="
getTooltipProps(getShortcutDisplayText(shortcuts.ToggleFilters), {
placement: 'right'
})
"
:active="activePanel === 'filters'"
:icon="'IconViewerExplorer'"
@click="toggleActivePanel('filters')"
/>
<!-- Comment threads -->
<ViewerControlsButtonToggle
v-tippy="{
content: getShortcutDisplayText(shortcuts.ToggleDiscussions),
placement: 'right'
}"
v-tippy="
getTooltipProps(getShortcutDisplayText(shortcuts.ToggleDiscussions), {
placement: 'right'
})
"
:active="activePanel === 'discussions'"
:icon="'IconViewerDiscussions'"
@click="toggleActivePanel('discussions')"
/>
<!-- Automation runs -->
<ViewerControlsButtonToggle
v-if="allAutomationRuns.length !== 0"
v-tippy="
@@ -165,6 +164,7 @@ const { isEnabled: isEmbedEnabled } = useEmbed()
const breakpoints = useBreakpoints(TailwindBreakpoints)
const { isSmallerOrEqualSm } = useIsSmallerOrEqualThanBreakpoint()
const isMobile = breakpoints.smaller('sm')
const { getTooltipProps } = useSmartTooltipDelay()
const activePanel = ref<ActivePanel>('none')
@@ -2,18 +2,16 @@
<aside class="absolute top-[3.75rem] z-20" :style="dynamicStyles">
<ViewerControlsButtonGroup direction="vertical">
<ViewerControlsButtonToggle
v-tippy="{
content: getShortcutDisplayText(shortcuts.ZoomExtentsOrSelection),
placement: 'left'
}"
v-tippy="
getTooltipProps(getShortcutDisplayText(shortcuts.ZoomExtentsOrSelection), {
placement: 'left'
})
"
icon="IconViewerZoom"
@click="trackAndzoomExtentsOrSelection()"
/>
<ViewerControlsButtonToggle
v-tippy="{
content: 'Camera controls',
placement: 'left'
}"
v-tippy="getTooltipProps('Camera controls', { placement: 'left' })"
icon="IconViewerCameraControls"
:active="activePanel === 'cameraControls'"
@click="toggleActivePanel('cameraControls')"
@@ -47,6 +45,7 @@ const props = withDefaults(defineProps<Props>(), {
const { zoomExtentsOrSelection } = useCameraUtilities()
const { registerShortcuts, getShortcutDisplayText, shortcuts } = useViewerShortcuts()
const mixpanel = useMixpanel()
const { getTooltipProps } = useSmartTooltipDelay()
const activePanel = ref<ActivePanel>('none')
const menuContainer = ref<Nullable<HTMLElement>>(null)
@@ -47,7 +47,7 @@
<ViewerButtonGroupButton
v-for="option in measurementTypeOptions"
:key="option.value"
v-tippy="option.title"
v-tippy="getTooltipProps(option.title)"
class="size-8"
:is-active="measurementOptions.type === option.value"
@click="updateMeasurementsType(option)"
@@ -90,6 +90,8 @@ const { measurementOptions, setMeasurementOptions, clearMeasurements } =
const showSettings = ref(false)
const { getTooltipProps } = useSmartTooltipDelay()
const updateMeasurementsType = (selectedOption: MeasurementTypeOption) => {
setMeasurementOptions({
...measurementOptions.value,
@@ -67,7 +67,7 @@
<ViewerButtonGroupButton
v-for="shortcut in viewModeShortcuts"
:key="shortcut.name"
v-tippy="shortcut.description"
v-tippy="getTooltipProps(getShortcutDisplayText(shortcut))"
:is-active="isActiveMode(shortcut.viewMode)"
@click="handleViewModeChange(shortcut.viewMode)"
>
@@ -109,8 +109,9 @@ const {
setEdgesColor,
edgesColor
} = useViewModeUtilities()
const { registerShortcuts } = useViewerShortcuts()
const { registerShortcuts, getShortcutDisplayText } = useViewerShortcuts()
const { isLightTheme } = useTheme()
const { getTooltipProps } = useSmartTooltipDelay()
const showSettings = ref(false)
@@ -126,10 +127,6 @@ const isActiveMode = (mode: ViewMode) => mode === currentViewMode.value
const viewModeShortcuts = Object.values(ViewModeShortcuts)
const emit = defineEmits<{
(e: 'force-close-others'): void
}>()
const edgesColorOptions = computed(() => [
isLightTheme.value || currentViewMode.value !== ViewMode.PEN ? 0x1a1a1a : 0xffffff, // black or white
0x3b82f6, // blue-500
@@ -143,9 +140,6 @@ const handleViewModeChange = (mode: ViewMode, isShortcut = false) => {
setViewMode(mode)
if (isShortcut) {
if (!open.value) {
emit('force-close-others')
}
open.value = true
}
}
@@ -0,0 +1,57 @@
import type { MaybeNullOrUndefined } from '@speckle/shared'
/**
* Smart tooltip delay composable
*
* Provides sophisticated tooltip behavior where:
* - 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)
*/
export function useSmartTooltipDelay() {
const initialDelay = 1000
const resetAfter = 3000
const hasShownAny = ref(false)
const resetTimer = ref<NodeJS.Timeout | null>(null)
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)
}
resetTimer.value = setTimeout(() => {
hasShownAny.value = false
}, resetAfter)
},
...additionalProps
})
const cleanup = () => {
if (resetTimer.value) {
clearTimeout(resetTimer.value)
resetTimer.value = null
}
}
const reset = () => {
cleanup()
hasShownAny.value = false
}
onUnmounted(() => {
cleanup()
})
return {
getTooltipProps,
reset
}
}