PluginProbe
The Innovative Form Builder – IvyForms / 1.1
The Innovative Form Builder – IvyForms v1.1
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / views / _components / table / composables / useTableWidth.ts

useTableWidth.ts in The Innovative Form Builder – IvyForms 1.1, at frontend/src/views/_components/table/composables/useTableWidth.ts

105 lines 3.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { computed, ref, watch, type ComputedRef, type Ref } from 'vue'
2 import { useResizeObserver } from '@vueuse/core'
3
4 export interface WidthColumn {
5 visible?: boolean
6 showInModal?: boolean
7 width?: string | number
8 minWidth?: string | number
9 }
10
11 export interface DynamicWidthContext {
12 dynamicWidth: number | null
13 remainingWidth: number
14 totalTableWidth: number
15 }
16
17 export interface UseTableWidthOptions<T extends WidthColumn> {
18 columns: Ref<T[]> | ComputedRef<T[]>
19 isDynamic?: (col: T) => boolean
20 widthFor?: (col: T, ctx: DynamicWidthContext) => number | null
21 minWidthFilter?: (col: T) => boolean
22 }
23
24 type TableRefLike = HTMLElement | { $el?: HTMLElement | null } | null | undefined
25
26 function getNumericColumnWidth(width: WidthColumn['width']): number {
27 if (typeof width === 'number') return width
28 if (typeof width === 'string') {
29 const parsed = parseFloat(width)
30 return Number.isFinite(parsed) ? parsed : 0
31 }
32 return 0
33 }
34
35 export function useTableWidth<T extends WidthColumn>(
36 tableRef: Ref<TableRefLike>,
37 opts: UseTableWidthOptions<T>,
38 ) {
39 const tableWidth = ref(0)
40
41 const isDynamic = opts.isDynamic ?? ((col: T) => col.showInModal === true)
42 const minWidthFilter = opts.minWidthFilter ?? (() => true)
43
44 const containerEl = computed<HTMLElement | null>(() => {
45 const value = tableRef.value
46 if (!value) return null
47 if (value instanceof HTMLElement) return value.parentElement
48 const el = (value as { $el?: HTMLElement | null }).$el
49 return el?.parentElement ?? null
50 })
51
52 const measureContainerWidth = (el: HTMLElement | null) => {
53 if (el) {
54 tableWidth.value = el.clientWidth
55 }
56 }
57
58 watch(containerEl, measureContainerWidth, { flush: 'post', immediate: true })
59
60 useResizeObserver(containerEl, (entries) => {
61 for (const entry of entries) {
62 tableWidth.value = entry.contentRect.width
63 }
64 })
65
66 const dynamicWidthColumns: ComputedRef<T[]> = computed(() => {
67 const visibleColumns = opts.columns.value.filter((col) => col.visible ?? true)
68 const modalColumns = visibleColumns.filter((col) => isDynamic(col))
69 const totalTableWidth = tableWidth.value || 0
70 const totalFixedWidth = visibleColumns
71 .filter((col) => !isDynamic(col))
72 .reduce((sum, col) => sum + getNumericColumnWidth(col.width), 0)
73
74 const remainingWidth = totalTableWidth - totalFixedWidth
75 const dynamicWidth =
76 remainingWidth > 0 && modalColumns.length > 0 ? remainingWidth / modalColumns.length : null
77
78 const ctx: DynamicWidthContext = { dynamicWidth, remainingWidth, totalTableWidth }
79
80 return visibleColumns.map((col) => {
81 if (opts.widthFor) {
82 const override = opts.widthFor(col, ctx)
83 if (override !== null && override !== undefined) {
84 return { ...col, width: override }
85 }
86 }
87 if (isDynamic(col) && dynamicWidth !== null) {
88 return { ...col, width: Math.floor(dynamicWidth) }
89 }
90 return col
91 })
92 })
93
94 const minTableWidth: ComputedRef<number> = computed(() => {
95 return opts.columns.value.reduce((sum, col) => {
96 if (!minWidthFilter(col)) return sum
97 const value =
98 typeof col.minWidth === 'number' ? col.minWidth : parseFloat(String(col.minWidth)) || 0
99 return sum + value
100 }, 0)
101 })
102
103 return { tableWidth, dynamicWidthColumns, minTableWidth }
104 }
105