HEX
Server: Apache
System: Linux p3plzcpnl506847.prod.phx3.secureserver.net 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: slfopp7cb1df (5698090)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: //home/slfopp7cb1df/www/sitepacket.com/src/editor/src/components/canvas/tooltip/tooltip.svelte
<script>
  import { onMount, onDestroy } from 'svelte'
  import { store, getters } from 'Editor/store'
  import { selected, hiddenTooltips, hidden, activeArtboard, mapSettingsVisible, objectSettingsVisible, objectListVisible } from 'Editor/store/ui'
  import * as consts from 'Editor/scripts/consts'
  import * as utilities from 'Editor/scripts/utilities'
  import ContentBuilder from 'Editor/components/canvas/tooltip/content-builder/content-builder'
  import Toolbar from 'Editor/components/canvas/tooltip/toolbar'

  let subscribers = []
  let root
  let arrow
  let settings
  let css = ''
  let arrowClass
  let arrowCss
  let visible = false
  let contentBuilder

  subscribers.push(
    selected.subscribe(() => {
      redraw()
    })
  )
  subscribers.push(
    activeArtboard.subscribe(() => {
      redraw()
    })
  )
  subscribers.push(
    store.subscribe(() => {
      redraw()
    })
  )
  subscribers.push(
    hidden.subscribe(() => {
      redraw()
    })
  )
  subscribers.push(
    hiddenTooltips.subscribe(() => {
      redraw()
    })
  )
  subscribers.push(
    objectListVisible.subscribe(() => {
      setTimeout(() => {
        redraw()
      }, 200)
    })
  )
  subscribers.push(
    objectSettingsVisible.subscribe(() => {
      setTimeout(() => {
        redraw()
      }, 200)
    })
  )
  subscribers.push(
    mapSettingsVisible.subscribe(() => {
      setTimeout(() => {
        redraw()
      }, 500)
    })
  )

  onMount(() => {
    redraw()
    createEvents()
  })
  onDestroy(() => {
    subscribers.forEach((unsub) => unsub())
    subscribers = []
    removeEvents()
  })

  function createEvents() {
    document.addEventListener(consts.EVENT_FORM_CONTROL_UPDATE, handleFormControlUpdate)
    document.addEventListener(consts.EVENT_OBJECT_MOVE, handleObjectMove)
    document.addEventListener(consts.EVENT_OBJECT_SCALE, handleObjectScale)
    document.addEventListener(consts.EVENT_CANVAS_DRAG, handleCanvasDrag)
    document.addEventListener(consts.EVENT_CANVAS_ZOOM_START, handleCanvasZoomStart)
    document.addEventListener(consts.EVENT_CANVAS_ZOOM_END, handleCanvasZoomEnd)
    window.addEventListener('resize', handleWindowResize)
  }
  function removeEvents() {
    document.removeEventListener(consts.EVENT_FORM_CONTROL_UPDATE, handleFormControlUpdate)
    document.removeEventListener(consts.EVENT_OBJECT_MOVE, handleObjectMove)
    document.removeEventListener(consts.EVENT_OBJECT_SCALE, handleObjectScale)
    document.removeEventListener(consts.EVENT_CANVAS_DRAG, handleCanvasDrag)
    document.removeEventListener(consts.EVENT_CANVAS_ZOOM_START, handleCanvasZoomStart)
    document.removeEventListener(consts.EVENT_CANVAS_ZOOM_END, handleCanvasZoomEnd)
    window.removeEventListener('resize', handleWindowResize)
  }

  function handleFormControlUpdate(e) {
    if (e.detail.group !== 'selected') return
    if (!visible) return

    if (['x', 'y', 'width', 'height'].includes(e.detail.prop)) {
      settings[e.detail.prop] = e.detail.value
      setPosition()
    }

    if (e.detail.prop === 'default_style.icon_size') {
      settings.default_style.icon_size = e.detail.value
      setPosition()
    }

    if (e.detail.prop === 'tooltip_style.width') {
      settings.tooltip_style.width = e.detail.value
      root.style.width = e.detail.value + 'px'
      root.dataset.impMeasuredWidth = e.detail.value
      setPosition()
    }

    if (e.detail.prop === 'tooltip_style.offset_x') {
      settings.tooltip_style.offset_x = e.detail.value
      setPosition()
    }

    if (e.detail.prop === 'tooltip_style.offset_y') {
      settings.tooltip_style.offset_y = e.detail.value
      setPosition()
    }

    if (e.detail.prop === 'tooltip_style.padding') {
      settings.tooltip_style.padding = e.detail.value
      root.style.padding = e.detail.value + 'px'
      root.dataset.impMeasuredWidth = root.getBoundingClientRect().width
      root.dataset.impMeasuredHeight = root.getBoundingClientRect().height
      setPosition()
    }

    if (e.detail.prop === 'tooltip_style.background_color') {
      settings.tooltip_style.background_color = e.detail.value
      let colorBg = utilities.hexToRgb(settings.tooltip_style.background_color)
      root.style.backgroundColor = `rgba(${colorBg.r}, ${colorBg.g}, ${colorBg.b}, ${settings.tooltip_style.background_opacity})`
      updateArrow()
    }

    if (e.detail.prop === 'tooltip_style.background_opacity') {
      settings.tooltip_style.background_opacity = e.detail.value
      let colorBg = utilities.hexToRgb(settings.tooltip_style.background_color)
      root.style.backgroundColor = `rgba(${colorBg.r}, ${colorBg.g}, ${colorBg.b}, ${settings.tooltip_style.background_opacity})`
      updateArrow()
    }

    if (e.detail.prop === 'tooltip_style.border_radius') {
      root.style.borderRadius = e.detail.value + 'px'
    }
  }
  function handleObjectMove(e) {
    setPosition(e.detail.move.x, e.detail.move.y)
  }
  function handleObjectScale(e) {
    setPosition(e.detail.move.x, e.detail.move.y, e.detail.scale.x, e.detail.scale.y)
  }
  function handleCanvasDrag() {
    setPosition()
  }
  function handleCanvasZoomStart() {
    visible = false
  }
  function handleCanvasZoomEnd() {
    redraw()
  }
  function handleWindowResize() {
    setPosition()
  }

  function redraw() {
    if (!root) return
    updateVisibility()
    if (!visible) return

    if (contentBuilder) contentBuilder.generateModel()
    settings = structuredClone(getters.getObject($selected[0]))

    updateCSS()
    updateArrow()

    requestAnimationFrame(() => {
      root.dataset.impMeasuredWidth = root.getBoundingClientRect().width
      root.dataset.impMeasuredHeight = root.getBoundingClientRect().height
      setPosition()
    })
  }
  function updateVisibility() {
    visible = false

    // Only one object must be selected and it must exist
    if ($selected.length !== 1 || !getters.getObject($selected[0])) return

    // Object must NOT be an artboard or text
    if (getters.getObject($selected[0]).type === consts.OBJECT_ARTBOARD || getters.getObject($selected[0]).type === consts.OBJECT_TEXT) return

    // Object should have its tooltip enabled
    if (!getters.getObject($selected[0]).tooltip.enable_tooltip) return

    // Object should not be hidden
    if ($hidden.includes($selected[0])) return

    // Object's tooltip should not be hidden
    if ($hiddenTooltips.includes($selected[0])) return

    // If the object is a group, it should have events and tooltips enabled
    if (getters.getObject($selected[0]).type === consts.OBJECT_GROUP && !getters.getObject($selected[0]).single_object) return

    // Object should be child of the active artboard
    if (!getters.isObjectChildOfActiveArtboard($selected[0])) return

    visible = true
  }
  function updateCSS() {
    let newCss = ''
    let colorBg = utilities.hexToRgb(settings.tooltip_style.background_color)

    newCss += `border-radius: ${settings.tooltip_style.border_radius}px;`
    newCss += `padding: ${settings.tooltip_style.padding}px;`
    newCss += `background: rgba(${colorBg.r}, ${colorBg.g}, ${colorBg.b}, ${settings.tooltip_style.background_opacity});`
    newCss += 'transition-property: transform; -moz-transition-property: -moz-transform; -webkit-transition-property: -webkit-transform;'

    if (!settings.tooltip_style.auto_width) {
      newCss += `width: ${settings.tooltip_style.width}px;`
    } else {
      newCss += `min-width: 200px;`
    }

    css = newCss
  }
  function updateArrow() {
    let colorBg = utilities.hexToRgb(settings.tooltip_style.background_color)

    if (settings.tooltip_style.position === 'top') {
      arrowClass = 'hs-arrow hs-arrow-bottom'
      arrowCss = `border-top-color: rgba(${colorBg.r}, ${colorBg.g}, ${colorBg.b}, ${settings.tooltip_style.background_opacity});`
    }
    if (settings.tooltip_style.position === 'bottom') {
      arrowClass = 'hs-arrow hs-arrow-top'
      arrowCss = `border-bottom-color: rgba(${colorBg.r}, ${colorBg.g}, ${colorBg.b}, ${settings.tooltip_style.background_opacity});`
    }
    if (settings.tooltip_style.position === 'left') {
      arrowClass = 'hs-arrow hs-arrow-right'
      arrowCss = `border-left-color: rgba(${colorBg.r}, ${colorBg.g}, ${colorBg.b}, ${settings.tooltip_style.background_opacity});`
    }
    if (settings.tooltip_style.position === 'right') {
      arrowClass = 'hs-arrow hs-arrow-left'
      arrowCss = `border-right-color: rgba(${colorBg.r}, ${colorBg.g}, ${colorBg.b}, ${settings.tooltip_style.background_opacity});`
    }
  }
  function setPosition(moveX = 0, moveY = 0, scaleX = 1, scaleY = 1) {
    if (!visible) return

    let distance = 20
    let canvasRect = document.querySelector(`[data-object-type="${consts.CANVAS_OBJECT_ROOT}"]`).getBoundingClientRect()
    let objectRect = getObjectRect(moveX, moveY, scaleX, scaleY)

    // Calculate and set the position
    let x, y
    if (settings.tooltip_style.position === 'left') {
      x = objectRect.x - parseInt(root.dataset.impMeasuredWidth) - distance
      y = objectRect.y + objectRect.height / 2 - parseInt(root.dataset.impMeasuredHeight) / 2
    }
    if (settings.tooltip_style.position === 'right') {
      x = objectRect.x + objectRect.width + distance
      y = objectRect.y + objectRect.height / 2 - parseInt(root.dataset.impMeasuredHeight) / 2
    }
    if (settings.tooltip_style.position === 'top') {
      x = objectRect.x + objectRect.width / 2 - parseInt(root.dataset.impMeasuredWidth) / 2
      y = objectRect.y - parseInt(root.dataset.impMeasuredHeight) - distance
    }
    if (settings.tooltip_style.position === 'bottom') {
      x = objectRect.x + objectRect.width / 2 - parseInt(root.dataset.impMeasuredWidth) / 2
      y = objectRect.y + objectRect.height + distance
    }

    // Apply tooltip position offset from settings
    x += (settings.tooltip_style.offset_x / 100) * canvasRect.width
    y += (settings.tooltip_style.offset_y / 100) * canvasRect.height

    // Offset from canvas origin
    x += canvasRect.x
    y += canvasRect.y

    // Limit to window
    if (x < 0) x = 0
    if (y < 0) y = 0
    if (x + parseInt(root.dataset.impMeasuredWidth) > window.innerWidth - 30) x = window.innerWidth - parseInt(root.dataset.impMeasuredWidth) - 30
    if (y + parseInt(root.dataset.impMeasuredHeight) > window.innerHeight) y = window.innerHeight - parseInt(root.dataset.impMeasuredHeight)

    // Apply the position to the element
    root.style.left = x + 'px'
    root.style.top = y + 'px'
  }
  function getObjectRect(moveX = 0, moveY = 0, scaleX = 0, scaleY = 0) {
    let canvasRect = document.querySelector(`[data-object-type="${consts.CANVAS_OBJECT_ROOT}"]`).getBoundingClientRect()
    let rect = utilities.calcBoundingRectForObjects([settings], canvasRect)

    // Convert to pixels, relative to canvas
    // also apply move and scale, which are temp values from fast updating controls
    rect = {
      x: (Math.round((rect.x + moveX) * 10) / 10 / 100) * canvasRect.width,
      y: (Math.round((rect.y + moveY) * 10) / 10 / 100) * canvasRect.height,
      width: ((rect.width * scaleX) / 100) * canvasRect.width,
      height: ((rect.height * scaleY) / 100) * canvasRect.height,
    }

    return rect
  }
</script>

<div bind:this={root} style="{css} {visible ? 'display: block;' : 'display: none;'} z-index: 9999;" class="imp-tooltip fixed">
  <Toolbar />
  <div bind:this={arrow} class={arrowClass} style={arrowCss} />
  <div class="tooltip-content">
    <ContentBuilder bind:this={contentBuilder} group="selected" prop="tooltip_content" />
  </div>
</div>