| 1 |
import { nextTick, onBeforeUnmount, ref, type Ref } from 'vue' |
| 2 |
|
| 3 |
export interface UseTableHoverOptions { |
| 4 |
tableSelector?: string |
| 5 |
} |
| 6 |
|
| 7 |
const ROW_SELECTOR = '.el-table-v2__row' |
| 8 |
|
| 9 |
export function useTableHover<TId extends number | string = number>( |
| 10 |
options: UseTableHoverOptions = {}, |
| 11 |
) { |
| 12 |
const { tableSelector = '.ivyforms-table' } = options |
| 13 |
const hoveredRowId: Ref<TId | null> = ref(null) |
| 14 |
let controller: AbortController | null = null |
| 15 |
|
| 16 |
const setHoveredRow = (table: Element, rowKey: string | null) => { |
| 17 |
table.querySelectorAll(`${ROW_SELECTOR}.hovered`).forEach((el) => { |
| 18 |
if (!rowKey || el.getAttribute('rowkey') !== rowKey) el.classList.remove('hovered') |
| 19 |
}) |
| 20 |
if (!rowKey) { |
| 21 |
hoveredRowId.value = null |
| 22 |
return |
| 23 |
} |
| 24 |
const parsed = Number(rowKey) |
| 25 |
hoveredRowId.value = (Number.isNaN(parsed) ? rowKey : parsed) as TId |
| 26 |
table.querySelectorAll(`${ROW_SELECTOR}[rowkey="${CSS.escape(rowKey)}"]`).forEach((el) => { |
| 27 |
el.classList.add('hovered') |
| 28 |
}) |
| 29 |
} |
| 30 |
|
| 31 |
const syncHover = async () => { |
| 32 |
await nextTick() |
| 33 |
|
| 34 |
controller?.abort() |
| 35 |
controller = new AbortController() |
| 36 |
const { signal } = controller |
| 37 |
|
| 38 |
const table = document.querySelector(tableSelector) |
| 39 |
if (!table) return |
| 40 |
|
| 41 |
table.addEventListener( |
| 42 |
'mouseover', |
| 43 |
(event) => { |
| 44 |
const target = event.target |
| 45 |
if (!(target instanceof Element)) return |
| 46 |
const row = target.closest(ROW_SELECTOR) |
| 47 |
if (!row || !table.contains(row)) return |
| 48 |
const rowKey = row.getAttribute('rowkey') |
| 49 |
if (!rowKey) return |
| 50 |
setHoveredRow(table, rowKey) |
| 51 |
}, |
| 52 |
{ signal }, |
| 53 |
) |
| 54 |
|
| 55 |
table.addEventListener( |
| 56 |
'mouseout', |
| 57 |
(event) => { |
| 58 |
const target = event.target |
| 59 |
if (!(target instanceof Element)) return |
| 60 |
const row = target.closest(ROW_SELECTOR) |
| 61 |
if (!row) return |
| 62 |
const related = (event as MouseEvent).relatedTarget |
| 63 |
if (related instanceof Node && row.contains(related)) return |
| 64 |
setHoveredRow(table, null) |
| 65 |
}, |
| 66 |
{ signal }, |
| 67 |
) |
| 68 |
} |
| 69 |
|
| 70 |
onBeforeUnmount(() => { |
| 71 |
controller?.abort() |
| 72 |
controller = null |
| 73 |
}) |
| 74 |
|
| 75 |
return { hoveredRowId, syncHover } |
| 76 |
} |
| 77 |
|