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/public_html/sitepacket.com/src/editor/src/components/context-menu.svelte
<script>
  import * as consts from 'Editor/scripts/consts'
  import * as utilities from 'Editor/scripts/utilities'
  import { getters, setters } from 'Editor/store'
  import { selected, clipboard, hiddenTooltips, hidden, zoom, minZoom, maxZoom } from 'Editor/store/ui'
  import Menu from 'Editor/components/UI/menu/menu'
  import MenuOption from 'Editor/components/UI/menu/menu-option'
  import MenuOptionGroup from 'Editor/components/UI/menu/menu-option-group'
  import { onDestroy, onMount } from 'svelte'

  let menu
  let menuInCanvas = false
  let menuInObjectList = false
  let mouseCoords = { x: 0, y: 0 }

  $: canCopyObjects = $selected.length > 0 ? true : false
  $: canPasteObjects = $clipboard.type === consts.CLIPBOARD_TYPE_OBJECTS
  $: canGroupObjects = $selected.length > 1 ? true : false
  $: canShowObjects = $selected.length > 0 && $selected.filter((obj) => $hidden.includes(obj)).length > 0 ? true : false
  $: canHideObjects = $selected.length > 0 && $selected.filter((obj) => !$hidden.includes(obj)).length > 0 ? true : false

  $: canCopyStyles = $selected.length === 1 ? true : false
  $: canPasteStyles = $selected.length > 0 && $clipboard.type === consts.CLIPBOARD_TYPE_STYLES ? true : false

  $: canCopyTooltipStyles = $selected.length === 1 ? true : false
  $: canCopyTooltipContent = $selected.length === 1 ? true : false
  $: canPasteTooltipStyles = $selected.length > 0 && $clipboard.type === consts.CLIPBOARD_TYPE_TOOLTIP_STYLES ? true : false
  $: canPasteTooltipContent = $selected.length > 0 && $clipboard.type === consts.CLIPBOARD_TYPE_TOOLTIP_CONTENT ? true : false
  $: canShowTooltip = $selected.length > 0 && $selected.filter((obj) => $hiddenTooltips.includes(obj)).length > 0 ? true : false
  $: canHideTooltip = $selected.length > 0 && $selected.filter((obj) => !$hiddenTooltips.includes(obj)).length > 0 ? true : false

  $: canDelete = $selected.length > 0 && menuInCanvas ? true : false

  $: canZoomIn = $zoom < $maxZoom && menuInCanvas ? true : false
  $: canZoomOut = $zoom > $minZoom && menuInCanvas ? true : false

  let canDeletePoint = false
  let pointToDeleteIndex = null

  onMount(() => {
    createEvents()
  })
  onDestroy(() => {
    removeEvents()
  })

  // Context menu actions
  function createEvents() {
    document.addEventListener('contextmenu', handleMouseup)
  }
  function removeEvents() {
    document.removeEventListener('contextmenu', handleMouseup)
  }
  function handleMouseup(e) {
    mouseCoords = { x: e.pageX, y: e.pageY }
    menuInCanvas = false
    menuInObjectList = false
    canDeletePoint = false

    // Object list
    if (e.target.closest('[data-object-list-item-id')) {
      let id = e.target.closest('[data-object-list-item-id').dataset.objectListItemId
      if (!$selected.includes(id)) setters.setSelection([id])
      menu.show(e.pageX, e.pageY)
      menuInObjectList = true
      e.preventDefault()
      return false
    }

    // Poly point
    if (e.target.dataset.selectionControlType === 'point') {
      menu.show(e.pageX, e.pageY)
      canDeletePoint = true
      pointToDeleteIndex = e.target.dataset.pointIndex
      e.preventDefault()
      return false
    }

    // Selected item in canvas
    if (e.target.closest('#selection')) {
      menu.show(e.pageX, e.pageY)
      menuInCanvas = true
      e.preventDefault()
      return false
    }

    // Non-selected item in canvas
    if (e.target.dataset.canvasObjectId || e.target.closest('[data-canvas-object-id')) {
      let id = e.target.dataset.canvasObjectId || e.target.closest('[data-canvas-object-id')?.dataset.canvasObjectId
      if (!$selected.includes(id)) setters.setSelection([id])
      menu.show(e.pageX, e.pageY)
      menuInCanvas = true
      e.preventDefault()
      return false
    }

    // In canvas
    if (e.target.dataset.objectType === consts.CANVAS_OBJECT_ROOT || e.target.closest('[data-object-type]')?.dataset.objectType === consts.CANVAS_OBJECT_ROOT) {
      setters.setSelection([])
      menu.toggle(e.pageX, e.pageY)
      menuInCanvas = true
      e.preventDefault()
      return false
    }

    // In object list
    if (e.target.dataset.uiObjectType === consts.UI_OBJECT_LIST || e.target.closest('[data-ui-object-type]')?.dataset.uiObjectType === consts.UI_OBJECT_LIST) {
      setters.setSelection([])
      if ($clipboard.type === consts.CLIPBOARD_TYPE_OBJECTS) {
        menu.show(e.pageX, e.pageY)
      } else {
        menu.hide()
      }
      menuInObjectList = true
      e.preventDefault()
      return false
    }

    menu.hide()
  }
  function copyObjects() {
    let value = []

    for (let id of $selected) {
      value.push(getters.getObject(id))
    }

    $clipboard = {
      type: consts.CLIPBOARD_TYPE_OBJECTS,
      value,
    }
  }
  function pasteObjects() {
    if ($clipboard.type !== consts.CLIPBOARD_TYPE_OBJECTS) return

    let objects = []
    for (let obj of $clipboard.value) {
      let clone = structuredClone(obj)
      setNewObjectId(clone)
      objects.push(clone)
    }

    if (menuInCanvas) {
      // Insert the clipboard objects at the current mouse coords
      // Apply offset to each object to preserve their relative distances
      let canvasRect = document.querySelector(`[data-object-type="${consts.CANVAS_OBJECT_ROOT}"]`).getBoundingClientRect()
      let originalObjectsRect = utilities.calcBoundingRectForObjects($clipboard.value, canvasRect)

      let destination = {
        x: ((mouseCoords.x - canvasRect.x) / canvasRect.width) * 100,
        y: ((mouseCoords.y - canvasRect.y) / canvasRect.height) * 100,
      }

      let offset = {
        x: destination.x - originalObjectsRect.x,
        y: destination.y - originalObjectsRect.y,
      }

      applyOffsetToObjects(objects, offset)

      setters.insertObjects({ objects })
    }

    if (menuInObjectList) {
      if (getters.getObject($clipboard.value[0].id).type === consts.OBJECT_ARTBOARD) {
        // If there is a selected object
        // and it's an artboard
        // insert it after the last arboard
        setters.insertObjects({ objects, parentId: null, index: getters.getArtboards().length })
      } else if ($selected.length === 1 && getters.getObject($selected[0]).type === consts.OBJECT_GROUP) {
        // If there is a selected object
        // and it's a group
        // insert the clipboard as the first child
        setters.insertObjects({ objects, parentId: $selected[0] })
      } else if ($selected.length === 1) {
        // If there is a selected object
        // insert clipboard at its parent/index
        getters.getParent($selected[0], (parent, index) => {
          setters.insertObjects({ objects, parentId: parent.id, index })
        })
      } else {
        // If there is no selected object
        // the user must have clicked below all objects
        // which always results in the last artboard
        let artboards = getters.getArtboards()
        let lastArtboardId = artboards[artboards.length - 1].id
        setters.insertObjects({ objects, parentId: lastArtboardId })
      }
    }
  }
  function showObjects() {
    $hidden = $hidden.filter((obj) => !$selected.includes(obj))
  }
  function hideObjects() {
    for (let id of $selected) {
      if (!$hidden.includes(id)) $hidden.push(id)
    }

    $hidden = $hidden
  }
  function groupObjects() {
    // The object-list can handles this best
    // because it involves sorting and flattening
    let e = new Event(consts.EVENT_GROUP_ITEMS)
    document.dispatchEvent(e)
  }
  function copyStyles() {
    let obj = getters.getObject($selected[0])
    $clipboard = {
      type: consts.CLIPBOARD_TYPE_STYLES,
      value: {
        default_style: obj.default_style,
        mouseover_style: obj.mouseover_style,
      },
    }
  }
  function pasteStyles() {
    if ($clipboard.type !== consts.CLIPBOARD_TYPE_STYLES) return

    let values = {}
    for (let id of $selected) {
      values[id] = {
        default_style: utilities.deepExtend({}, $clipboard.value.default_style),
        mouseover_style: utilities.deepExtend({}, $clipboard.value.mouseover_style),
      }
    }
    setters.setObjectValues(values)
  }
  function copyTooltipStyles() {
    let obj = getters.getObject($selected[0])
    $clipboard = {
      type: consts.CLIPBOARD_TYPE_TOOLTIP_STYLES,
      value: {
        tooltip_style: obj.tooltip_style,
      },
    }
  }
  function copyTooltipContent() {
    let obj = getters.getObject($selected[0])
    $clipboard = {
      type: consts.CLIPBOARD_TYPE_TOOLTIP_CONTENT,
      value: {
        tooltip_content: obj.tooltip_content,
      },
    }
  }
  function pasteTooltipStyles() {
    if ($clipboard.type !== consts.CLIPBOARD_TYPE_TOOLTIP_STYLES) return

    let values = {}
    for (let id of $selected) {
      values[id] = {
        tooltip_style: $clipboard.value.tooltip_style,
      }
    }

    setters.setObjectValues(values)
  }
  function pasteTooltipContent() {
    if ($clipboard.type !== consts.CLIPBOARD_TYPE_TOOLTIP_CONTENT) return

    let values = {}
    for (let id of $selected) {
      values[id] = {
        tooltip_content: $clipboard.value.tooltip_content,
      }
    }

    setters.setObjectValues(values)
  }
  function showTooltip() {
    for (let id of $selected) {
      if ($hiddenTooltips.includes(id)) $hiddenTooltips = $hiddenTooltips.filter((obj) => obj !== id)
    }
  }
  function hideTooltip() {
    for (let id of $selected) {
      if (!$hiddenTooltips.includes(id)) $hiddenTooltips.push(id)
    }

    $hidden = $hidden
  }
  function deleteObjects() {
    if ($selected.length === 0) return
    let e = new Event(consts.EVENT_DELETE_ITEMS)
    document.dispatchEvent(e)
  }
  function zoomIn() {
    let e = new Event(consts.EVENT_CONTEXT_ZOOM_IN)
    e.detail = { ...mouseCoords }
    document.dispatchEvent(e)
  }
  function zoomOut() {
    let e = new Event(consts.EVENT_CONTEXT_ZOOM_OUT)
    e.detail = { ...mouseCoords }
    document.dispatchEvent(e)
  }
  function deletePoint() {
    console.log(pointToDeleteIndex)

    let values = {}
    for (let id of $selected) {
      values[id] = {
        points: getters.getObject(id).points.filter((point, index) => index !== parseInt(pointToDeleteIndex)),
      }
    }
    setters.setObjectValues(values)
  }

  // Utilities
  function setNewObjectId(obj) {
    obj.id = utilities.uuidv4()

    if (obj.type === consts.OBJECT_GROUP || obj.type === consts.OBJECT_ARTBOARD) {
      for (let child of obj.children) {
        setNewObjectId(child)
      }
    }
  }
  function applyOffsetToObjects(objects, offset) {
    for (let obj of objects) {
      if (obj.type === consts.OBJECT_GROUP) {
        applyOffsetToObjects(obj.children, offset)
      } else {
        obj.x += offset.x
        obj.y += offset.y
      }
    }
  }
