ropav 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/autocomplete/autocomplete-root.js +3 -1
- package/dist/components/autocomplete/index.d.ts +3 -0
- package/dist/components/avatar/avatar-image.js +13 -2
- package/dist/components/avatar/avatar.types.d.ts +7 -1
- package/dist/components/breadcrumbs/breadcrumbs-item.js +1 -0
- package/dist/components/breadcrumbs/index.d.ts +1 -0
- package/dist/components/calendar/calendar-header.js +1 -1
- package/dist/components/calendar/index.d.ts +5 -3
- package/dist/components/calendar-year-picker/index.d.ts +4 -2
- package/dist/components/checkbox/checkbox-content.js +1 -1
- package/dist/components/color-field/index.d.ts +3 -2
- package/dist/components/color-input-group/color-input-group-root.js +2 -0
- package/dist/components/color-input-group/color-input-group.types.d.ts +2 -0
- package/dist/components/color-input-group/index.d.ts +2 -0
- package/dist/components/combo-box/combo-box-root.js +9 -2
- package/dist/components/combo-box/combo-box.types.d.ts +3 -0
- package/dist/components/combo-box/index.d.ts +3 -0
- package/dist/components/date-field/index.d.ts +1 -0
- package/dist/components/date-input-group/date-input-group-root.js +2 -0
- package/dist/components/date-input-group/date-input-group-segment.js +1 -1
- package/dist/components/date-input-group/date-input-group.types.d.ts +2 -0
- package/dist/{composables → components/date-input-group}/use-date-segment.d.ts +2 -2
- package/dist/{composables → components/date-input-group}/use-date-segment.js +9 -9
- package/dist/components/date-picker/index.d.ts +3 -2
- package/dist/components/date-picker/index.js +2 -2
- package/dist/components/date-range-picker/date-range-picker-root.js +1 -1
- package/dist/components/date-range-picker/index.d.ts +18 -16
- package/dist/components/date-range-picker/index.js +2 -2
- package/dist/{composables → components/date-range-picker}/use-date-range-picker.d.ts +7 -7
- package/dist/{composables → components/date-range-picker}/use-date-range-picker.js +10 -10
- package/dist/components/drawer/drawer-dialog.js +1 -1
- package/dist/{composables → components/drawer}/use-drawer-drag.d.ts +1 -1
- package/dist/{composables → components/drawer}/use-drawer-drag.js +1 -1
- package/dist/components/dropdown/dropdown-menu.js +3 -1
- package/dist/components/dropdown/dropdown-submenu-trigger.js +1 -1
- package/dist/components/dropdown/dropdown.types.d.ts +3 -3
- package/dist/components/dropdown/index.d.ts +4 -2
- package/dist/{composables → components/dropdown}/use-safely-mouse-to-submenu.js +1 -1
- package/dist/components/icons/icon-overlay-arrow.js +1 -1
- package/dist/components/index.d.ts +5 -0
- package/dist/components/input/index.d.ts +12 -4
- package/dist/components/input/input-root.d.ts +5 -2
- package/dist/components/input/input-root.js +46 -5
- package/dist/components/input/input.types.d.ts +11 -0
- package/dist/components/input-group/index.d.ts +2 -0
- package/dist/components/input-group/input-group-root.js +3 -0
- package/dist/components/input-group/input-group.types.d.ts +5 -0
- package/dist/components/input-otp/index.d.ts +6 -4
- package/dist/components/link/index.d.ts +2 -0
- package/dist/components/link/link-root.js +22 -5
- package/dist/components/link/link.types.d.ts +14 -3
- package/dist/{composables → components/list-box}/drag-collection.d.ts +2 -2
- package/dist/{composables → components/list-box}/drag-collection.js +1 -1
- package/dist/components/list-box/index.d.ts +4 -0
- package/dist/components/list-box/list-box-root.js +60 -35
- package/dist/components/list-box/list-box.types.d.ts +2 -0
- package/dist/components/menu/index.d.ts +4 -0
- package/dist/components/menu/menu-root.js +9 -2
- package/dist/components/menu/menu.types.d.ts +5 -0
- package/dist/components/number-field/index.d.ts +3 -0
- package/dist/components/number-field/number-field-root.js +2 -0
- package/dist/components/number-field/number-field.types.d.ts +2 -0
- package/dist/components/overlay/overlay-arrow.js +64 -11
- package/dist/components/pagination/index.d.ts +2 -0
- package/dist/components/pagination/pagination-root.js +8 -3
- package/dist/components/pagination/pagination.types.d.ts +8 -0
- package/dist/components/popover/popover-trigger.js +14 -7
- package/dist/components/radio/radio-content.js +1 -1
- package/dist/components/range-calendar/index.d.ts +18 -15
- package/dist/components/range-calendar/range-calendar-header.js +1 -1
- package/dist/components/router-provider/index.d.ts +5 -0
- package/dist/components/router-provider/index.js +3 -0
- package/dist/components/router-provider/router-provider.context.d.ts +40 -0
- package/dist/components/router-provider/router-provider.context.js +16 -0
- package/dist/components/router-provider/router-provider.d.ts +21 -0
- package/dist/components/router-provider/router-provider.js +22 -0
- package/dist/components/router-provider/router-provider.types.d.ts +23 -0
- package/dist/components/scroll-shadow/scroll-shadow-root.js +7 -1
- package/dist/components/scroll-shadow/use-scroll-shadow.js +25 -1
- package/dist/components/search-field/index.d.ts +2 -0
- package/dist/components/search-field/search-field-root.js +3 -1
- package/dist/components/search-field/search-field.types.d.ts +2 -0
- package/dist/{composables → components/search-field}/use-search-field.d.ts +2 -2
- package/dist/{composables → components/search-field}/use-search-field.js +3 -3
- package/dist/components/segmented-control/index.d.ts +134 -0
- package/dist/components/segmented-control/index.js +15 -0
- package/dist/components/segmented-control/segmented-control-indicator.d.ts +19 -0
- package/dist/components/segmented-control/segmented-control-indicator.js +36 -0
- package/dist/components/segmented-control/segmented-control-item.d.ts +21 -0
- package/dist/components/segmented-control/segmented-control-item.js +96 -0
- package/dist/components/segmented-control/segmented-control-root.d.ts +25 -0
- package/dist/components/segmented-control/segmented-control-root.js +112 -0
- package/dist/components/segmented-control/segmented-control-separator.d.ts +19 -0
- package/dist/components/segmented-control/segmented-control-separator.js +18 -0
- package/dist/components/segmented-control/segmented-control.context.d.ts +17 -0
- package/dist/components/segmented-control/segmented-control.context.js +12 -0
- package/dist/components/segmented-control/segmented-control.types.d.ts +67 -0
- package/dist/components/select/index.d.ts +3 -0
- package/dist/components/select/select-hidden-select.js +1 -1
- package/dist/components/select/select-root.js +2 -0
- package/dist/components/select/select.types.d.ts +2 -0
- package/dist/components/separator/separator-root.js +11 -5
- package/dist/components/separator/separator.context.d.ts +7 -0
- package/dist/components/sidebar/index.d.ts +544 -0
- package/dist/components/sidebar/index.js +47 -0
- package/dist/components/sidebar/sidebar-collapsible-trigger.d.ts +21 -0
- package/dist/components/sidebar/sidebar-collapsible-trigger.js +70 -0
- package/dist/components/sidebar/sidebar-collapsible.d.ts +24 -0
- package/dist/components/sidebar/sidebar-collapsible.js +78 -0
- package/dist/components/sidebar/sidebar-content.d.ts +21 -0
- package/dist/components/sidebar/sidebar-content.js +37 -0
- package/dist/components/sidebar/sidebar-footer.d.ts +21 -0
- package/dist/components/sidebar/sidebar-footer.js +24 -0
- package/dist/components/sidebar/sidebar-group-label.d.ts +21 -0
- package/dist/components/sidebar/sidebar-group-label.js +32 -0
- package/dist/components/sidebar/sidebar-group.d.ts +21 -0
- package/dist/components/sidebar/sidebar-group.js +48 -0
- package/dist/components/sidebar/sidebar-header.d.ts +21 -0
- package/dist/components/sidebar/sidebar-header.js +24 -0
- package/dist/components/sidebar/sidebar-inset.d.ts +21 -0
- package/dist/components/sidebar/sidebar-inset.js +24 -0
- package/dist/components/sidebar/sidebar-item-icon.d.ts +21 -0
- package/dist/components/sidebar/sidebar-item-icon.js +20 -0
- package/dist/components/sidebar/sidebar-item-indicator.d.ts +21 -0
- package/dist/components/sidebar/sidebar-item-indicator.js +48 -0
- package/dist/components/sidebar/sidebar-item-label.d.ts +21 -0
- package/dist/components/sidebar/sidebar-item-label.js +28 -0
- package/dist/components/sidebar/sidebar-item-tooltip.d.ts +21 -0
- package/dist/components/sidebar/sidebar-item-tooltip.js +38 -0
- package/dist/components/sidebar/sidebar-item-trailing.d.ts +21 -0
- package/dist/components/sidebar/sidebar-item-trailing.js +28 -0
- package/dist/components/sidebar/sidebar-item.d.ts +23 -0
- package/dist/components/sidebar/sidebar-item.js +160 -0
- package/dist/components/sidebar/sidebar-panel.d.ts +21 -0
- package/dist/components/sidebar/sidebar-panel.js +88 -0
- package/dist/components/sidebar/sidebar-rail.d.ts +21 -0
- package/dist/components/sidebar/sidebar-rail.js +210 -0
- package/dist/components/sidebar/sidebar-root.d.ts +31 -0
- package/dist/components/sidebar/sidebar-root.js +138 -0
- package/dist/components/sidebar/sidebar-separator.d.ts +19 -0
- package/dist/components/sidebar/sidebar-separator.js +16 -0
- package/dist/components/sidebar/sidebar-sub-menu.d.ts +23 -0
- package/dist/components/sidebar/sidebar-sub-menu.js +40 -0
- package/dist/components/sidebar/sidebar-trigger.d.ts +21 -0
- package/dist/components/sidebar/sidebar-trigger.js +69 -0
- package/dist/components/sidebar/sidebar.context.d.ts +75 -0
- package/dist/components/sidebar/sidebar.context.js +30 -0
- package/dist/components/sidebar/sidebar.state.d.ts +73 -0
- package/dist/components/sidebar/sidebar.state.js +76 -0
- package/dist/components/sidebar/sidebar.storage.d.ts +8 -0
- package/dist/components/sidebar/sidebar.storage.js +45 -0
- package/dist/components/sidebar/sidebar.types.d.ts +259 -0
- package/dist/components/splitter/index.d.ts +143 -0
- package/dist/components/splitter/index.js +13 -0
- package/dist/components/splitter/splitter-handle.d.ts +21 -0
- package/dist/components/splitter/splitter-handle.js +209 -0
- package/dist/components/splitter/splitter-panel.d.ts +21 -0
- package/dist/components/splitter/splitter-panel.js +70 -0
- package/dist/components/splitter/splitter-root.d.ts +31 -0
- package/dist/components/splitter/splitter-root.js +152 -0
- package/dist/components/splitter/splitter.context.d.ts +15 -0
- package/dist/components/splitter/splitter.context.js +5 -0
- package/dist/components/splitter/splitter.state.d.ts +87 -0
- package/dist/components/splitter/splitter.state.js +322 -0
- package/dist/components/splitter/splitter.storage.d.ts +15 -0
- package/dist/components/splitter/splitter.storage.js +56 -0
- package/dist/components/splitter/splitter.types.d.ts +98 -0
- package/dist/components/switch/switch-content.js +1 -1
- package/dist/components/table/index.d.ts +10 -9
- package/dist/components/table/table-body.js +13 -13
- package/dist/components/table/table-cell.js +49 -46
- package/dist/components/table/table-column-resizer.js +1 -1
- package/dist/components/table/table-column.js +1 -1
- package/dist/components/table/table-content.js +37 -18
- package/dist/{composables → components/table}/table-drag-collection.d.ts +1 -1
- package/dist/{composables → components/table}/table-drag-collection.js +1 -1
- package/dist/components/table/table-drop-indicator.js +3 -1
- package/dist/components/table/table-resizable-container.d.ts +4 -5
- package/dist/components/table/table-row.js +45 -14
- package/dist/components/table/table-virtualizer-item.d.ts +2 -7
- package/dist/components/table/table-virtualizer-item.js +20 -9
- package/dist/components/table/table.context.d.ts +11 -3
- package/dist/components/table/table.types.d.ts +1 -1
- package/dist/{composables → components/table}/use-grid-keyboard.d.ts +3 -3
- package/dist/{composables → components/table}/use-grid-keyboard.js +57 -55
- package/dist/{composables → components/table}/use-grid-selection-announcement.d.ts +2 -2
- package/dist/{composables → components/table}/use-grid-selection-announcement.js +2 -2
- package/dist/{composables → components/table}/use-table-collection.d.ts +2 -2
- package/dist/{composables → components/table}/use-table-collection.js +4 -4
- package/dist/{composables → components/table}/use-table-column-layout.d.ts +13 -20
- package/dist/components/table/use-table-column-layout.js +115 -0
- package/dist/components/tabs/tabs-panel.js +5 -1
- package/dist/components/tabs/tabs-tab.js +1 -1
- package/dist/components/textarea/index.d.ts +12 -4
- package/dist/components/textarea/textarea-root.d.ts +5 -2
- package/dist/components/textarea/textarea-root.js +40 -5
- package/dist/components/textarea/textarea.types.d.ts +11 -0
- package/dist/components/textfield/index.d.ts +2 -0
- package/dist/components/textfield/textfield-root.js +5 -1
- package/dist/components/textfield/textfield.context.d.ts +2 -0
- package/dist/components/textfield/textfield.types.d.ts +5 -0
- package/dist/components/time-field/index.d.ts +1 -0
- package/dist/components/toast/index.d.ts +1 -0
- package/dist/components/toast/toast-default-content.js +1 -1
- package/dist/components/toggle-button-group/toggle-button-group-root.js +3 -3
- package/dist/components/toolbar/index.d.ts +3 -2
- package/dist/components/toolbar/toolbar-root.js +3 -3
- package/dist/components/tooltip/index.js +1 -1
- package/dist/components/tooltip/tooltip-root.js +1 -1
- package/dist/components/tooltip/tooltip-trigger.js +13 -5
- package/dist/components/virtualizer/index.d.ts +3 -1
- package/dist/components/virtualizer/index.js +2 -1
- package/dist/components/virtualizer/use-virtualizer-item.d.ts +23 -0
- package/dist/components/virtualizer/use-virtualizer-item.js +45 -0
- package/dist/components/virtualizer/virtualizer-item.js +7 -41
- package/dist/components/virtualizer/virtualizer-scrollbar-track.d.ts +20 -0
- package/dist/components/virtualizer/virtualizer-scrollbar-track.js +141 -0
- package/dist/components/virtualizer/virtualizer-scrollbar.d.ts +18 -0
- package/dist/components/virtualizer/virtualizer-scrollbar.js +55 -0
- package/dist/components/virtualizer/virtualizer.context.d.ts +17 -1
- package/dist/composables/drag-manager.js +1 -13
- package/dist/composables/index.d.ts +3 -71
- package/dist/composables/use-collection.d.ts +22 -3
- package/dist/composables/use-collection.js +67 -14
- package/dist/composables/use-color-area.js +2 -2
- package/dist/composables/use-color-field.js +1 -1
- package/dist/composables/use-combo-box.js +1 -1
- package/dist/composables/use-date-field.js +1 -1
- package/dist/composables/use-drag-and-drop.js +1 -1
- package/dist/composables/use-drag.d.ts +6 -5
- package/dist/composables/use-draggable-item.d.ts +2 -2
- package/dist/composables/use-droppable-collection.js +2 -2
- package/dist/composables/use-droppable-item.js +1 -1
- package/dist/composables/use-form-reset.d.ts +6 -2
- package/dist/composables/use-form-reset.js +9 -5
- package/dist/composables/use-list-keyboard.js +64 -62
- package/dist/composables/use-menu.d.ts +10 -0
- package/dist/composables/use-menu.js +22 -0
- package/dist/composables/use-number-field.js +1 -1
- package/dist/composables/use-select.js +2 -3
- package/dist/composables/use-selection-manager.js +4 -4
- package/dist/composables/use-single-select-list-state.d.ts +37 -0
- package/dist/composables/use-single-select-list-state.js +110 -0
- package/dist/composables/use-slider-thumb.js +2 -2
- package/dist/composables/use-slider.js +1 -1
- package/dist/composables/use-tab-list-state.d.ts +20 -29
- package/dist/composables/use-tab-list-state.js +20 -99
- package/dist/composables/use-text-field.js +1 -1
- package/dist/composables/use-virtualizer-scroll.d.ts +52 -2
- package/dist/composables/use-virtualizer-scroll.js +79 -13
- package/dist/composables/use-virtualizer.d.ts +24 -4
- package/dist/composables/use-virtualizer.js +40 -6
- package/dist/index.js +58 -80
- package/dist/utils/document-order.d.ts +14 -0
- package/dist/utils/document-order.js +17 -0
- package/dist/utils/flex-sizing.d.ts +41 -0
- package/dist/utils/flex-sizing.js +140 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/open-link.d.ts +31 -0
- package/dist/utils/open-link.js +32 -0
- package/dist/utils/story-truncation.d.ts +20 -0
- package/dist/utils/story-users.d.ts +11 -0
- package/dist/utils/virtualizer-collection.d.ts +14 -4
- package/dist/utils/virtualizer-collection.js +9 -14
- package/dist/utils/virtualizer-layout-info.js +3 -2
- package/dist/utils/virtualizer-list-layout.d.ts +75 -29
- package/dist/utils/virtualizer-list-layout.js +182 -99
- package/dist/utils/virtualizer-offsets.d.ts +111 -0
- package/dist/utils/virtualizer-offsets.js +159 -0
- package/dist/utils/virtualizer-overscan.d.ts +9 -15
- package/dist/utils/virtualizer-overscan.js +14 -33
- package/dist/utils/virtualizer-table-layout.d.ts +50 -21
- package/dist/utils/virtualizer-table-layout.js +130 -89
- package/dist/version.js +1 -1
- package/package.json +19 -3
- package/dist/composables/use-table-column-layout.js +0 -214
- /package/dist/{composables → components/dropdown}/use-safely-mouse-to-submenu.d.ts +0 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
//#region src/utils/virtualizer-offsets.ts
|
|
2
|
+
/** Reads a start out of the flat array, which holds a `[start, size]` pair per row. */
|
|
3
|
+
var START = 0;
|
|
4
|
+
var SIZE = 1;
|
|
5
|
+
var RowOffsets = class {
|
|
6
|
+
count = 0;
|
|
7
|
+
keyAt = (index) => index;
|
|
8
|
+
estimate = () => 0;
|
|
9
|
+
start = 0;
|
|
10
|
+
gap = 0;
|
|
11
|
+
/** The measured sizes, by key rather than by index so a reorder keeps them. */
|
|
12
|
+
sizes = /* @__PURE__ */ new Map();
|
|
13
|
+
/** `[start, size]` per row. Longer than `count` when the collection has shrunk. */
|
|
14
|
+
flat = /* @__PURE__ */ new Float64Array(0);
|
|
15
|
+
/** The lowest index whose start may have moved, or `count` when nothing has. */
|
|
16
|
+
dirtyFrom = 0;
|
|
17
|
+
collection = null;
|
|
18
|
+
/**
|
|
19
|
+
* Takes the shape of the collection.
|
|
20
|
+
*
|
|
21
|
+
* The measurements survive as long as the keys can still be asked for — a row that scrolled out
|
|
22
|
+
* of the window and back in is the same row, and re-measuring it would place it at an estimate
|
|
23
|
+
* for a frame first.
|
|
24
|
+
*/
|
|
25
|
+
configure(options) {
|
|
26
|
+
const start = options.start ?? 0;
|
|
27
|
+
const gap = options.gap ?? 0;
|
|
28
|
+
if (this.count === options.count && this.start === start && this.gap === gap && this.collection === options.collection) {
|
|
29
|
+
this.keyAt = options.keyAt;
|
|
30
|
+
this.estimate = options.estimate;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
this.count = options.count;
|
|
34
|
+
this.keyAt = options.keyAt;
|
|
35
|
+
this.estimate = options.estimate;
|
|
36
|
+
this.collection = options.collection;
|
|
37
|
+
this.start = start;
|
|
38
|
+
this.gap = gap;
|
|
39
|
+
this.dirtyFrom = 0;
|
|
40
|
+
}
|
|
41
|
+
/** Forgets every measurement, which is what a resize does — a row's height follows its width. */
|
|
42
|
+
reset() {
|
|
43
|
+
this.sizes.clear();
|
|
44
|
+
this.dirtyFrom = 0;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Records a measured size.
|
|
48
|
+
*
|
|
49
|
+
* Returns whether anything moved, which is the caller's cue to lay out again. Measuring the same
|
|
50
|
+
* height twice moves nothing, and that is what keeps measurement from looping.
|
|
51
|
+
*/
|
|
52
|
+
measure(key, size, index) {
|
|
53
|
+
if (this.sizes.get(key) === size) return false;
|
|
54
|
+
this.sizes.set(key, size);
|
|
55
|
+
this.invalidateFrom(index ?? 0);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
/** Whether the row at an index has been measured, rather than placed at its estimate. */
|
|
59
|
+
isMeasured(index) {
|
|
60
|
+
return this.sizes.has(this.keyAt(index));
|
|
61
|
+
}
|
|
62
|
+
/** Marks the row at an index, and everything below it, as sitting somewhere new. */
|
|
63
|
+
invalidateFrom(index) {
|
|
64
|
+
this.dirtyFrom = Math.min(this.dirtyFrom, Math.max(0, index));
|
|
65
|
+
}
|
|
66
|
+
size(index) {
|
|
67
|
+
this.build();
|
|
68
|
+
return index >= 0 && index < this.count ? this.flat[index * 2 + SIZE] : 0;
|
|
69
|
+
}
|
|
70
|
+
/** Where the row at an index starts. Answers for a row nobody has rendered. */
|
|
71
|
+
startOf(index) {
|
|
72
|
+
this.build();
|
|
73
|
+
if (this.count === 0) return this.start;
|
|
74
|
+
const clamped = Math.max(0, Math.min(index, this.count - 1));
|
|
75
|
+
const start = this.flat[clamped * 2 + START];
|
|
76
|
+
return index > clamped ? start + this.flat[clamped * 2 + SIZE] + this.gap : start;
|
|
77
|
+
}
|
|
78
|
+
endOf(index) {
|
|
79
|
+
return this.startOf(index) + this.size(index);
|
|
80
|
+
}
|
|
81
|
+
/** How tall every row together comes to, the gaps between them included. */
|
|
82
|
+
total() {
|
|
83
|
+
this.build();
|
|
84
|
+
if (this.count === 0) return this.start;
|
|
85
|
+
const last = this.count - 1;
|
|
86
|
+
return this.flat[last * 2 + START] + this.flat[last * 2 + SIZE];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The row whose band contains an offset, clamped to the collection.
|
|
90
|
+
*
|
|
91
|
+
* A row ending exactly on the offset counts as being above it, which is the edge rule the whole
|
|
92
|
+
* window turns on: a rectangle snapped to a row boundary would otherwise take in the row above.
|
|
93
|
+
*/
|
|
94
|
+
indexAt(offset) {
|
|
95
|
+
this.build();
|
|
96
|
+
if (this.count === 0) return 0;
|
|
97
|
+
let low = 0;
|
|
98
|
+
let high = this.count - 1;
|
|
99
|
+
while (low <= high) {
|
|
100
|
+
const middle = low + high >> 1;
|
|
101
|
+
const start = this.flat[middle * 2 + START];
|
|
102
|
+
if (start + this.flat[middle * 2 + SIZE] <= offset) low = middle + 1;
|
|
103
|
+
else if (start > offset) high = middle - 1;
|
|
104
|
+
else return middle;
|
|
105
|
+
}
|
|
106
|
+
return Math.max(0, Math.min(low, this.count - 1));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The rows a window covers, plus `overscan` of them either side.
|
|
110
|
+
*
|
|
111
|
+
* Closed at both ends: `last` is a row, not the one past it.
|
|
112
|
+
*
|
|
113
|
+
* Both ends are clamped into the collection, so a window that outran the data comes back with
|
|
114
|
+
* the rows nearest to it rather than with nothing. That is the property the whole thing turns
|
|
115
|
+
* on: there is no offset a scrollbar can reach that has no rows to answer with.
|
|
116
|
+
*
|
|
117
|
+
* An empty collection is the one range that holds nothing, and it says so with `last` below
|
|
118
|
+
* `first` rather than by naming a row that is not there.
|
|
119
|
+
*/
|
|
120
|
+
rangeFor(offset, length, overscan = 0) {
|
|
121
|
+
this.build();
|
|
122
|
+
if (this.count === 0) return {
|
|
123
|
+
first: 0,
|
|
124
|
+
last: -1
|
|
125
|
+
};
|
|
126
|
+
const first = this.indexAt(offset);
|
|
127
|
+
const last = this.indexAt(offset + length);
|
|
128
|
+
return {
|
|
129
|
+
first: Math.max(0, first - overscan),
|
|
130
|
+
last: Math.min(this.count - 1, last + overscan)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Fills in the starts from the lowest one a measurement disturbed.
|
|
135
|
+
*
|
|
136
|
+
* Every row below a size that changed sits somewhere new, so there is no shortcut past them —
|
|
137
|
+
* but this is a loop over a typed array with no allocation in it, which is why a hundred thousand
|
|
138
|
+
* rows can afford to be re-added on the frame a row is measured.
|
|
139
|
+
*/
|
|
140
|
+
build() {
|
|
141
|
+
if (this.dirtyFrom >= this.count) return;
|
|
142
|
+
const needed = this.count * 2;
|
|
143
|
+
if (this.flat.length < needed) {
|
|
144
|
+
const grown = new Float64Array(needed);
|
|
145
|
+
grown.set(this.flat.subarray(0, Math.min(this.flat.length, this.dirtyFrom * 2)));
|
|
146
|
+
this.flat = grown;
|
|
147
|
+
}
|
|
148
|
+
let running = this.dirtyFrom === 0 ? this.start : this.flat[(this.dirtyFrom - 1) * 2 + START] + this.flat[(this.dirtyFrom - 1) * 2 + SIZE] + this.gap;
|
|
149
|
+
for (let index = this.dirtyFrom; index < this.count; index += 1) {
|
|
150
|
+
const size = this.sizes.get(this.keyAt(index)) ?? this.estimate(index);
|
|
151
|
+
this.flat[index * 2 + START] = running;
|
|
152
|
+
this.flat[index * 2 + SIZE] = size;
|
|
153
|
+
running += size + this.gap;
|
|
154
|
+
}
|
|
155
|
+
this.dirtyFrom = this.count;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
//#endregion
|
|
159
|
+
export { RowOffsets };
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { Rect } from "./virtualizer-geometry.js";
|
|
2
2
|
/**
|
|
3
|
-
* How much beyond the viewport the virtualizer renders
|
|
4
|
-
* `OverscanManager`.
|
|
3
|
+
* How much beyond the viewport the virtualizer renders.
|
|
5
4
|
*
|
|
6
|
-
* A third of the viewport
|
|
7
|
-
* land on rather than a gap. The extra is added in the direction of travel only, which is why the
|
|
8
|
-
* rendered set depends on how the scroll position was arrived at and not only on where it is.
|
|
5
|
+
* A third of the viewport either side, so a scroll has something to land on rather than a gap.
|
|
9
6
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* React Aria adds it in the direction of travel only, from a velocity it samples on every move and
|
|
8
|
+
* never resets — so the rendered set depends on how an offset was arrived at rather than on the
|
|
9
|
+
* offset, a collection sitting still keeps whichever side it was last moving towards, and the
|
|
10
|
+
* other side has nothing to land on when the scroll reverses. Both sides here: an extra third of a
|
|
11
|
+
* screen of rows is cheap, and a rendered set that is a function of where the window *is* costs
|
|
12
|
+
* nothing to reason about.
|
|
13
13
|
*/
|
|
14
|
-
export declare
|
|
15
|
-
private startTime;
|
|
16
|
-
private velocity;
|
|
17
|
-
private visibleRect;
|
|
18
|
-
setVisibleRect(rect: Rect): void;
|
|
19
|
-
getOverscannedRect(): Rect;
|
|
20
|
-
}
|
|
14
|
+
export declare const overscannedRect: (visibleRect: Rect) => Rect;
|
|
@@ -1,40 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Rect } from "./virtualizer-geometry.js";
|
|
2
2
|
//#region src/utils/virtualizer-overscan.ts
|
|
3
3
|
/**
|
|
4
|
-
* How much beyond the viewport the virtualizer renders
|
|
5
|
-
* `OverscanManager`.
|
|
4
|
+
* How much beyond the viewport the virtualizer renders.
|
|
6
5
|
*
|
|
7
|
-
* A third of the viewport
|
|
8
|
-
* land on rather than a gap. The extra is added in the direction of travel only, which is why the
|
|
9
|
-
* rendered set depends on how the scroll position was arrived at and not only on where it is.
|
|
6
|
+
* A third of the viewport either side, so a scroll has something to land on rather than a gap.
|
|
10
7
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* React Aria adds it in the direction of travel only, from a velocity it samples on every move and
|
|
9
|
+
* never resets — so the rendered set depends on how an offset was arrived at rather than on the
|
|
10
|
+
* offset, a collection sitting still keeps whichever side it was last moving towards, and the
|
|
11
|
+
* other side has nothing to land on when the scroll reverses. Both sides here: an extra third of a
|
|
12
|
+
* screen of rows is cheap, and a rendered set that is a function of where the window *is* costs
|
|
13
|
+
* nothing to reason about.
|
|
14
14
|
*/
|
|
15
|
-
var
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
setVisibleRect(rect) {
|
|
20
|
-
const time = performance.now() - this.startTime;
|
|
21
|
-
if (time < 500) {
|
|
22
|
-
if (rect.x !== this.visibleRect.x && time > 0) this.velocity.x = (rect.x - this.visibleRect.x) / time;
|
|
23
|
-
if (rect.y !== this.visibleRect.y && time > 0) this.velocity.y = (rect.y - this.visibleRect.y) / time;
|
|
24
|
-
}
|
|
25
|
-
this.startTime = performance.now();
|
|
26
|
-
this.visibleRect = rect;
|
|
27
|
-
}
|
|
28
|
-
getOverscannedRect() {
|
|
29
|
-
const overscanned = this.visibleRect.copy();
|
|
30
|
-
const overscanY = this.visibleRect.height / 3;
|
|
31
|
-
const overscanX = this.visibleRect.width / 3;
|
|
32
|
-
overscanned.height += overscanY;
|
|
33
|
-
if (this.velocity.y < 0) overscanned.y -= overscanY;
|
|
34
|
-
overscanned.width += overscanX;
|
|
35
|
-
if (this.velocity.x < 0) overscanned.x -= overscanX;
|
|
36
|
-
return overscanned;
|
|
37
|
-
}
|
|
15
|
+
var overscannedRect = (visibleRect) => {
|
|
16
|
+
const overscanY = visibleRect.height / 3;
|
|
17
|
+
const overscanX = visibleRect.width / 3;
|
|
18
|
+
return new Rect(visibleRect.x - overscanX, visibleRect.y - overscanY, visibleRect.width + overscanX * 2, visibleRect.height + overscanY * 2);
|
|
38
19
|
};
|
|
39
20
|
//#endregion
|
|
40
|
-
export {
|
|
21
|
+
export { overscannedRect };
|
|
@@ -21,39 +21,68 @@ export declare class TableLayout<Options extends TableLayoutOptions = TableLayou
|
|
|
21
21
|
shouldInvalidateLayoutOptions(newOptions: Options, oldOptions: Options): boolean;
|
|
22
22
|
update(context: InvalidationContext<Options>): void;
|
|
23
23
|
private widthsChanged;
|
|
24
|
-
/**
|
|
25
|
-
* The rectangle grown to whole rows.
|
|
26
|
-
*
|
|
27
|
-
* The table's own stride, which ignores the gap and does not compensate for how far the top edge
|
|
28
|
-
* moved — the list layout does both. Kept as upstream has it, because one extra row rendered is
|
|
29
|
-
* one row of difference against the React build.
|
|
30
|
-
*/
|
|
31
|
-
protected snapVisibleRect(rect: Rect): Rect;
|
|
32
24
|
/**
|
|
33
25
|
* Every layout info to render, parents before children.
|
|
34
26
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* as an overlap. Since the rectangle is snapped to whole rows, its top edge lands on a row
|
|
39
|
-
* boundary nearly always — so the difference is a whole extra row, every time. That is what makes
|
|
40
|
-
* this a port and not a simplification.
|
|
27
|
+
* Everything a pass built is reported. The body's children *are* the window — the index decided
|
|
28
|
+
* which rows those were before any of them was placed — so there is nothing left to search over
|
|
29
|
+
* and nothing to clamp against.
|
|
41
30
|
*/
|
|
42
31
|
getVisibleLayoutInfos(rect: Rect): LayoutInfo[];
|
|
43
32
|
private addVisibleLayoutInfos;
|
|
44
|
-
/**
|
|
45
|
-
* The child whose band contains `y`, clamped to the ends.
|
|
46
|
-
*
|
|
47
|
-
* A row ending exactly on `y` counts as being before it, which is the edge rule the whole window
|
|
48
|
-
* turns on.
|
|
49
|
-
*/
|
|
50
|
-
private binarySearch;
|
|
51
33
|
protected getEstimatedRowHeight(): number;
|
|
52
34
|
protected buildCollection(): LayoutNode[];
|
|
53
35
|
protected buildTableHeader(): LayoutNode;
|
|
54
36
|
protected buildHeaderRow(node: VirtualizerNode, x: number, y: number): LayoutNode;
|
|
55
37
|
protected buildColumn(node: VirtualizerNode, x: number, y: number): LayoutNode;
|
|
38
|
+
/**
|
|
39
|
+
* How many things the body places: a row per item, and the sentinel after them.
|
|
40
|
+
*
|
|
41
|
+
* The sentinel is one of them rather than a special case bolted on after, so the index knows
|
|
42
|
+
* where it sits and the rows around it do not have to be walked to find out.
|
|
43
|
+
*/
|
|
44
|
+
protected get placedCount(): number;
|
|
45
|
+
/**
|
|
46
|
+
* Where the index puts a body child.
|
|
47
|
+
*
|
|
48
|
+
* A row is not a root here — the body is — so what the index places is a child of the body, and
|
|
49
|
+
* the offset it gives is what lets a row nobody scrolled to be placed on its own. Pressing End
|
|
50
|
+
* asks for exactly that.
|
|
51
|
+
*/
|
|
52
|
+
protected indexedOffset(node: VirtualizerNode): number | null;
|
|
53
|
+
/**
|
|
54
|
+
* Hands a measurement to the index, or to the row that owns it.
|
|
55
|
+
*
|
|
56
|
+
* A cell's measurement is not a row's: a row is as tall as its **tallest** cell, so a cell that
|
|
57
|
+
* measured means the row has to be built again from all of them. `buildRow` is what works the
|
|
58
|
+
* height out, and what tells the index.
|
|
59
|
+
*/
|
|
60
|
+
protected recordMeasuredSize(layoutNode: LayoutNode, height: number): void;
|
|
61
|
+
/** The key of the body child at `index`, which is a row until it is the sentinel. */
|
|
62
|
+
private bodyChildKey;
|
|
63
|
+
protected estimatedSizeAt(index: number): number;
|
|
64
|
+
protected configureOffsets(start?: number): void;
|
|
65
|
+
/**
|
|
66
|
+
* The body children placed wherever the window is.
|
|
67
|
+
*
|
|
68
|
+
* The sentinel, because one that is not in the DOM can never report that it came into view, so
|
|
69
|
+
* the next page would never be asked for. A persisted row, because the roving tab stop lives on
|
|
70
|
+
* it and losing its element drops focus to the document.
|
|
71
|
+
*/
|
|
72
|
+
protected indicesPlacedOutsideTheWindow(): Set<number>;
|
|
56
73
|
protected buildRowGroup(y: number, node: VirtualizerNode): LayoutNode;
|
|
74
|
+
/** A row sits below the header, not at the top of the table. */
|
|
75
|
+
protected rowOffset(index: number): number;
|
|
76
|
+
/**
|
|
77
|
+
* The cells of one row, derived rather than looked up.
|
|
78
|
+
*
|
|
79
|
+
* A virtualized table renders one cell per column, so the cell where this row meets each column
|
|
80
|
+
* is known without anyone having rendered it — and building them here rather than holding them
|
|
81
|
+
* in the collection is what keeps the node count proportional to the window instead of to the
|
|
82
|
+
* data. A cell that has been placed before keeps the node it was placed from, so a rebuilt row
|
|
83
|
+
* does not have to measure its cells again.
|
|
84
|
+
*/
|
|
85
|
+
private buildCellNodes;
|
|
57
86
|
protected buildRow(node: VirtualizerNode, x: number, y: number): LayoutNode;
|
|
58
87
|
protected buildCell(node: VirtualizerNode, x: number, y: number): LayoutNode;
|
|
59
88
|
protected buildLoader(node: VirtualizerNode, x: number, y: number): LayoutNode;
|
|
@@ -11,7 +11,7 @@ import { ListLayout } from "./virtualizer-list-layout.js";
|
|
|
11
11
|
* that the widths a browser lays a plain table out with and the widths this places cells at are
|
|
12
12
|
* the same numbers.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* Departures from upstream, all deliberate:
|
|
15
15
|
*
|
|
16
16
|
* **No horizontal windowing.** Upstream drops cells and columns that are scrolled off to the side
|
|
17
17
|
* and keeps a list of sticky indices — the selection column, and every row header column, so a row
|
|
@@ -25,8 +25,11 @@ import { ListLayout } from "./virtualizer-list-layout.js";
|
|
|
25
25
|
* visible range in three passes. Asking the virtualizer about each candidate instead gives the same
|
|
26
26
|
* set in the same order, because the children are already in order.
|
|
27
27
|
*
|
|
28
|
-
* **The
|
|
29
|
-
*
|
|
28
|
+
* **The body's children are the window.** Upstream builds a region and then binary searches inside
|
|
29
|
+
* it for the rows the rectangle covers, clamping to the ends when it finds none — which renders a
|
|
30
|
+
* single off-screen row whenever the region and the rectangle have come apart. The index decides
|
|
31
|
+
* which rows those are before any of them is placed, so there is nothing left to search and no
|
|
32
|
+
* clamp to land on.
|
|
30
33
|
*/
|
|
31
34
|
/** The height React Aria falls back to when neither a fixed nor an estimated height is given. */
|
|
32
35
|
var DEFAULT_ROW_HEIGHT = 48;
|
|
@@ -51,74 +54,26 @@ var TableLayout = class extends ListLayout {
|
|
|
51
54
|
return false;
|
|
52
55
|
}
|
|
53
56
|
/**
|
|
54
|
-
* The rectangle grown to whole rows.
|
|
55
|
-
*
|
|
56
|
-
* The table's own stride, which ignores the gap and does not compensate for how far the top edge
|
|
57
|
-
* moved — the list layout does both. Kept as upstream has it, because one extra row rendered is
|
|
58
|
-
* one row of difference against the React build.
|
|
59
|
-
*/
|
|
60
|
-
snapVisibleRect(rect) {
|
|
61
|
-
if (rect.height <= 1) return rect;
|
|
62
|
-
const rowHeight = this.getEstimatedRowHeight();
|
|
63
|
-
const snapped = rect.copy();
|
|
64
|
-
snapped.y = Math.floor(snapped.y / rowHeight) * rowHeight;
|
|
65
|
-
snapped.height = Math.ceil(snapped.height / rowHeight) * rowHeight;
|
|
66
|
-
return snapped;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
57
|
* Every layout info to render, parents before children.
|
|
70
58
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* as an overlap. Since the rectangle is snapped to whole rows, its top edge lands on a row
|
|
75
|
-
* boundary nearly always — so the difference is a whole extra row, every time. That is what makes
|
|
76
|
-
* this a port and not a simplification.
|
|
59
|
+
* Everything a pass built is reported. The body's children *are* the window — the index decided
|
|
60
|
+
* which rows those were before any of them was placed — so there is nothing left to search over
|
|
61
|
+
* and nothing to clamp against.
|
|
77
62
|
*/
|
|
78
63
|
getVisibleLayoutInfos(rect) {
|
|
79
|
-
|
|
80
|
-
this.layoutIfNeeded(searchRect);
|
|
64
|
+
this.layoutFor(this.snapVisibleRect(rect));
|
|
81
65
|
const result = [];
|
|
82
66
|
for (const node of this.rootNodes) {
|
|
83
67
|
result.push(node.layoutInfo);
|
|
84
|
-
this.addVisibleLayoutInfos(result, node
|
|
68
|
+
this.addVisibleLayoutInfos(result, node);
|
|
85
69
|
}
|
|
86
70
|
return result;
|
|
87
71
|
}
|
|
88
|
-
addVisibleLayoutInfos(result, node
|
|
89
|
-
if (node.children.length === 0) return;
|
|
90
|
-
if (node.layoutInfo.type === "rowgroup") {
|
|
91
|
-
const first = this.binarySearch(node.children, rect.y);
|
|
92
|
-
const last = this.binarySearch(node.children, rect.maxY);
|
|
93
|
-
for (const [index, child] of node.children.entries()) {
|
|
94
|
-
if (!(index >= first && index <= last || child.layoutInfo.type === "loader" || this.host.isPersistedKey(child.layoutInfo.key))) continue;
|
|
95
|
-
result.push(child.layoutInfo);
|
|
96
|
-
this.addVisibleLayoutInfos(result, child, rect);
|
|
97
|
-
}
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
72
|
+
addVisibleLayoutInfos(result, node) {
|
|
100
73
|
for (const child of node.children) {
|
|
101
74
|
result.push(child.layoutInfo);
|
|
102
|
-
this.addVisibleLayoutInfos(result, child
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* The child whose band contains `y`, clamped to the ends.
|
|
107
|
-
*
|
|
108
|
-
* A row ending exactly on `y` counts as being before it, which is the edge rule the whole window
|
|
109
|
-
* turns on.
|
|
110
|
-
*/
|
|
111
|
-
binarySearch(items, y) {
|
|
112
|
-
let low = 0;
|
|
113
|
-
let high = items.length - 1;
|
|
114
|
-
while (low <= high) {
|
|
115
|
-
const mid = low + high >> 1;
|
|
116
|
-
const rect = items[mid].layoutInfo.rect;
|
|
117
|
-
if (rect.maxY <= y) low = mid + 1;
|
|
118
|
-
else if (rect.y > y) high = mid - 1;
|
|
119
|
-
else return mid;
|
|
75
|
+
this.addVisibleLayoutInfos(result, child);
|
|
120
76
|
}
|
|
121
|
-
return Math.max(0, Math.min(items.length - 1, low));
|
|
122
77
|
}
|
|
123
78
|
getEstimatedRowHeight() {
|
|
124
79
|
return this.rowSize ?? this.estimatedRowSize ?? DEFAULT_ROW_HEIGHT;
|
|
@@ -172,8 +127,7 @@ var TableLayout = class extends ListLayout {
|
|
|
172
127
|
return {
|
|
173
128
|
children,
|
|
174
129
|
layoutInfo,
|
|
175
|
-
node: collection.getNode(collection.headerKey)
|
|
176
|
-
validRect: rect
|
|
130
|
+
node: collection.getNode(collection.headerKey)
|
|
177
131
|
};
|
|
178
132
|
}
|
|
179
133
|
buildHeaderRow(node, x, y) {
|
|
@@ -196,8 +150,7 @@ var TableLayout = class extends ListLayout {
|
|
|
196
150
|
return {
|
|
197
151
|
children: columns,
|
|
198
152
|
layoutInfo,
|
|
199
|
-
node
|
|
200
|
-
validRect: rect
|
|
153
|
+
node
|
|
201
154
|
};
|
|
202
155
|
}
|
|
203
156
|
buildColumn(node, x, y) {
|
|
@@ -209,49 +162,139 @@ var TableLayout = class extends ListLayout {
|
|
|
209
162
|
return {
|
|
210
163
|
children: [],
|
|
211
164
|
layoutInfo,
|
|
212
|
-
node
|
|
213
|
-
validRect: layoutInfo.rect
|
|
165
|
+
node
|
|
214
166
|
};
|
|
215
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* How many things the body places: a row per item, and the sentinel after them.
|
|
170
|
+
*
|
|
171
|
+
* The sentinel is one of them rather than a special case bolted on after, so the index knows
|
|
172
|
+
* where it sits and the rows around it do not have to be walked to find out.
|
|
173
|
+
*/
|
|
174
|
+
get placedCount() {
|
|
175
|
+
const collection = this.tableCollection;
|
|
176
|
+
return collection.rows.keys.length + (collection.loaderKey == null ? 0 : 1);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Where the index puts a body child.
|
|
180
|
+
*
|
|
181
|
+
* A row is not a root here — the body is — so what the index places is a child of the body, and
|
|
182
|
+
* the offset it gives is what lets a row nobody scrolled to be placed on its own. Pressing End
|
|
183
|
+
* asks for exactly that.
|
|
184
|
+
*/
|
|
185
|
+
indexedOffset(node) {
|
|
186
|
+
if (node.parentKey !== this.tableCollection.bodyKey) return null;
|
|
187
|
+
return this.offsets.startOf(node.index);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Hands a measurement to the index, or to the row that owns it.
|
|
191
|
+
*
|
|
192
|
+
* A cell's measurement is not a row's: a row is as tall as its **tallest** cell, so a cell that
|
|
193
|
+
* measured means the row has to be built again from all of them. `buildRow` is what works the
|
|
194
|
+
* height out, and what tells the index.
|
|
195
|
+
*/
|
|
196
|
+
recordMeasuredSize(layoutNode, height) {
|
|
197
|
+
const node = layoutNode.node;
|
|
198
|
+
if (!node) return;
|
|
199
|
+
if (node.type === "cell") {
|
|
200
|
+
if (node.parentKey != null) this.layoutNodes.delete(node.parentKey);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (node.parentKey === this.tableCollection.bodyKey) this.offsets.measure(node.key, height, node.index);
|
|
204
|
+
}
|
|
205
|
+
/** The key of the body child at `index`, which is a row until it is the sentinel. */
|
|
206
|
+
bodyChildKey(index) {
|
|
207
|
+
const collection = this.tableCollection;
|
|
208
|
+
const rowKeys = collection.rows.keys;
|
|
209
|
+
return index < rowKeys.length ? rowKeys[index] : collection.loaderKey;
|
|
210
|
+
}
|
|
211
|
+
estimatedSizeAt(index) {
|
|
212
|
+
if (index >= this.tableCollection.rows.keys.length) return this.loaderSize ?? this.getEstimatedRowHeight();
|
|
213
|
+
return this.getEstimatedRowHeight();
|
|
214
|
+
}
|
|
215
|
+
configureOffsets(start = this.padding) {
|
|
216
|
+
this.offsets.configure({
|
|
217
|
+
collection: this.tableCollection,
|
|
218
|
+
count: this.placedCount,
|
|
219
|
+
estimate: (index) => this.estimatedSizeAt(index),
|
|
220
|
+
gap: this.gap,
|
|
221
|
+
keyAt: (index) => this.bodyChildKey(index) ?? index,
|
|
222
|
+
start
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* The body children placed wherever the window is.
|
|
227
|
+
*
|
|
228
|
+
* The sentinel, because one that is not in the DOM can never report that it came into view, so
|
|
229
|
+
* the next page would never be asked for. A persisted row, because the roving tab stop lives on
|
|
230
|
+
* it and losing its element drops focus to the document.
|
|
231
|
+
*/
|
|
232
|
+
indicesPlacedOutsideTheWindow() {
|
|
233
|
+
const collection = this.tableCollection;
|
|
234
|
+
const indices = this.persistedIndices(collection.rows.keys);
|
|
235
|
+
if (collection.loaderKey != null) indices.add(collection.rows.keys.length);
|
|
236
|
+
return indices;
|
|
237
|
+
}
|
|
216
238
|
buildRowGroup(y, node) {
|
|
217
239
|
const collection = this.tableCollection;
|
|
218
240
|
const rect = new Rect(this.padding, y, 0, 0);
|
|
219
241
|
const layoutInfo = new LayoutInfo("rowgroup", node.key, rect);
|
|
220
242
|
const children = [];
|
|
221
|
-
const rowHeight = this.getEstimatedRowHeight() + this.gap;
|
|
222
243
|
const startY = y;
|
|
223
|
-
|
|
244
|
+
this.configureOffsets(startY);
|
|
224
245
|
let width = 0;
|
|
225
|
-
for (const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
width = Math.max(width, row.layoutInfo.rect.width);
|
|
235
|
-
children.push(row);
|
|
246
|
+
for (const index of this.windowedIndices(this.window)) {
|
|
247
|
+
const key = this.bodyChildKey(index);
|
|
248
|
+
const child = key == null ? void 0 : collection.getNode(key);
|
|
249
|
+
if (!child) continue;
|
|
250
|
+
const placed = this.buildChild(child, this.padding, this.offsets.startOf(index), layoutInfo.key);
|
|
251
|
+
placed.layoutInfo.parentKey = layoutInfo.key;
|
|
252
|
+
placed.index = children.length;
|
|
253
|
+
width = Math.max(width, placed.layoutInfo.rect.width);
|
|
254
|
+
children.push(placed);
|
|
236
255
|
}
|
|
237
|
-
if (collection.itemCount === 0) y = this.host.size.height;
|
|
238
|
-
else y -= this.gap;
|
|
239
256
|
rect.width = width;
|
|
240
|
-
rect.height =
|
|
257
|
+
rect.height = collection.itemCount === 0 ? this.host.size.height - startY : this.offsets.total() - startY;
|
|
241
258
|
return {
|
|
242
259
|
children,
|
|
243
260
|
layoutInfo,
|
|
244
|
-
node
|
|
245
|
-
validRect: rect.intersection(this.requestedRect)
|
|
261
|
+
node
|
|
246
262
|
};
|
|
247
263
|
}
|
|
264
|
+
/** A row sits below the header, not at the top of the table. */
|
|
265
|
+
rowOffset(index) {
|
|
266
|
+
const header = this.layoutNodes.get(this.tableCollection.headerKey);
|
|
267
|
+
return (header == null ? this.padding : header.layoutInfo.rect.maxY + this.gap) + index * (this.getEstimatedRowHeight() + this.gap);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* The cells of one row, derived rather than looked up.
|
|
271
|
+
*
|
|
272
|
+
* A virtualized table renders one cell per column, so the cell where this row meets each column
|
|
273
|
+
* is known without anyone having rendered it — and building them here rather than holding them
|
|
274
|
+
* in the collection is what keeps the node count proportional to the window instead of to the
|
|
275
|
+
* data. A cell that has been placed before keeps the node it was placed from, so a rebuilt row
|
|
276
|
+
* does not have to measure its cells again.
|
|
277
|
+
*/
|
|
278
|
+
buildCellNodes(rowKey) {
|
|
279
|
+
const collection = this.tableCollection;
|
|
280
|
+
return collection.columnKeys.map((columnKey, index) => {
|
|
281
|
+
const key = collection.cellKey(rowKey, columnKey);
|
|
282
|
+
const placed = this.layoutNodes.get(key)?.node;
|
|
283
|
+
return placed?.index === index ? placed : {
|
|
284
|
+
childKeys: [],
|
|
285
|
+
index,
|
|
286
|
+
key,
|
|
287
|
+
parentKey: rowKey,
|
|
288
|
+
type: "cell"
|
|
289
|
+
};
|
|
290
|
+
});
|
|
291
|
+
}
|
|
248
292
|
buildRow(node, x, y) {
|
|
249
293
|
const rect = new Rect(x, y, 0, 0);
|
|
250
294
|
const layoutInfo = new LayoutInfo("row", node.key, rect);
|
|
251
295
|
const children = [];
|
|
252
296
|
let height = 0;
|
|
253
|
-
for (const child of this.
|
|
254
|
-
if (child.type !== "cell") continue;
|
|
297
|
+
for (const child of this.buildCellNodes(node.key)) {
|
|
255
298
|
const cell = this.buildChild(child, x, y, layoutInfo.key);
|
|
256
299
|
cell.index = children.length;
|
|
257
300
|
x = cell.layoutInfo.rect.maxX;
|
|
@@ -261,11 +304,11 @@ var TableLayout = class extends ListLayout {
|
|
|
261
304
|
this.setChildHeights(children, height);
|
|
262
305
|
rect.width = this.headerWidth();
|
|
263
306
|
rect.height = height;
|
|
307
|
+
if (this.rowSize == null) this.offsets.measure(node.key, height, node.index);
|
|
264
308
|
return {
|
|
265
309
|
children,
|
|
266
310
|
layoutInfo,
|
|
267
|
-
node
|
|
268
|
-
validRect: rect.intersection(this.requestedRect)
|
|
311
|
+
node
|
|
269
312
|
};
|
|
270
313
|
}
|
|
271
314
|
buildCell(node, x, y) {
|
|
@@ -277,14 +320,12 @@ var TableLayout = class extends ListLayout {
|
|
|
277
320
|
return {
|
|
278
321
|
children: [],
|
|
279
322
|
layoutInfo,
|
|
280
|
-
node
|
|
281
|
-
validRect: layoutInfo.rect
|
|
323
|
+
node
|
|
282
324
|
};
|
|
283
325
|
}
|
|
284
326
|
buildLoader(node, x, y) {
|
|
285
327
|
const layoutNode = super.buildLoader(node, x, y);
|
|
286
328
|
layoutNode.layoutInfo.rect.width = this.headerWidth();
|
|
287
|
-
layoutNode.validRect = layoutNode.layoutInfo.rect.intersection(this.requestedRect);
|
|
288
329
|
return layoutNode;
|
|
289
330
|
}
|
|
290
331
|
buildNode(node, x, y) {
|
package/dist/version.js
CHANGED