(function initLargeTableColumnResizeModule() { const STORAGE_PREFIX = 'sg:large-table:column-widths:'; const STORAGE_VERSION = 3; const DEFAULT_MIN_WIDTH = 128; const KEYBOARD_STEP = 8; const KEYBOARD_LARGE_STEP = 40; let activeResize = null; const getTables = (root) => root.querySelectorAll('[data-component="large-table"][data-column-resize="true"]'); const getColumnHeaders = (table) => Array.from( table.querySelectorAll('[data-component-part="large-table-header-row"] [role="columnheader"]'), ); const getMinWidth = (table) => { const configured = table.dataset.columnMinWidth; const value = configured || getComputedStyle(table).getPropertyValue('--large-table-column-min-width').trim(); if (!value) { return DEFAULT_MIN_WIDTH; } const probe = document.createElement('span'); probe.style.cssText = `position:absolute;width:${value};visibility:hidden;`; table.appendChild(probe); const width = probe.getBoundingClientRect().width; probe.remove(); return width > 0 ? width : DEFAULT_MIN_WIDTH; }; const parseDefaultWidth = (rawValue, tableWidth) => { if (!rawValue) { return null; } const value = Number.parseFloat(rawValue); if (!Number.isFinite(value) || value <= 0) { return null; } if (rawValue.trim().endsWith('%')) { return tableWidth * value / 100; } if (rawValue.trim().endsWith('fr')) { return value; } return value; }; const normalizeRatios = (values) => { const safeValues = values.map((value) => (Number.isFinite(value) && value > 0 ? value : 0)); const total = safeValues.reduce((sum, value) => sum + value, 0); if (total <= 0) { return Array(values.length).fill(1 / values.length); } return safeValues.map((value) => value / total); }; const getDefaultRatios = (table, headers, minWidth) => { const tableWidth = Math.max(table.querySelector('[data-component-part="large-table-viewport"]')?.clientWidth || 0, minWidth * headers.length); const configured = headers.map((header) => parseDefaultWidth(header.dataset.defaultWidth, tableWidth)); if (configured.some((width) => width === null)) { return Array(headers.length).fill(1 / headers.length); } return normalizeRatios(configured); }; const getStorageKey = (table) => `${STORAGE_PREFIX}${table.dataset.columnResizeKey || ''}`; const readStoredRatios = (table, count) => { const key = getStorageKey(table); if (!table.dataset.columnResizeKey) { return null; } try { if (!window.localStorage) { return null; } const stored = JSON.parse(window.localStorage.getItem(key)); if (stored?.version !== STORAGE_VERSION || !Array.isArray(stored.ratios) || stored.ratios.length !== count) { return null; } if (stored.ratios.some((ratio) => !Number.isFinite(ratio) || ratio <= 0)) { return null; } return normalizeRatios(stored.ratios); } catch (error) { return null; } }; const writeStoredRatios = (table, ratios) => { if (!table.dataset.columnResizeKey) { return; } try { if (!window.localStorage) { return; } window.localStorage.setItem(getStorageKey(table), JSON.stringify({ version: STORAGE_VERSION, ratios: normalizeRatios(ratios), })); } catch (error) { // Storage can be unavailable in privacy-restricted browser contexts. } }; const updateHandleState = (table, widths, minWidth) => { const handles = table.querySelectorAll('[data-large-table-resize-handle]'); handles.forEach((handle) => { const index = Number.parseInt(handle.dataset.largeTableResizeHandle, 10); const current = widths[index]; const maximum = Math.max(minWidth, widths[index] + widths[index + 1] - minWidth); if (table.id) { handle.setAttribute('aria-controls', table.id); } handle.setAttribute('aria-valuemin', String(Math.round(minWidth))); handle.setAttribute('aria-valuemax', String(Math.round(maximum))); handle.setAttribute('aria-valuenow', String(Math.round(current))); handle.setAttribute('aria-valuetext', `${Math.round(current)} Pixel`); }); }; const getRenderedWidths = (table, count, minWidth) => { const headers = getColumnHeaders(table); const widths = headers.map((header) => header.getBoundingClientRect().width); if (widths.length === count && widths.every((width) => width > 0)) { return widths; } return Array(count).fill(minWidth); }; const applyRatios = (table, ratios, minWidth) => { const safeRatios = normalizeRatios(ratios); const columnTemplate = safeRatios .map((ratio) => `minmax(${Math.ceil(minWidth)}px, ${ratio}fr)`) .join(' '); table.style.setProperty('--large-table-grid-template-columns', columnTemplate); table.style.setProperty('--large-table-grid-min-width', `${Math.ceil(minWidth * safeRatios.length)}px`); table._largeTableColumnRatios = safeRatios; table._largeTableColumnWidths = getRenderedWidths(table, safeRatios.length, minWidth); updateHandleState(table, table._largeTableColumnWidths, minWidth); }; const resizeColumn = (table, index, delta, persist, baseWidths = table._largeTableColumnWidths) => { const widths = [...baseWidths]; const minWidth = getMinWidth(table); const nextLeft = Math.max(minWidth, Math.min(widths[index] + widths[index + 1] - minWidth, widths[index] + delta)); const appliedDelta = nextLeft - widths[index]; widths[index] = nextLeft; widths[index + 1] -= appliedDelta; applyRatios(table, widths.map((width) => width / widths.reduce((sum, value) => sum + value, 0)), minWidth); if (persist) { writeStoredRatios(table, table._largeTableColumnRatios); } }; const initTable = (table) => { const headers = getColumnHeaders(table); if (headers.length < 2) { return; } const minWidth = getMinWidth(table); const stored = readStoredRatios(table, headers.length); applyRatios(table, stored || getDefaultRatios(table, headers, minWidth), minWidth); if (table.dataset.largeTableResizeInitialized === 'true') { return; } table.dataset.largeTableResizeInitialized = 'true'; table.addEventListener('pointerdown', (event) => { const handle = event.target.closest('[data-large-table-resize-handle]'); if (!handle || !table.contains(handle)) { return; } event.preventDefault(); activeResize = { table, handle, index: Number.parseInt(handle.dataset.largeTableResizeHandle, 10), startX: event.clientX, startWidths: [...table._largeTableColumnWidths], }; handle.dataset.largeTableResizeActive = 'true'; table.dataset.columnResizeState = 'resizing'; handle.setPointerCapture?.(event.pointerId); }); table.addEventListener('pointermove', (event) => { if (!activeResize || activeResize.table !== table) { return; } resizeColumn( table, activeResize.index, event.clientX - activeResize.startX, false, activeResize.startWidths, ); }); table.addEventListener('pointerup', () => { if (!activeResize || activeResize.table !== table) { return; } writeStoredRatios(table, table._largeTableColumnRatios); activeResize.handle.dataset.largeTableResizeActive = 'false'; table.dataset.columnResizeState = 'idle'; activeResize = null; }); if (typeof ResizeObserver === 'function') { table._largeTableResizeObserver = new ResizeObserver(() => { applyRatios(table, table._largeTableColumnRatios, getMinWidth(table)); }); table._largeTableResizeObserver.observe(table.querySelector('[data-component-part="large-table-viewport"]')); } table.addEventListener('keydown', (event) => { const handle = event.target.closest('[data-large-table-resize-handle]'); if (!handle || !table.contains(handle)) { return; } const index = Number.parseInt(handle.dataset.largeTableResizeHandle, 10); const step = event.shiftKey ? KEYBOARD_LARGE_STEP : KEYBOARD_STEP; if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { event.preventDefault(); resizeColumn(table, index, event.key === 'ArrowRight' ? step : -step, true); } else if (event.key === 'Home' || event.key === 'End') { event.preventDefault(); const minWidth = getMinWidth(table); const current = table._largeTableColumnWidths[index]; const target = event.key === 'Home' ? minWidth : current + table._largeTableColumnWidths[index + 1] - minWidth; resizeColumn(table, index, target - current, true); } }); }; window.sgInitLargeTableColumnResize = (options = {}) => { const root = options.root || document; getTables(root).forEach(initTable); }; document.addEventListener('DOMContentLoaded', () => window.sgInitLargeTableColumnResize(), { once: true }); })();