1let grids = []; 2 3export function init(gridElement, autoFocus) { 4 if (gridElement === undefined || gridElement === null) { 5 return; 6 }; 7 8 enableColumnResizing(gridElement); 9 10 let start = gridElement.querySelector('td:first-child'); 11 12 if (autoFocus) { 13 start.focus(); 14 } 15 16 const bodyClickHandler = event => { 17 const columnOptionsElement = gridElement?.querySelector('.col-options'); 18 if (columnOptionsElement && event.composedPath().indexOf(columnOptionsElement) < 0) { 19 gridElement.dispatchEvent(new CustomEvent('closecolumnoptions', { bubbles: true })); 20 } 21 const columnResizeElement = gridElement?.querySelector('.col-resize'); 22 if (columnResizeElement && event.composedPath().indexOf(columnResizeElement) < 0) { 23 gridElement.dispatchEvent(new CustomEvent('closecolumnresize', { bubbles: true })); 24 } 25 }; 26 const keyboardNavigation = (sibling) => { 27 if (sibling !== null) { 28 start.focus(); 29 sibling.focus(); 30 start = sibling; 31 } 32 } 33 const keyDownHandler = event => { 34 if (document.activeElement.tagName.toLowerCase() != 'table' && document.activeElement.tagName.toLowerCase() != 'td' && document.activeElement.tagName.toLowerCase() != 'th') { 35 return; 36 } 37 const columnOptionsElement = gridElement?.querySelector('.col-options'); 38 if (columnOptionsElement) { 39 if (event.key === "Escape") { 40 gridElement.dispatchEvent(new CustomEvent('closecolumnoptions', { bubbles: true })); 41 gridElement.focus(); 42 } 43 columnOptionsElement.addEventListener( 44 "keydown", 45 (event) => { 46 if (event.key === "ArrowRight" || event.key === "ArrowLeft" || event.key === "ArrowDown" || event.key === "ArrowUp") { 47 event.stopPropagation(); 48 } 49 } 50 ); 51 } 52 const columnResizeElement = gridElement?.querySelector('.col-resize'); 53 if (columnResizeElement) { 54 if (event.key === "Escape") { 55 gridElement.dispatchEvent(new CustomEvent('closecolumnresize', { bubbles: true })); 56 gridElement.focus(); 57 } 58 columnResizeElement.addEventListener( 59 "keydown", 60 (event) => { 61 if (event.key === "ArrowRight" || event.key === "ArrowLeft" || event.key === "ArrowDown" || event.key === "ArrowUp") { 62 event.stopPropagation(); 63 } 64 } 65 ); 66 } 67 68 // check if start is a child of gridElement 69 if (start !== null && (gridElement.contains(start) || gridElement === start) && document.activeElement === start && document.activeElement.tagName.toLowerCase() !== 'fluent-text-field' && document.activeElement.tagName.toLowerCase() !== 'fluent-menu-item') { 70 const idx = start.cellIndex; 71 72 if (event.key === "ArrowUp") { 73 // up arrow 74 const previousRow = start.parentElement.previousElementSibling; 75 if (previousRow !== null) { 76 event.preventDefault(); 77 const previousSibling = previousRow.cells[idx]; 78 keyboardNavigation(previousSibling); 79 } 80 } else if (event.key === "ArrowDown") { 81 // down arrow 82 const nextRow = start.parentElement.nextElementSibling; 83 if (nextRow !== null) { 84 event.preventDefault(); 85 const nextSibling = nextRow.cells[idx]; 86 keyboardNavigation(nextSibling); 87 } 88 } else if (event.key === "ArrowLeft") { 89 // left arrow 90 event.preventDefault(); 91 const previousSibling = (document.body.dir === '' || document.body.dir === 'ltr') ? start.previousElementSibling : start.nextElementSibling; 92 keyboardNavigation(previousSibling); 93 event.stopPropagation(); 94 } else if (event.key === "ArrowRight") { 95 // right arrow 96 event.preventDefault(); 97 const nextsibling = (document.body.dir === '' || document.body.dir === 'ltr') ? start.nextElementSibling : start.previousElementSibling; 98 keyboardNavigation(nextsibling); 99 event.stopPropagation(); 100 } 101 } 102 else { 103 start = document.activeElement; 104 } 105 106 }; 107 108 const cells = gridElement.querySelectorAll('[role="gridcell"]');
109 cells.forEach((cell) => { 110 cell.columnDefinition = { 111 columnDataKey: "", 112 cellInternalFocusQueue: true, 113 cellFocusTargetCallback: (cell) => { 114 return cell.children[0]; 115 } 116 } 117 cell.addEventListener( 118 "keydown", 119 (event) => { 120 if (event.target.role !== "gridcell" && (event.key === "ArrowRight" || event.key === "ArrowLeft")) { 121 event.stopPropagation(); 122 } 123 } 124 ); 125 }); 126 127 document.body.addEventListener('click', bodyClickHandler); 128 document.body.addEventListener('mousedown', bodyClickHandler); // Otherwise it seems strange that it doesn't go away until you release the mouse button 129 gridElement.addEventListener('keydown', keyDownHandler); 130 131 return { 132 stop: () => { 133 document.body.removeEventListener('click', bodyClickHandler); 134 document.body.removeEventListener('mousedown', bodyClickHandler); 135 gridElement.removeEventListener('keydown', keyDownHandler); 136 delete grids[gridElement]; 137 } 138 }; 139} 140 141export function checkColumnPopupPosition(gridElement, selector) { 142 const colPopup = gridElement.querySelector(selector); 143 if (colPopup) { 144 const gridRect = gridElement.getBoundingClientRect(); 145 const popupRect = colPopup.getBoundingClientRect(); 146 const leftOverhang = Math.max(0, gridRect.left - popupRect.left); 147 const rightOverhang = Math.max(0, popupRect.right - gridRect.right); 148 if (leftOverhang || rightOverhang) { 149 const applyOffset = leftOverhang && rightOverhang ? (leftOverhang - rightOverhang) / 2 : (leftOverhang - rightOverhang); 150 colPopup.style.transform = `translateX(${applyOffset}px)`; 151 } 152 153 colPopup.style.visibility = 'visible'; 154 colPopup.scrollIntoViewIfNeeded(); 155 156 const autoFocusElem = colPopup.querySelector('[autofocus]'); 157 if (autoFocusElem) { 158 autoFocusElem.focus(); 159 } 160 } 161} 162 163export function enableColumnResizing(gridElement, resizeColumnOnAllRows = true) { 164 const columns = []; 165 const headers = gridElement.querySelectorAll('.column-header.resizable'); 166 167 if (headers.length === 0) { 168 return; 169 } 170 171 const isRTL = getComputedStyle(gridElement).direction === 'rtl'; 172 const isGrid = gridElement.classList.contains('grid') 173 174 let tableHeight = gridElement.offsetHeight; 175 // rows have not been loaded yet, so we need to calculate the height 176 if (tableHeight < 70) { 177 // by getting the aria rowcount attribute 178 const rowCount = gridElement.getAttribute('aria-rowcount'); 179 if (rowCount) { 180 const rowHeight = gridElement.querySelector('thead tr th').offsetHeight; 181 // and multiply by the itemsize (== height of the header cells) 182 tableHeight = rowCount * rowHeight; 183 } 184 } 185 186 // Determine the height based on the resizeColumnOnAllRows parameter 187 let resizeHandleHeight = tableHeight; 188 if (!resizeColumnOnAllRows) { 189 // Only use the header height when resizeColumnOnAllRows is false 190 // Use the first header's height if available 191 resizeHandleHeight = headers.length > 0 ? (headers[0].offsetHeight - 14 ): 30; // fallback to 30px if no headers 192 } 193 194 headers.forEach((header) => { 195 columns.push({ 196 header, 197 size: `${header.clientWidth}px`, 198 }); 199 200 // remove any previously created divs 201 const resizedivs = header.querySelectorAll(".actual-resize-handle"); 202 resizedivs.forEach(div => div.remove()); 203 204 // add a new resize div 205 const div = createDiv(resizeHandleHeight, isRTL); 206 header.appendChild(div); 207 setListeners(div, isRTL); 208 }); 209 210 let initialWidths; 211 if (gridElement.style.gridTemplateColumns) { 212 initialWidths = gridElement.style.gridTemplateColumns; 213 } else { 214 initialWidths = columns.map(({ size }) => size).join(' '); 215 216 if (isGrid) { 217 gridElement.style.gridTemplateColumns = initialWidths; 218 } 219 } 220 221 const id = gridElement.id; 222 if (!grids.find(grid => grid.id === id)) { 223 grids.push({ 224 id, 225 columns, 226 initialWidths, 227 }); 228 } 229 230 function setListeners(div, isRTL) { 231 let pageX, curCol, curColWidth; 232 233 div.addEventListener('pointerdown', function (e) { 234 curCol = e.target.parentElement; 235 pageX = e.pageX; 236 237 const padding = paddingDiff(curCol); 238 239 curColWidth = curCol.offsetWidth - padding; 240 }); 241 242 div.addEventListener('pointerover', function (e) { 243 e.target.style.borderInlineEnd = 'var(--fluent-data-grid-resize-handle-width) solid var(--fluent-data-grid-resize-handle-color)'; 244 e.target.previousElementSibling.style.visibility = 'hidden'; 245 }); 246 247 div.addEventListener('pointerup', removeBorder); 248 div.addEventListener('pointercancel', removeBorder); 249 div.addEventListener('pointerleave', removeBorder); 250 251 document.addEventListener('pointermove', (e) => 252 requestAnimationFrame(() => { 253 gridElement.style.tableLayout = 'fixed'; 254 255 if (curCol) { 256 const diffX = isRTL ? pageX - e.pageX : e.pageX - pageX; 257 const column = columns.find(({ header }) => header === curCol); 258 259 column.size = parseInt(Math.max(parseInt(column.header.style.minWidth), curColWidth + diffX), 10) + 'px'; 260 261 columns.forEach((col) => { 262 if (col.size.startsWith('minmax')) { 263 col.size = parseInt(col.header.clientWidth, 10) + 'px'; 264 } 265 }); 266 267 if (isGrid) { 268 gridElement.style.gridTemplateColumns = columns 269 .map(({ size }) => size) 270 .join(' '); 271 } 272 else { 273 curCol.style.width = column.size; 274 } 275 } 276 }) 277 ); 278 279 document.addEventListener('pointerup', function () { 280 curCol = undefined; 281 curColWidth = undefined; 282 pageX = undefined; 283 }); 284 } 285 286 function createDiv(height, isRTL) { 287 const div = document.createElement('div'); 288 div.className = "actual-resize-handle"; 289 div.style.top = '5px'; 290 div.style.position = 'absolute'; 291 div.style.cursor = 'col-resize'; 292 div.style.userSelect = 'none'; 293 div.style.height = (height - 5) + 'px'; 294 div.style.width = '6px'; 295 div.style.opacity = 'var(--fluent-data-grid-header-opacity)' 296 297 if (isRTL) { 298 div.style.left = '0px'; 299 div.style.right = 'unset'; 300 } else { 301 div.style.left = 'unset'; 302 div.style.right = '0px'; 303 } 304 return div; 305 } 306 307 function paddingDiff(col) { 308 if (getStyleVal(col, 'box-sizing') === 'border-box') { 309 return 0; 310 } 311 312 const padLeft = getStyleVal(col, 'padding-left'); 313 const padRight = getStyleVal(col, 'padding-right'); 314 return parseInt(padLeft) + parseInt(padRight); 315 } 316 317 function getStyleVal(elm, css) { 318 return window.getComputedStyle(elm, null).getPropertyValue(css); 319 } 320 321 function removeBorder(e) { 322 e.target.style.borderInlineEnd = ''; 323 e.target.previousElementSibling.style.visibility = 'visible'; 324 } 325} 326 327 328 329export function resetColumnWidths(gridElement) { 330 const isGrid = gridElement.classList.contains('grid'); 331 const grid = grids.find(({ id }) => id === gridElement.id); 332 if (!grid) { 333 return; 334 } 335 336 const columnsWidths = grid.initialWidths.split(' '); 337 338 grid.columns.forEach((column, index) => { 339 if (isGrid) { 340 column.size = columnsWidths[index]; 341 } else { 342 column.header.style.width = columnsWidths[index]; 343 } 344 });
345 346 if (isGrid) { 347 gridElement.style.gridTemplateColumns = grid.initialWidths; 348 } 349 gridElement.dispatchEvent( 350 new CustomEvent('closecolumnresize', { bubbles: true }) 351 ); 352 gridElement.focus(); 353} 354 355export function resizeColumnDiscrete(gridElement, column, change) { 356 const isGrid = gridElement.classList.contains('grid'); 357 const columns = []; 358 let headerBeingResized; 359 360 if (!column) { 361 const targetElement = document.activeElement.parentElement.parentElement.parentElement.parentElement; 362 if (!(targetElement.classList.contains("column-header") && targetElement.classList.contains("resizable"))) { 363 return; 364 } 365 headerBeingResized = targetElement; 366 } 367 else { 368 headerBeingResized = gridElement.querySelector('.column-header[col-index="' + column + '"]'); 369 } 370 371 372 grids.find(({ id }) => id === gridElement.id).columns.forEach(column => { 373 if (column.header === headerBeingResized) { 374 const width = headerBeingResized.offsetWidth + change; //.getBoundingClientRect().width + change; 375 376 if (change < 0) { 377 column.size = Math.max(parseInt(column.header.style.minWidth), width) + 'px'; 378 } 379 else { 380 column.size = width + 'px'; 381 } 382 column.header.style.width = column.size; 383 } 384 if (isGrid) { 385 // for grid we need to recalculate all columns that are minmax 386 if (column.size.startsWith('minmax')) { 387 column.size = parseInt(column.header.clientWidth, 10) + 'px'; 388 } 389 columns.push(column.size); 390 } 391 }); 392 393 if (isGrid) { 394 gridElement.style.gridTemplateColumns = columns.join(' '); 395 } 396} 397 398export function resizeColumnExact(gridElement, column, width) { 399 const isGrid = gridElement.classList.contains('grid'); 400 const columns = []; 401 let headerBeingResized = gridElement.querySelector('.column-header[col-index="' + column + '"]'); 402 403 if (!headerBeingResized) { 404 return; 405 } 406 407 grids.find(({ id }) => id === gridElement.id).columns.forEach(column => { 408 if (column.header === headerBeingResized) { 409 column.size = Math.max(parseInt(column.header.style.minWidth), width) + 'px'; 410 column.header.style.width = column.size; 411 } 412 if (isGrid) { 413 // for grid we need to recalculate all columns that are minmax 414 if (column.size.startsWith('minmax')) { 415 column.size = parseInt(column.header.clientWidth, 10) + 'px'; 416 } 417 column.header.style.width = column.size; 418 columns.push(column.size); 419 } 420 }); 421 422 if (isGrid) { 423 gridElement.style.gridTemplateColumns = columns.join(' '); 424 } 425 426 gridElement.dispatchEvent(new CustomEvent('closecolumnresize', { bubbles: true })); 427 gridElement.focus(); 428} 429 430export function autoFitGridColumns(gridElement, columnCount) { 431 let gridTemplateColumns = ''; 432 433 for (var i = 0; i < columnCount; i++) { 434 const columnWidths = Array 435 .from(gridElement.querySelectorAll(`[col-index="${i + 1}"]`)) 436 .flatMap((x) => x.offsetWidth); 437 438 const maxColumnWidth = Math.max(...columnWidths); 439 440 gridTemplateColumns += ` ${maxColumnWidth}px`; 441 } 442 443 gridElement.style.gridTemplateColumns = gridTemplateColumns; 444 gridElement.classList.remove("auto-fit"); 445 446 grids[gridElement.id] = gridTemplateColumns; 447} 448 449function calculateVisibleRows(gridElement, rowHeight) { 450 if (rowHeight <= 0) { 451 return 0; 452 } 453 454 const gridContainer = gridElement.parentElement; 455 456 if (!gridContainer) { 457 return 0; 458 } 459 460 const availableHeight = gridContainer?.clientHeight || window.visualViewport?.height || window.innerHeight; 461 462 const visibleRows = Math.max(Math.floor(availableHeight / rowHeight), 1); 463 return visibleRows; 464} 465 466export function dynamicItemsPerPage(gridElement, dotNetObject, rowSize) { 467 const observer = new ResizeObserver(() => { 468 const visibleRows = calculateVisibleRows(gridElement, rowSize) 469 dotNetObject.invokeMethodAsync('UpdateItemsPerPageAsync', visibleRows) 470 .catch(err => console.error("Error invoking Blazor method:", err));
471 }); 472 473 const targetElement = gridElement.parentElement; 474 if (targetElement) { 475 observer.observe(targetElement); 476 } 477}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.