{"version":3,"file":"useStickyTableHeaders.mjs","sources":["../../../../../src/composables/useStickyTableHeaders/useStickyTableHeaders.ts"],"sourcesContent":["import { Ref, computed, watchEffect, type ComponentPublicInstance } from 'vue'\nimport { useEvent } from '../useEvent'\nimport { useElementBackground } from '../useElementBackground'\n\nconst syncTh = (thead1: HTMLElement, thead2: HTMLElement) => {\n  const ths1 = thead1.querySelectorAll('th')\n  const ths2 = thead2.querySelectorAll('th')\n\n  ths1.forEach((th, index) => {\n    const th2 = ths2[index]\n\n    th2.style.width = `${th.getBoundingClientRect().width}px`\n  })\n}\n\nconst recursiveGetOffset = (el: HTMLElement, offset = 0): number => {\n  if (!el) { return offset }\n\n  return recursiveGetOffset(el.offsetParent as HTMLElement, offset + el.offsetTop)\n}\n\n/**\n * Creates virtual table headers, that are sticky when you scroll the table with position fixed.\n * When standard table headers are visible, the virtual headers are hidden.\n */\nexport const useStickyTableHeaders = (tableEl: Ref<HTMLTableElement | ComponentPublicInstance | undefined | null>, offset = 0) => {\n  let mutationObserver: MutationObserver | null = null\n  let headClone: HTMLElement | null = null\n  let head: HTMLElement | null = null\n  let table: HTMLTableElement | null = null\n\n  let headOffset = 0\n  let hasTransform = false\n  let tableHeight = 0\n  let headHeight = 0\n\n  const htmlTable = computed(() => {\n    if (!tableEl.value) { return null }\n\n    const el = '$el' in tableEl.value ? tableEl.value.$el as HTMLElement : tableEl.value\n\n    return el.tagName === 'TABLE' ? el as HTMLTableElement : el.querySelector('table') as HTMLTableElement\n  })\n\n  const htmlTableHead = computed(() => {\n    if (!htmlTable.value) { return null }\n\n    return htmlTable.value.querySelector('thead') as HTMLElement\n  })\n\n  const bg = useElementBackground(htmlTableHead)\n\n  watchEffect(() => {\n    if (!tableEl.value) { return }\n\n    const rootEl = htmlTable.value\n\n    if (!rootEl) { return }\n\n    table = htmlTable.value\n    head = htmlTableHead.value\n\n    if (!head) { return }\n\n    table.style.position = 'relative'\n\n    headClone = head.cloneNode(true) as HTMLElement\n\n    headClone.style.position = 'fixed'\n    headClone.style.top = '0px'\n    headClone.style.width = `${table.clientWidth}px`\n    headClone.style.zIndex = '1'\n    headClone.style.backgroundColor = bg.value\n    headClone.style.display = 'none'\n\n    table.appendChild(headClone)\n\n    syncTh(head, headClone)\n    headOffset = recursiveGetOffset(head)\n    hasTransform = window.getComputedStyle(table).transform !== 'none'\n    tableHeight = table.clientHeight\n    headHeight = head.clientHeight\n\n    mutationObserver = new MutationObserver(() => {\n      if (!headClone) { return }\n      if (!head) { return }\n      if (!table) { return }\n\n      headClone.style.width = `${table.clientWidth}px`\n      syncTh(head, headClone)\n      headOffset = recursiveGetOffset(head)\n      tableHeight = table.clientHeight\n      headHeight = head.clientHeight\n    })\n\n    mutationObserver.observe(table, {\n      childList: true,\n      subtree: true,\n      attributes: true,\n    })\n  })\n\n  // TODO: Maybe optimize it with IntersectionObserver\n  useEvent('scroll', () => {\n    if (!headClone) { return }\n\n    const y = window.scrollY\n\n    if (y > headOffset - offset && y < headOffset + tableHeight - offset - headHeight) {\n      headClone.style.display = 'block'\n\n      // Somehow transform prevents fixed positioning.\n      if (hasTransform) {\n        headClone.style.top = `${y - headOffset + offset - 2}px`\n      } else {\n        headClone.style.top = `${offset}px`\n      }\n    } else {\n      headClone.style.top = '0px'\n      headClone.style.display = 'none'\n    }\n  })\n}\n"],"names":[],"mappings":"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"}