vuestic-ui
Version:
Vue 3 UI Framework
1 lines • 5.99 kB
Source Map (JSON)
{"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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"}