1// Lays the Toolkit board out as fixed flex-columns and reports the split to the component, so the 2// view and edit boards render identically and nothing jumps. 3// 4// The board is split column-major and height-balanced (greedy: fill a column to ~1/N of the total 5// height, then move on). The split is recomputed only when the width changes or the card set 6// changes â NOT when a tab switch changes a card's height, and NOT when toggling edit mode. So a 7// card keeps its column: switching a tab only grows/shrinks it in place, and entering edit mode 8// (same cards, same split) doesn't reshuffle anything. 9 10import Sortable from '../../js/sortable.esm.js'; 11 12let dotnetRef = null; 13let resizeObserver = null; 14let cardSortables = []; 15let minColPx = 300; 16let gapPx = 16; 17let lastWidth = -1; 18 19function columnCountFor(width) { 20 return Math.max(1, Math.floor((width + gapPx) / (minColPx + gapPx))); 21} 22 23// Measure the rendered cards (in flat/DOM order) and greedily split them into height-balanced 24// column-major buckets. Reports the per-column card counts to the component. 25function computeLayout() { 26 if (!dotnetRef) return; 27 const board = document.querySelector('.toolkit-board, .toolkit-board-edit'); 28 if (!board) return; 29 const width = board.clientWidth; 30 if (width <= 0) return; 31 32 const columns = columnCountFor(width); 33 const cards = [...board.querySelectorAll('.toolkit-card')]; 34 if (cards.length === 0) return; 35 36 const heights = cards.map(c => c.getBoundingClientRect().height + gapPx); 37 const target = heights.reduce((a, b) => a + b, 0) / columns; 38 39 const counts = new Array(columns).fill(0); 40 let col = 0; 41 let acc = 0; 42 for (let i = 0; i < cards.length; i++) { 43 counts[col]++; 44 acc += heights[i]; 45 const remainingCards = cards.length - i - 1; 46 const remainingCols = columns - col - 1; 47 // Advance to the next column once this one hit the target â but always leave at least one 48 // card for each remaining column. 49 if (col < columns - 1 && acc >= target && remainingCards > remainingCols) { 50 col++; 51 acc = 0; 52 } 53 } 54 dotnetRef.invokeMethodAsync('SetColumnLayout', counts); 55} 56 57function onResize() { 58 const board = document.querySelector('.toolkit-board, .toolkit-board-edit'); 59 const width = board ? board.clientWidth : 0; 60 if (width > 0 && width !== lastWidth) { 61 lastWidth = width; 62 computeLayout(); 63 } 64} 65 66export function init(dotnet, minColumnPx, columnGapPx) { 67 dotnetRef = dotnet; 68 minColPx = minColumnPx; 69 gapPx = columnGapPx; 70 if (resizeObserver) resizeObserver.disconnect(); 71 resizeObserver = new ResizeObserver(onResize); 72 const container = document.querySelector('.toolkit-container'); 73 if (container) resizeObserver.observe(container); 74 window.addEventListener('resize', onResize); 75 lastWidth = -1; 76 onResize(); 77} 78 79// Recompute the split â called after the card set changes (add/remove/reorder), never on a tab 80// switch or an edit-mode toggle. 81export function relayout() { 82 computeLayout(); 83} 84 85// Attach cross-column drag-reorder to the edit board's columns (all columns share a group, so a 86// card can be dragged to any column). On drop, report the full card order (DOM order across the 87// columns is the column-major flat order) so the component can persist it. 88export function attachCardSort(dotnet) { 89 disposeCardSort(); 90 const columns = document.querySelectorAll('.toolkit-board-edit > .toolkit-column'); 91 columns.forEach(column => { 92 cardSortables.push(Sortable.create(column, { 93 group: 'toolkit-cards', 94 animation: 200, 95 handle: '.toolkit-drag-group', 96 forceFallback: true, 97 fallbackOnBody: true, 98 onEnd: () => { 99 const ids = [...document.querySelectorAll('.toolkit-board-edit .toolkit-card--edit')] 100 .map(el => el.getAttribute('data-card-id')) 101 .filter(Boolean); 102 dotnet.invokeMethodAsync('OnCardsReordered', ids); 103 }, 104 })); 105 }); 106} 107 108export function disposeCardSort() {
109 cardSortables.forEach(s => { try { s.destroy(); } catch { /* already gone */ } }); 110 cardSortables = []; 111} 112 113export function dispose() { 114 if (resizeObserver) { resizeObserver.disconnect(); resizeObserver = null; } 115 window.removeEventListener('resize', onResize); 116 disposeCardSort(); 117 dotnetRef = null; 118 lastWidth = -1; 119}
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.