</script>

<Menu bind:this={menu} name="context menu">
  {#if canDeletePoint}
    <MenuOptionGroup>
      <MenuOption small onClick={deletePoint} icon="fa-solid fa-trash" title="Delete Point" />
    </MenuOptionGroup>
  {/if}

  {#if canCopyObjects || canPasteObjects || canGroupObjects || canShowObjects || canHideObjects}
    <MenuOptionGroup>
      {#if canCopyObjects}
        <MenuOption small onClick={copyObjects} icon="fa-solid fa-copy" title="Copy" />
      {/if}
      {#if canPasteObjects}
        <MenuOption small onClick={pasteObjects} icon="fa-solid fa-paste" title="Paste Here" />
      {/if}
      {#if canGroupObjects}
        <MenuOption small onClick={groupObjects} icon="fa-regular fa-object-group" title="Group" />
      {/if}
      {#if canShowObjects}
        <MenuOption small onClick={showObjects} icon="fa-solid fa-eye" title="Show" />
      {/if}
      {#if canHideObjects}
        <MenuOption small onClick={hideObjects} icon="fa-solid fa-eye-slash" title="Hide" />
      {/if}
    </MenuOptionGroup>
  {/if}

  {#if canCopyStyles || canPasteStyles}
    <MenuOptionGroup>
      {#if canCopyStyles}
        <MenuOption small onClick={copyStyles} icon="fa-solid fa-copy" title="Copy Styles" />
      {/if}
      {#if canPasteStyles}
        <MenuOption small onClick={pasteStyles} icon="fa-solid fa-paste" title="Paste Styles" />
      {/if}
    </MenuOptionGroup>
  {/if}

  {#if canCopyTooltipStyles || canCopyTooltipContent || canPasteTooltipStyles || canPasteTooltipContent || canShowTooltip || canHideTooltip}
    <MenuOptionGroup>
      {#if canCopyTooltipStyles}
        <MenuOption small onClick={copyTooltipStyles} icon="fa-solid fa-copy" title="Copy Tooltip Styles" />
      {/if}
      {#if canCopyTooltipContent}
        <MenuOption small onClick={copyTooltipContent} icon="fa-solid fa-copy" title="Copy Tooltip Content" />
      {/if}
      {#if canPasteTooltipStyles}
        <MenuOption small onClick={pasteTooltipStyles} icon="fa-solid fa-paste" title="Paste Tooltip Styles" />
      {/if}
      {#if canPasteTooltipContent}
        <MenuOption small onClick={pasteTooltipContent} icon="fa-solid fa-paste" title="Paste Tooltip Content" />
      {/if}
      {#if canShowTooltip}
        <MenuOption small onClick={showTooltip} icon="fa-solid fa-eye" title="Show Tooltip" />
      {/if}
      {#if canHideTooltip}
        <MenuOption small onClick={hideTooltip} icon="fa-solid fa-eye-slash" title="Hide Tooltip" />
      {/if}
    </MenuOptionGroup>
  {/if}

  {#if canDelete}
    <MenuOptionGroup>
      {#if canDelete}
        <MenuOption small onClick={deleteObjects} icon="fa-solid fa-trash" title="Delete" />
      {/if}
    </MenuOptionGroup>
  {/if}

  {#if canZoomIn || canZoomOut}
    <MenuOptionGroup>
      {#if canZoomIn}
        <MenuOption small onClick={zoomIn} icon="fa-solid fa-magnifying-glass-plus" title="Zoom In" />
      {/if}
      {#if canZoomOut}
        <MenuOption small onClick={zoomOut} icon="fa-solid fa-magnifying-glass-minus" title="Zoom Out" />
      {/if}
    </MenuOptionGroup>
  {/if}
</Menu>