Created
June 27, 2025 14:38
-
-
Save scbj/2e5f35874a04b00cc855c43734bcdcb3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { computed, Ref, ref, watch } from 'vue' | |
| import { ColumnProps } from '@/components/jk/table-2/types' | |
| const minColumnWidth = 80 | |
| const defaultColumnWidth = '1fr' | |
| /** Checks if the object is an instance of HTMLElement */ | |
| function isHtmlElement(obj: object): obj is HTMLElement { | |
| return obj instanceof HTMLElement | |
| } | |
| /** Generate grid column width based on column properties */ | |
| function generateColumnTrackValue(column: ColumnProps) { | |
| return `minmax(${minColumnWidth}px,${column.width ?? defaultColumnWidth})` | |
| } | |
| /** This composable function provides functionality for resizing table columns. */ | |
| export function useResizableTableColumns(columns: Ref<ColumnProps[]>) { | |
| const responsiveColumnsTemplate = computed(() => | |
| columns.value.map(generateColumnTrackValue).join(' ') | |
| ) | |
| const absoluteColumnWidths = ref<Nullable<number[]>>(null) | |
| const absoluteColumnsTemplate = computed( | |
| () => absoluteColumnWidths.value?.map((width) => `${width}px`).join(' ') | |
| ) | |
| const columnsTemplate = computed( | |
| () => absoluteColumnsTemplate.value ?? responsiveColumnsTemplate.value | |
| ) | |
| const columnBeingResizedLabel = ref<Nullable<ColumnProps['label']>>(null) | |
| /** | |
| * Handle mouse down event for the resize handle of the table column. | |
| * @param {MouseEvent} event - The mouse event. | |
| * @param {Column} column - The column that will be resized. | |
| */ | |
| const onResizeHandleMouseDown = (event: MouseEvent, column: ColumnProps) => { | |
| const { target, pageX: initialMousePositionX } = event | |
| if (!target || !isHtmlElement(target)) return | |
| const header = target.closest('th') | |
| const table = target.closest('table') | |
| if (!header || !table) return | |
| const initialHeaderWidth = header.clientWidth | |
| const headers = Array.from(table.querySelectorAll('th')) | |
| const headerWidths = headers.map((th) => th.clientWidth) | |
| const headerIndex = headers.indexOf(header) | |
| const onMouseMove = (event: MouseEvent) => { | |
| const changeInPosition = event.pageX - initialMousePositionX | |
| const newWidth = Math.max( | |
| minColumnWidth, | |
| initialHeaderWidth + changeInPosition | |
| ) | |
| headerWidths.splice(headerIndex, 1, newWidth) | |
| absoluteColumnWidths.value = headerWidths | |
| } | |
| const onMouseUp = () => { | |
| columnBeingResizedLabel.value = null | |
| window.removeEventListener('mousemove', onMouseMove) | |
| window.removeEventListener('mouseup', onMouseUp) | |
| } | |
| columnBeingResizedLabel.value = column.label | |
| window.addEventListener('mousemove', onMouseMove) | |
| window.addEventListener('mouseup', onMouseUp) | |
| } | |
| /** | |
| * Watch for changes in the columns. If there are changes and the columns had absolute | |
| * widths, then they must be recalculated to preserve the sizes of the existing columns. | |
| */ | |
| watch(columns, (columns, oldColumns) => { | |
| if (!absoluteColumnWidths.value) return | |
| absoluteColumnWidths.value = columns.map((column) => { | |
| const oldIndex = oldColumns.findIndex( | |
| (oldColumn) => oldColumn.label === column.label | |
| ) | |
| return oldIndex !== -1 | |
| ? absoluteColumnWidths.value?.[oldIndex] ?? minColumnWidth | |
| : minColumnWidth | |
| }) | |
| }) | |
| return { | |
| columnBeingResizedLabel, | |
| columnsTemplate, | |
| onResizeHandleMouseDown | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment