PageSourceSearch

https://geoconfirmed.org/Pages/Toolkit/Toolkit.ahklpziiur.razor.js

js geoconfirmed.org collected 2026-09-28 11:04:36 UTC 4,498 bytes, 119 lines download raw bytes

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.