import type { Ref } from 'vue' import type { ColumnConfig, Row } from '../types' import { computed, ref, watch, onUnmounted } from 'vue' export function useSpreadsheetColumns( localRows: Ref, getColumnConfig: () => ColumnConfig[] | undefined, ) { const visibleColumns = ref([]) const columnWidths = ref>(new Map()) const isResizing = ref(false) const resizingColumn = ref(null) const startX = ref(0) const startWidth = ref(0) const columns = computed(() => { const dataKeys = localRows.value.length ? new Set(localRows.value.flatMap(row => Object.keys(row))) : new Set() const config = getColumnConfig() const configMap = new Map((config || []).map(c => [c.key, c])) const allKeys = new Set([...dataKeys, ...(config?.map(c => c.key) || [])]) return Array.from(allKeys).map((key) => { const col = configMap.get(key) return { key, label: col?.label ?? key, locked: col?.locked ?? false, sortable: col?.sortable ?? true, format: col?.format ?? 'text', width: col?.width, fixed: col?.fixed ?? false, hidden: col?.hidden ?? false, defaultValue: col?.defaultValue, } }) }) const fixedColumns = computed(() => columns.value.filter(col => col.fixed && !col.hidden && visibleColumns.value.includes(col.key)) ) const scrollableColumns = computed(() => columns.value.filter(col => !col.fixed && !col.hidden && visibleColumns.value.includes(col.key)) ) const columnOptions = computed(() => columns.value.filter(col => !col.hidden)) watch(columns, (newColumns) => { if (newColumns.length > 0 && visibleColumns.value.length === 0) { visibleColumns.value = newColumns.filter(col => !col.hidden).map(col => col.key) } }) function isCellEditable(columnKey: string): boolean { return !(columns.value.find(col => col.key === columnKey)?.locked ?? false) } function createEmptyRow(): Row { const row: Row = {} columns.value.forEach((col) => { row[col.key] = col.defaultValue !== undefined ? col.defaultValue : col.format === 'boolean' ? false : '' }) return row } function handleResizeMove(e: MouseEvent) { if (!isResizing.value || !resizingColumn.value) { return } e.preventDefault() const newWidth = Math.max(80, startWidth.value + (e.pageX - startX.value)) columnWidths.value.set(resizingColumn.value, newWidth) } function handleResizeEnd() { isResizing.value = false resizingColumn.value = null document.removeEventListener('mousemove', handleResizeMove) document.removeEventListener('mouseup', handleResizeEnd) } function handleResizeStart(e: MouseEvent, columnKey: string) { isResizing.value = true resizingColumn.value = columnKey startX.value = e.pageX const th = (e.target as HTMLElement).closest('th') startWidth.value = columnWidths.value.get(columnKey) ?? (th?.getBoundingClientRect().width ?? 0) document.addEventListener('mousemove', handleResizeMove) document.addEventListener('mouseup', handleResizeEnd) } onUnmounted(() => { document.removeEventListener('mousemove', handleResizeMove) document.removeEventListener('mouseup', handleResizeEnd) }) return { columns, fixedColumns, scrollableColumns, columnOptions, visibleColumns, columnWidths, isCellEditable, createEmptyRow, handleResizeStart, } }