Files
nocodb/packages/nc-gui/store/ui/tooltip.ts
2025-06-25 15:47:33 +00:00

123 lines
2.9 KiB
TypeScript

import { isBoxHovered } from '../../components/smartsheet/grid/canvas/utils/canvas'
import type { RenderRectangleProps } from '../../components/smartsheet/grid/canvas/utils/types'
export interface TooltipPosition {
x: number
y: number
}
export interface TooltipInfo {
text: string
position: TooltipPosition
theme?: 'light' | 'dark'
}
export type TooltipTextType = string | VNode
export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right'
export const useTooltipStore = defineStore('tooltip', () => {
const placement = ref<TooltipPlacement>('top')
const tooltipText = ref<TooltipTextType>('')
const targetRect = ref<Omit<DOMRect, 'toJSON'>>({ x: 0, y: 0, top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 })
const hideTooltip = () => {
tooltipText.value = ''
}
const targetReference = computed(() => {
const rect = targetRect.value
return {
getBoundingClientRect() {
return rect
},
}
})
function showTooltip({
text,
rect,
placement: _placement = 'top',
}: {
text: TooltipTextType
/**
* Todo: Show tooltip at mouse position if showAtMousePosition is true & targetWidth is provided
*/
rect: RenderRectangleProps & { showAtMousePosition?: boolean }
mousePosition: {
x: number
y: number
}
placement?: TooltipPlacement
}) {
if (!text) return
let canvasX = 0
let canvasY = 0
const canvasRect = document.querySelector('canvas')?.getBoundingClientRect()
if (canvasRect) {
canvasX = canvasRect.x
canvasY = canvasRect.y
}
rect.x += canvasX
rect.y += canvasY
tooltipText.value = text
placement.value = _placement
targetRect.value = {
x: rect.x,
y: rect.y,
left: rect.x,
top: rect.y,
right: rect.x + (rect.targetWidth || rect.width),
bottom: rect.y + rect.height,
width: rect.targetWidth || rect.width,
height: rect.height,
}
}
function tryShowTooltip({
text,
description,
rect,
mousePosition,
placement,
}: {
text: TooltipTextType
description?: string
rect?: RenderRectangleProps & { showAtMousePosition?: boolean }
mousePosition: {
x: number
y: number
}
placement?: TooltipPlacement
}) {
if (!rect || !isBoxHovered(rect, mousePosition) || !text) return false
const tooltipWithDescription =
ncIsString(text) && description
? h('div', { class: 'flex flex-col gap-1' }, [
h('div', { class: 'text-captionBold' }, text),
h('div', { class: 'text-captionSm' }, description),
])
: text
showTooltip({ text: tooltipWithDescription, rect, mousePosition, placement })
return true
}
return {
targetReference,
tooltipText,
placement,
tryShowTooltip,
showTooltip,
hideTooltip,
}
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useTooltipStore as any, import.meta.hot))
}