Source: app/directives/tooltip-popover.js

/*
 * Copyright 2025 The Ray Optics Simulation authors and contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import * as bootstrap from 'bootstrap'

/**
 * Vue directive for handling Bootstrap tooltips and popovers
 * Usage:
 * - For tooltips: v-tooltip-popover="{ title: 'Tooltip text' }"
 * - For popovers: v-tooltip-popover:popover="{ title: 'Title', content: 'Content', popoverImage: 'image.svg' }"
 */
export const vTooltipPopover = {
  mounted(el, binding) {
    const options = binding.value || {}
    const isPopover = binding.arg === 'popover'
    
    // Store initial options for comparison in updates
    el._lastOptions = { ...options }
    el._lastIsPopover = isPopover
    
    if (isPopover) {
      let content = options.content || ''
      if (options.popoverImage) {
        content = `<img src="../img/${options.popoverImage}" class="popover-image" id="dynamic-popover-image">${content}`
      }

      const popoverConfig = {
        title: options.title || '',
        content,
        trigger: options.trigger || 'hover',
        html: options.html !== false
      }
      
      if (options.placement) {
        popoverConfig.placement = options.placement
      }
      if (options.offset) {
        popoverConfig.offset = options.offset
      }

      const popover = new bootstrap.Popover(el, popoverConfig)

      if (options.popoverImage) {
        el.addEventListener('inserted.bs.popover', () => {
          const imgElement = document.querySelectorAll('#dynamic-popover-image')
          if (imgElement.length > 0) {
            imgElement[imgElement.length - 1].addEventListener('load', () => {
              popover.update()
            })
          }
        })
      }

      // Store popover instance for cleanup
      el._popover = popover
    } else if (options.title) { // Only create tooltip if title is provided
      const tooltipConfig = {
        title: options.title,
        trigger: options.trigger || 'hover',
        placement: options.placement || 'bottom'
      }
      
      if (options.offset) {
        tooltipConfig.offset = options.offset
      }

      const tooltip = new bootstrap.Tooltip(el, tooltipConfig)

      // Store tooltip instance for cleanup
      el._tooltip = tooltip
    }
  },
  
  updated(el, binding) {
    const options = binding.value || {}
    const isPopover = binding.arg === 'popover'
    const oldOptions = el._lastOptions || {}
    const oldIsPopover = el._lastIsPopover
    
    // Store new options and arg for future comparison
    el._lastOptions = { ...options }
    el._lastIsPopover = isPopover
    
    // Only update if options or type have changed
    if (JSON.stringify(options) === JSON.stringify(oldOptions) && isPopover === oldIsPopover) {
      return
    }

    // Clean up any existing instances
    if (el._popover) {
      el._popover.dispose()
      el._popover = null
    }
    if (el._tooltip) {
      el._tooltip.dispose()
      el._tooltip = null
    }
    
    if (isPopover) {
      let content = options.content || ''
      if (options.popoverImage) {
        content = `<img src="../img/${options.popoverImage}" class="popover-image" id="dynamic-popover-image">${content}`
      }

      const popoverConfig = {
        title: options.title || '',
        content,
        trigger: options.trigger || 'hover',
        html: options.html !== false
      }
      
      if (options.placement) {
        popoverConfig.placement = options.placement
      }
      if (options.offset) {
        popoverConfig.offset = options.offset
      }

      el._popover = new bootstrap.Popover(el, popoverConfig)

      if (options.popoverImage) {
        el.addEventListener('inserted.bs.popover', () => {
          const imgElement = document.querySelectorAll('#dynamic-popover-image')
          if (imgElement.length > 0) {
            imgElement[imgElement.length - 1].addEventListener('load', () => {
              el._popover.update()
            })
          }
        })
      }
    } else if (options.title) { // Only create tooltip if title is provided
      const tooltipConfig = {
        title: options.title,
        trigger: options.trigger || 'hover',
        placement: options.placement || 'bottom'
      }
      
      if (options.offset) {
        tooltipConfig.offset = options.offset
      }

      el._tooltip = new bootstrap.Tooltip(el, tooltipConfig)
    }
  },
  
  unmounted(el) {
    if (el._popover) {
      el._popover.dispose()
      el._popover = null
    }
    if (el._tooltip) {
      el._tooltip.dispose()
      el._tooltip = null
    }
  }
}