@sectile/virtual 0.7.0 → 0.9.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/README.md +3 -3
- package/dist/error.d.ts +1 -2
- package/dist/extent-index.d.ts +7 -1
- package/dist/extent-index.js +93 -83
- package/dist/extent-index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/internal/blocked-vector.d.ts +20 -0
- package/dist/internal/blocked-vector.js +171 -0
- package/dist/internal/blocked-vector.js.map +1 -0
- package/dist/internal/foundation.d.ts +0 -2
- package/dist/internal/foundation.js +3 -13
- package/dist/internal/foundation.js.map +1 -1
- package/dist/internal/repair-diagnostics.d.ts +11 -0
- package/dist/internal/repair-diagnostics.js +8 -0
- package/dist/internal/repair-diagnostics.js.map +1 -0
- package/dist/internal/track.d.ts +0 -1
- package/dist/layout.d.ts +18 -20
- package/dist/layout.js +7 -21
- package/dist/layout.js.map +1 -1
- package/dist/linear-layout.d.ts +0 -1
- package/dist/linear-layout.js +27 -2
- package/dist/linear-layout.js.map +1 -1
- package/dist/masonry-layout.d.ts +0 -1
- package/dist/masonry-layout.js +73 -17
- package/dist/masonry-layout.js.map +1 -1
- package/dist/partitioned-track-grid-layout.d.ts +87 -0
- package/dist/partitioned-track-grid-layout.js +585 -0
- package/dist/partitioned-track-grid-layout.js.map +1 -0
- package/dist/spatial-layout.d.ts +7 -4
- package/dist/spatial-layout.js +217 -56
- package/dist/spatial-layout.js.map +1 -1
- package/dist/track-grid-layout.d.ts +17 -3
- package/dist/track-grid-layout.js +211 -15
- package/dist/track-grid-layout.js.map +1 -1
- package/package.json +8 -3
- package/dist/error.d.ts.map +0 -1
- package/dist/extent-index.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/internal/foundation.d.ts.map +0 -1
- package/dist/internal/machine.d.ts +0 -4
- package/dist/internal/machine.d.ts.map +0 -1
- package/dist/internal/machine.js +0 -8
- package/dist/internal/machine.js.map +0 -1
- package/dist/internal/track.d.ts.map +0 -1
- package/dist/layout.d.ts.map +0 -1
- package/dist/linear-layout.d.ts.map +0 -1
- package/dist/masonry-layout.d.ts.map +0 -1
- package/dist/spatial-layout.d.ts.map +0 -1
- package/dist/track-grid-layout.d.ts.map +0 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { StableID } from '@sectile/core';
|
|
2
|
+
import type { VirtualResult } from './error.js';
|
|
3
|
+
import { type Extent } from './extent-index.js';
|
|
4
|
+
import { type VirtualAnchor, type VirtualLayoutMutation, type VirtualLayoutPlan, type VirtualLayoutStrategy, type VirtualIndexedView, type VirtualMeasurementBatch, type VirtualPoint, type VirtualQueryInput, type VirtualRect, type VirtualScrollAlignment } from './layout.js';
|
|
5
|
+
export type TrackPartition = 'start' | 'center' | 'end';
|
|
6
|
+
export interface PartitionedTrack<ID extends StableID = StableID> {
|
|
7
|
+
readonly id: ID;
|
|
8
|
+
readonly partition: TrackPartition;
|
|
9
|
+
readonly extent: Extent;
|
|
10
|
+
}
|
|
11
|
+
export interface PartitionedTrackGridRegion<ID extends StableID = StableID, RowID extends StableID = StableID, ColumnID extends StableID = StableID> {
|
|
12
|
+
readonly id: ID;
|
|
13
|
+
readonly row: RowID;
|
|
14
|
+
readonly column: ColumnID;
|
|
15
|
+
readonly rowSpan?: number;
|
|
16
|
+
readonly columnSpan?: number;
|
|
17
|
+
}
|
|
18
|
+
declare const partitionedTrackGridLayoutStateBrand: unique symbol;
|
|
19
|
+
export interface PartitionedTrackGridLayoutState<ID extends StableID = StableID, RowID extends StableID = StableID, ColumnID extends StableID = StableID> {
|
|
20
|
+
readonly [partitionedTrackGridLayoutStateBrand]: true;
|
|
21
|
+
readonly rows: VirtualIndexedView<PartitionedTrack<RowID>>;
|
|
22
|
+
readonly columns: VirtualIndexedView<PartitionedTrack<ColumnID>>;
|
|
23
|
+
readonly regions: readonly PartitionedTrackGridRegion<ID, RowID, ColumnID>[];
|
|
24
|
+
readonly rowGap: number;
|
|
25
|
+
readonly columnGap: number;
|
|
26
|
+
readonly maxTracks: number;
|
|
27
|
+
readonly maxRegions: number;
|
|
28
|
+
readonly generation: number;
|
|
29
|
+
}
|
|
30
|
+
export interface PartitionedTrackGridLayoutSnapshot<ID extends StableID = StableID, RowID extends StableID = StableID, ColumnID extends StableID = StableID> {
|
|
31
|
+
readonly schemaVersion: 1;
|
|
32
|
+
readonly kind: 'partitioned-track-grid';
|
|
33
|
+
readonly rows: readonly PartitionedTrack<RowID>[];
|
|
34
|
+
readonly columns: readonly PartitionedTrack<ColumnID>[];
|
|
35
|
+
readonly regions: readonly PartitionedTrackGridRegion<ID, RowID, ColumnID>[];
|
|
36
|
+
readonly rowGap: number;
|
|
37
|
+
readonly columnGap: number;
|
|
38
|
+
readonly maxTracks: number;
|
|
39
|
+
readonly maxRegions: number;
|
|
40
|
+
readonly generation: number;
|
|
41
|
+
}
|
|
42
|
+
export interface PartitionedTrackGridLayoutInput {
|
|
43
|
+
readonly rowGap?: number;
|
|
44
|
+
readonly columnGap?: number;
|
|
45
|
+
readonly maxTracks?: number;
|
|
46
|
+
readonly maxRegions?: number;
|
|
47
|
+
}
|
|
48
|
+
export interface PartitionedTrackGridLayoutPlan<ID extends StableID = StableID> extends VirtualLayoutPlan<ID> {
|
|
49
|
+
readonly pinnedStartWidth: number;
|
|
50
|
+
readonly pinnedEndWidth: number;
|
|
51
|
+
readonly pinnedStartHeight: number;
|
|
52
|
+
readonly pinnedEndHeight: number;
|
|
53
|
+
}
|
|
54
|
+
export type PartitionedTrackGridMeasurement<RowID extends StableID = StableID, ColumnID extends StableID = StableID> = {
|
|
55
|
+
readonly axis: 'row';
|
|
56
|
+
readonly id: RowID;
|
|
57
|
+
readonly extent: Extent;
|
|
58
|
+
} | {
|
|
59
|
+
readonly axis: 'column';
|
|
60
|
+
readonly id: ColumnID;
|
|
61
|
+
readonly extent: Extent;
|
|
62
|
+
};
|
|
63
|
+
export type PartitionedTrackGridMutation<ID extends StableID = StableID, RowID extends StableID = StableID, ColumnID extends StableID = StableID> = {
|
|
64
|
+
readonly type: 'replace-regions';
|
|
65
|
+
readonly regions: readonly PartitionedTrackGridRegion<ID, RowID, ColumnID>[];
|
|
66
|
+
} | {
|
|
67
|
+
readonly type: 'replace-row-tracks';
|
|
68
|
+
readonly tracks: readonly PartitionedTrack<RowID>[];
|
|
69
|
+
} | {
|
|
70
|
+
readonly type: 'replace-column-tracks';
|
|
71
|
+
readonly tracks: readonly PartitionedTrack<ColumnID>[];
|
|
72
|
+
};
|
|
73
|
+
export declare const partitionedTrackGridLayoutStrategy: VirtualLayoutStrategy<PartitionedTrackGridLayoutState, StableID, PartitionedTrackGridMeasurement, PartitionedTrackGridMutation>;
|
|
74
|
+
export declare function createPartitionedTrackGridLayout<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(rows: readonly PartitionedTrack<RowID>[], columns: readonly PartitionedTrack<ColumnID>[], regions: readonly PartitionedTrackGridRegion<ID, RowID, ColumnID>[], input?: PartitionedTrackGridLayoutInput): PartitionedTrackGridLayoutState<ID, RowID, ColumnID>;
|
|
75
|
+
export declare function tryCreatePartitionedTrackGridLayout<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(rows: readonly PartitionedTrack<RowID>[], columns: readonly PartitionedTrack<ColumnID>[], regions: readonly PartitionedTrackGridRegion<ID, RowID, ColumnID>[], input?: PartitionedTrackGridLayoutInput): VirtualResult<PartitionedTrackGridLayoutState<ID, RowID, ColumnID>>;
|
|
76
|
+
export declare function snapshotPartitionedTrackGridLayout<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(state: PartitionedTrackGridLayoutState<ID, RowID, ColumnID>): PartitionedTrackGridLayoutSnapshot<ID, RowID, ColumnID>;
|
|
77
|
+
export declare function restorePartitionedTrackGridLayout<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(snapshot: PartitionedTrackGridLayoutSnapshot<ID, RowID, ColumnID>): PartitionedTrackGridLayoutState<ID, RowID, ColumnID>;
|
|
78
|
+
export declare function tryRestorePartitionedTrackGridLayout<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(snapshot: PartitionedTrackGridLayoutSnapshot<ID, RowID, ColumnID>): VirtualResult<PartitionedTrackGridLayoutState<ID, RowID, ColumnID>>;
|
|
79
|
+
export declare function queryPartitionedTrackGridLayout<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(state: PartitionedTrackGridLayoutState<ID, RowID, ColumnID>, input: VirtualQueryInput): PartitionedTrackGridLayoutPlan<ID>;
|
|
80
|
+
export declare function tryQueryPartitionedTrackGridLayout<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(state: PartitionedTrackGridLayoutState<ID, RowID, ColumnID>, input: VirtualQueryInput): VirtualResult<PartitionedTrackGridLayoutPlan<ID>>;
|
|
81
|
+
export declare function applyPartitionedTrackGridMeasurements<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(state: PartitionedTrackGridLayoutState<ID, RowID, ColumnID>, batch: VirtualMeasurementBatch<PartitionedTrackGridMeasurement<RowID, ColumnID>, ID>): VirtualLayoutMutation<PartitionedTrackGridLayoutState<ID, RowID, ColumnID>>;
|
|
82
|
+
export declare function tryApplyPartitionedTrackGridMeasurements<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(state: PartitionedTrackGridLayoutState<ID, RowID, ColumnID>, batch: VirtualMeasurementBatch<PartitionedTrackGridMeasurement<RowID, ColumnID>, ID>): VirtualResult<VirtualLayoutMutation<PartitionedTrackGridLayoutState<ID, RowID, ColumnID>>>;
|
|
83
|
+
export declare function applyPartitionedTrackGridMutation<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(state: PartitionedTrackGridLayoutState<ID, RowID, ColumnID>, mutation: PartitionedTrackGridMutation<ID, RowID, ColumnID>, anchor?: VirtualAnchor<ID> | null): VirtualLayoutMutation<PartitionedTrackGridLayoutState<ID, RowID, ColumnID>>;
|
|
84
|
+
export declare function tryApplyPartitionedTrackGridMutation<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(state: PartitionedTrackGridLayoutState<ID, RowID, ColumnID>, mutation: PartitionedTrackGridMutation<ID, RowID, ColumnID>, anchor?: VirtualAnchor<ID> | null): VirtualResult<VirtualLayoutMutation<PartitionedTrackGridLayoutState<ID, RowID, ColumnID>>>;
|
|
85
|
+
export declare function partitionedTrackGridScrollTarget<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(state: PartitionedTrackGridLayoutState<ID, RowID, ColumnID>, id: ID, viewport: VirtualRect, alignment?: VirtualScrollAlignment): VirtualPoint;
|
|
86
|
+
export declare function tryPartitionedTrackGridScrollTarget<ID extends StableID, RowID extends StableID, ColumnID extends StableID>(state: PartitionedTrackGridLayoutState<ID, RowID, ColumnID>, id: ID, viewport: VirtualRect, alignment?: VirtualScrollAlignment): VirtualResult<VirtualPoint>;
|
|
87
|
+
export {};
|
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
import { unwrap } from '@sectile/core/result';
|
|
2
|
+
import { tryCreateSequence } from '@sectile/core/sequence';
|
|
3
|
+
import { tryCreateExtentIndex } from './extent-index.js';
|
|
4
|
+
import { blockedTrackRepairBound, createBlockedVector, useBlockedTrackRepair } from './internal/blocked-vector.js';
|
|
5
|
+
import { fail, ok } from './internal/foundation.js';
|
|
6
|
+
import { recordRepairDiagnostics } from './internal/repair-diagnostics.js';
|
|
7
|
+
import { alignedScrollOffset, anchorForPlan, normalizeQuery, pointDelta, rectanglesIntersect, ZERO_POINT, } from './layout.js';
|
|
8
|
+
import { trackGridRegionRect, tryApplyGridMeasurements, tryCreateTrackGridLayout, tryQueryTrackGridLayout, } from './track-grid-layout.js';
|
|
9
|
+
const partitionedTrackGridLayoutStateBrand = Symbol('SectilePartitionedTrackGridLayoutState');
|
|
10
|
+
const internals = new WeakMap();
|
|
11
|
+
export const partitionedTrackGridLayoutStrategy = Object.freeze({
|
|
12
|
+
kind: 'partitioned-track-grid',
|
|
13
|
+
tryQuery: (state, input) => tryQueryPartitionedTrackGridLayout(state, input),
|
|
14
|
+
tryMeasure: (state, batch) => tryApplyPartitionedTrackGridMeasurements(state, batch),
|
|
15
|
+
tryMutate: (state, input) => tryApplyPartitionedTrackGridMutation(state, input.mutation, input.anchor),
|
|
16
|
+
tryScrollTarget: (state, id, viewport, alignment) => tryPartitionedTrackGridScrollTarget(state, id, viewport, alignment),
|
|
17
|
+
});
|
|
18
|
+
export function createPartitionedTrackGridLayout(rows, columns, regions, input = {}) {
|
|
19
|
+
return unwrap(tryCreatePartitionedTrackGridLayout(rows, columns, regions, input));
|
|
20
|
+
}
|
|
21
|
+
export function tryCreatePartitionedTrackGridLayout(rows, columns, regions, input = {}) {
|
|
22
|
+
const rowGap = input.rowGap ?? 0;
|
|
23
|
+
const columnGap = input.columnGap ?? 0;
|
|
24
|
+
const maxTracks = input.maxTracks ?? 1_000_000;
|
|
25
|
+
const maxRegions = input.maxRegions ?? 1_000_000;
|
|
26
|
+
if (!finiteNonNegative(rowGap) || !finiteNonNegative(columnGap)) {
|
|
27
|
+
return fail('construction', 'virtual-layout-geometry-invalid', 'Partitioned grid gaps must be finite and non-negative.', { rowGap, columnGap });
|
|
28
|
+
}
|
|
29
|
+
if (!nonNegativeSafe(maxTracks) || !nonNegativeSafe(maxRegions)) {
|
|
30
|
+
return fail('construction', 'invalid-max-items', 'Partitioned grid ceilings must be non-negative safe integers.', { maxTracks, maxRegions });
|
|
31
|
+
}
|
|
32
|
+
const normalizedRows = normalizeTracks(rows, maxTracks);
|
|
33
|
+
if (!normalizedRows.ok)
|
|
34
|
+
return normalizedRows;
|
|
35
|
+
const normalizedColumns = normalizeTracks(columns, maxTracks);
|
|
36
|
+
if (!normalizedColumns.ok)
|
|
37
|
+
return normalizedColumns;
|
|
38
|
+
return createState({
|
|
39
|
+
rows: normalizedRows.value,
|
|
40
|
+
columns: normalizedColumns.value,
|
|
41
|
+
regions: freezeRegions(regions),
|
|
42
|
+
rowGap,
|
|
43
|
+
columnGap,
|
|
44
|
+
maxTracks,
|
|
45
|
+
maxRegions,
|
|
46
|
+
generation: 0,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export function snapshotPartitionedTrackGridLayout(state) {
|
|
50
|
+
requireInternals(state);
|
|
51
|
+
return Object.freeze({
|
|
52
|
+
schemaVersion: 1,
|
|
53
|
+
kind: 'partitioned-track-grid',
|
|
54
|
+
rows: freezeTrackView(state.rows),
|
|
55
|
+
columns: freezeTrackView(state.columns),
|
|
56
|
+
regions: freezeRegions(state.regions),
|
|
57
|
+
rowGap: state.rowGap,
|
|
58
|
+
columnGap: state.columnGap,
|
|
59
|
+
maxTracks: state.maxTracks,
|
|
60
|
+
maxRegions: state.maxRegions,
|
|
61
|
+
generation: state.generation,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export function restorePartitionedTrackGridLayout(snapshot) {
|
|
65
|
+
return unwrap(tryRestorePartitionedTrackGridLayout(snapshot));
|
|
66
|
+
}
|
|
67
|
+
export function tryRestorePartitionedTrackGridLayout(snapshot) {
|
|
68
|
+
if (!validSnapshotHeader(snapshot))
|
|
69
|
+
return snapshotFailure();
|
|
70
|
+
const created = tryCreatePartitionedTrackGridLayout(snapshot.rows, snapshot.columns, snapshot.regions, snapshot);
|
|
71
|
+
if (!created.ok)
|
|
72
|
+
return created;
|
|
73
|
+
const data = getInternals(created.value);
|
|
74
|
+
if (!data.ok)
|
|
75
|
+
return data;
|
|
76
|
+
return createState({
|
|
77
|
+
rows: data.value.rows.view.toArray(),
|
|
78
|
+
columns: data.value.columns.view.toArray(),
|
|
79
|
+
regions: created.value.regions,
|
|
80
|
+
rowGap: created.value.rowGap,
|
|
81
|
+
columnGap: created.value.columnGap,
|
|
82
|
+
maxTracks: created.value.maxTracks,
|
|
83
|
+
maxRegions: created.value.maxRegions,
|
|
84
|
+
generation: snapshot.generation,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
export function queryPartitionedTrackGridLayout(state, input) {
|
|
88
|
+
return unwrap(tryQueryPartitionedTrackGridLayout(state, input));
|
|
89
|
+
}
|
|
90
|
+
export function tryQueryPartitionedTrackGridLayout(state, input) {
|
|
91
|
+
const normalized = normalizeQuery(input);
|
|
92
|
+
if (!normalized.ok)
|
|
93
|
+
return normalized;
|
|
94
|
+
const data = getInternals(state);
|
|
95
|
+
if (!data.ok)
|
|
96
|
+
return data;
|
|
97
|
+
const rowSegments = querySegments(data.value.rowRanges, normalized.value.renderBounds.y, normalized.value.renderBounds.height);
|
|
98
|
+
const columnSegments = querySegments(data.value.columnRanges, normalized.value.renderBounds.x, normalized.value.renderBounds.width);
|
|
99
|
+
const candidates = new Map();
|
|
100
|
+
for (const row of rowSegments) {
|
|
101
|
+
for (const column of columnSegments) {
|
|
102
|
+
const queried = tryQueryTrackGridLayout(data.value.grid, {
|
|
103
|
+
viewport: Object.freeze({ x: column.offset, y: row.offset, width: column.extent, height: row.extent }),
|
|
104
|
+
});
|
|
105
|
+
if (!queried.ok)
|
|
106
|
+
return queried;
|
|
107
|
+
for (const placement of queried.value.placements)
|
|
108
|
+
candidates.set(placement.id, placement);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const placements = [];
|
|
112
|
+
for (const placement of candidates.values()) {
|
|
113
|
+
const region = state.regions[data.value.regionIndex.get(placement.id) ?? -1];
|
|
114
|
+
if (region === undefined)
|
|
115
|
+
return domainMismatch('Partitioned region index is stale.');
|
|
116
|
+
const rowIndex = data.value.rowIndex.get(region.row);
|
|
117
|
+
const columnIndex = data.value.columnIndex.get(region.column);
|
|
118
|
+
if (rowIndex === undefined || columnIndex === undefined)
|
|
119
|
+
return domainMismatch('Partitioned region track index is stale.');
|
|
120
|
+
const rowPartition = data.value.rowPartitions[rowIndex];
|
|
121
|
+
const columnPartition = data.value.columnPartitions[columnIndex];
|
|
122
|
+
const rect = projectRect(placement.rect, rowPartition, columnPartition, data.value.rowRanges, data.value.columnRanges, normalized.value.viewport);
|
|
123
|
+
if (!rectanglesIntersect(rect, normalized.value.renderBounds))
|
|
124
|
+
continue;
|
|
125
|
+
const zIndex = (rowPartition === 'center' ? 0 : 1) + (columnPartition === 'center' ? 0 : 1);
|
|
126
|
+
placements.push(Object.freeze({
|
|
127
|
+
id: placement.id,
|
|
128
|
+
index: placement.index,
|
|
129
|
+
rect,
|
|
130
|
+
visible: rectanglesIntersect(rect, normalized.value.viewport),
|
|
131
|
+
...(zIndex === 0 ? {} : { zIndex }),
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
placements.sort((left, right) => (left.zIndex ?? 0) - (right.zIndex ?? 0) || left.index - right.index);
|
|
135
|
+
const frozen = Object.freeze(placements);
|
|
136
|
+
return ok(Object.freeze({
|
|
137
|
+
generation: state.generation,
|
|
138
|
+
contentSize: Object.freeze({ width: data.value.grid.columns.totalExtent + gapExtent(state.columns.size, state.columnGap), height: data.value.grid.rows.totalExtent + gapExtent(state.rows.size, state.rowGap) }),
|
|
139
|
+
viewport: normalized.value.viewport,
|
|
140
|
+
renderBounds: normalized.value.renderBounds,
|
|
141
|
+
placements: frozen,
|
|
142
|
+
anchor: anchorForPlan(normalized.value.viewport, frozen),
|
|
143
|
+
pinnedStartWidth: data.value.columnRanges.start.extent,
|
|
144
|
+
pinnedEndWidth: data.value.columnRanges.end.extent,
|
|
145
|
+
pinnedStartHeight: data.value.rowRanges.start.extent,
|
|
146
|
+
pinnedEndHeight: data.value.rowRanges.end.extent,
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
export function applyPartitionedTrackGridMeasurements(state, batch) {
|
|
150
|
+
return unwrap(tryApplyPartitionedTrackGridMeasurements(state, batch));
|
|
151
|
+
}
|
|
152
|
+
export function tryApplyPartitionedTrackGridMeasurements(state, batch) {
|
|
153
|
+
if (batch.generation !== state.generation) {
|
|
154
|
+
return fail('transition-rejection', 'virtual-layout-measurement-stale', 'Measurement generation is stale.', { generation: batch.generation, activeGeneration: state.generation });
|
|
155
|
+
}
|
|
156
|
+
if (batch.measurements.length === 0)
|
|
157
|
+
return ok(Object.freeze({ state, scrollDelta: ZERO_POINT }));
|
|
158
|
+
const data = getInternals(state);
|
|
159
|
+
if (!data.ok)
|
|
160
|
+
return data;
|
|
161
|
+
const seen = new Set();
|
|
162
|
+
const rowChanges = [];
|
|
163
|
+
const columnChanges = [];
|
|
164
|
+
const gridMeasurements = [];
|
|
165
|
+
for (const measurement of batch.measurements) {
|
|
166
|
+
const key = `${measurement.axis}:${measurement.id}`;
|
|
167
|
+
if (seen.has(key))
|
|
168
|
+
return measurementFailure('Measurements must target each track at most once.', measurement);
|
|
169
|
+
seen.add(key);
|
|
170
|
+
const index = measurement.axis === 'row'
|
|
171
|
+
? data.value.rowIndex.get(measurement.id)
|
|
172
|
+
: data.value.columnIndex.get(measurement.id);
|
|
173
|
+
if (index === undefined)
|
|
174
|
+
return measurementFailure('Measurement target must exist in the active track domain.', measurement);
|
|
175
|
+
const source = measurement.axis === 'row' ? data.value.rows.at(index) : data.value.columns.at(index);
|
|
176
|
+
if (source === undefined)
|
|
177
|
+
return domainMismatch('Measurement track index is stale.');
|
|
178
|
+
if (sameExtent(source.extent, measurement.extent))
|
|
179
|
+
continue;
|
|
180
|
+
const updated = Object.freeze({ ...source, extent: Object.freeze({ ...measurement.extent }) });
|
|
181
|
+
if (measurement.axis === 'row')
|
|
182
|
+
rowChanges.push([index, updated]);
|
|
183
|
+
else
|
|
184
|
+
columnChanges.push([index, updated]);
|
|
185
|
+
gridMeasurements.push(Object.freeze({ axis: measurement.axis, index, extent: measurement.extent }));
|
|
186
|
+
}
|
|
187
|
+
if (gridMeasurements.length === 0)
|
|
188
|
+
return ok(Object.freeze({ state, scrollDelta: ZERO_POINT }));
|
|
189
|
+
const generation = nextGeneration(state.generation);
|
|
190
|
+
if (!generation.ok)
|
|
191
|
+
return generation;
|
|
192
|
+
const before = baseAnchorRect(state, batch.anchor);
|
|
193
|
+
let next;
|
|
194
|
+
rowChanges.sort(([left], [right]) => left - right);
|
|
195
|
+
columnChanges.sort(([left], [right]) => left - right);
|
|
196
|
+
const trackCount = state.rows.size + state.columns.size;
|
|
197
|
+
const repairBound = blockedTrackRepairBound(gridMeasurements.length, trackCount);
|
|
198
|
+
if (useBlockedTrackRepair(gridMeasurements.length, trackCount)) {
|
|
199
|
+
const grid = tryApplyGridMeasurements(data.value.grid, {
|
|
200
|
+
generation: data.value.grid.generation,
|
|
201
|
+
measurements: gridMeasurements,
|
|
202
|
+
});
|
|
203
|
+
if (!grid.ok)
|
|
204
|
+
return grid;
|
|
205
|
+
const rows = data.value.rows.updateDetailed(rowChanges);
|
|
206
|
+
const columns = data.value.columns.updateDetailed(columnChanges);
|
|
207
|
+
const measured = createMeasuredState(state, data.value, rows.vector, columns.vector, grid.value.state, generation.value);
|
|
208
|
+
recordRepairDiagnostics(measured, {
|
|
209
|
+
mode: 'incremental',
|
|
210
|
+
changed: gridMeasurements.length,
|
|
211
|
+
touchedBlocks: new Set([
|
|
212
|
+
...rowChanges.map(([index]) => `r:${Math.floor(index / 64)}`),
|
|
213
|
+
...columnChanges.map(([index]) => `c:${Math.floor(index / 64)}`),
|
|
214
|
+
]).size,
|
|
215
|
+
copiedNodes: rows.copiedNodes + columns.copiedNodes,
|
|
216
|
+
copiedEntries: rows.copiedEntries + columns.copiedEntries,
|
|
217
|
+
rebuiltItems: 0,
|
|
218
|
+
repairBound,
|
|
219
|
+
});
|
|
220
|
+
next = ok(measured);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
const rows = materializeChanges(data.value.rows, rowChanges);
|
|
224
|
+
const columns = materializeChanges(data.value.columns, columnChanges);
|
|
225
|
+
next = createState({
|
|
226
|
+
rows,
|
|
227
|
+
columns,
|
|
228
|
+
regions: state.regions,
|
|
229
|
+
rowGap: state.rowGap,
|
|
230
|
+
columnGap: state.columnGap,
|
|
231
|
+
maxTracks: state.maxTracks,
|
|
232
|
+
maxRegions: state.maxRegions,
|
|
233
|
+
generation: generation.value,
|
|
234
|
+
});
|
|
235
|
+
if (next.ok)
|
|
236
|
+
recordRepairDiagnostics(next.value, {
|
|
237
|
+
mode: 'rebuild',
|
|
238
|
+
changed: gridMeasurements.length,
|
|
239
|
+
touchedBlocks: new Set([
|
|
240
|
+
...rowChanges.map(([index]) => `r:${Math.floor(index / 64)}`),
|
|
241
|
+
...columnChanges.map(([index]) => `c:${Math.floor(index / 64)}`),
|
|
242
|
+
]).size,
|
|
243
|
+
copiedNodes: 0,
|
|
244
|
+
copiedEntries: 0,
|
|
245
|
+
rebuiltItems: trackCount + state.regions.length,
|
|
246
|
+
repairBound,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (!next.ok)
|
|
250
|
+
return transitionResult(next);
|
|
251
|
+
return ok(Object.freeze({ state: next.value, scrollDelta: anchorDelta(before, baseAnchorRect(next.value, batch.anchor)) }));
|
|
252
|
+
}
|
|
253
|
+
export function applyPartitionedTrackGridMutation(state, mutation, anchor = null) {
|
|
254
|
+
return unwrap(tryApplyPartitionedTrackGridMutation(state, mutation, anchor));
|
|
255
|
+
}
|
|
256
|
+
export function tryApplyPartitionedTrackGridMutation(state, mutation, anchor = null) {
|
|
257
|
+
const data = getInternals(state);
|
|
258
|
+
if (!data.ok)
|
|
259
|
+
return data;
|
|
260
|
+
let rows = data.value.rows.view.toArray();
|
|
261
|
+
let columns = data.value.columns.view.toArray();
|
|
262
|
+
let regions = state.regions;
|
|
263
|
+
if (mutation.type === 'replace-regions')
|
|
264
|
+
regions = freezeRegions(mutation.regions);
|
|
265
|
+
else if (mutation.type === 'replace-row-tracks') {
|
|
266
|
+
const normalized = normalizeTracks(mutation.tracks, state.maxTracks);
|
|
267
|
+
if (!normalized.ok)
|
|
268
|
+
return transitionResult(normalized);
|
|
269
|
+
rows = preserveExtents(normalized.value, data.value.rows.view);
|
|
270
|
+
}
|
|
271
|
+
else if (mutation.type === 'replace-column-tracks') {
|
|
272
|
+
const normalized = normalizeTracks(mutation.tracks, state.maxTracks);
|
|
273
|
+
if (!normalized.ok)
|
|
274
|
+
return transitionResult(normalized);
|
|
275
|
+
columns = preserveExtents(normalized.value, data.value.columns.view);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
return fail('transition-rejection', 'virtual-layout-mutation-invalid', 'Partitioned grid mutation type is unsupported.', { mutation });
|
|
279
|
+
}
|
|
280
|
+
const generation = nextGeneration(state.generation);
|
|
281
|
+
if (!generation.ok)
|
|
282
|
+
return generation;
|
|
283
|
+
const before = baseAnchorRect(state, anchor);
|
|
284
|
+
const next = createState({
|
|
285
|
+
rows,
|
|
286
|
+
columns,
|
|
287
|
+
regions,
|
|
288
|
+
rowGap: state.rowGap,
|
|
289
|
+
columnGap: state.columnGap,
|
|
290
|
+
maxTracks: state.maxTracks,
|
|
291
|
+
maxRegions: state.maxRegions,
|
|
292
|
+
generation: generation.value,
|
|
293
|
+
});
|
|
294
|
+
if (!next.ok)
|
|
295
|
+
return transitionResult(next);
|
|
296
|
+
return ok(Object.freeze({ state: next.value, scrollDelta: anchorDelta(before, baseAnchorRect(next.value, anchor)) }));
|
|
297
|
+
}
|
|
298
|
+
export function partitionedTrackGridScrollTarget(state, id, viewport, alignment = 'nearest') {
|
|
299
|
+
return unwrap(tryPartitionedTrackGridScrollTarget(state, id, viewport, alignment));
|
|
300
|
+
}
|
|
301
|
+
export function tryPartitionedTrackGridScrollTarget(state, id, viewport, alignment = 'nearest') {
|
|
302
|
+
const normalized = normalizeQuery({ viewport });
|
|
303
|
+
if (!normalized.ok)
|
|
304
|
+
return normalized;
|
|
305
|
+
const data = getInternals(state);
|
|
306
|
+
if (!data.ok)
|
|
307
|
+
return data;
|
|
308
|
+
const region = state.regions[data.value.regionIndex.get(id) ?? -1];
|
|
309
|
+
const rect = trackGridRegionRect(data.value.grid, id);
|
|
310
|
+
if (region === undefined || rect === null) {
|
|
311
|
+
return fail('transition-rejection', 'virtual-layout-scroll-target-invalid', 'Scroll target must exist in the partitioned grid region domain.', { id });
|
|
312
|
+
}
|
|
313
|
+
const rowIndex = data.value.rowIndex.get(region.row);
|
|
314
|
+
const columnIndex = data.value.columnIndex.get(region.column);
|
|
315
|
+
if (rowIndex === undefined || columnIndex === undefined)
|
|
316
|
+
return domainMismatch('Scroll target track index is stale.');
|
|
317
|
+
const rowPinned = data.value.rowPartitions[rowIndex] !== 'center';
|
|
318
|
+
const columnPinned = data.value.columnPartitions[columnIndex] !== 'center';
|
|
319
|
+
const contentWidth = data.value.grid.columns.totalExtent + gapExtent(state.columns.size, state.columnGap);
|
|
320
|
+
const contentHeight = data.value.grid.rows.totalExtent + gapExtent(state.rows.size, state.rowGap);
|
|
321
|
+
return ok(Object.freeze({
|
|
322
|
+
x: columnPinned ? viewport.x : alignedScrollOffset(rect.x, rect.width, viewport.x, viewport.width, contentWidth, alignment),
|
|
323
|
+
y: rowPinned ? viewport.y : alignedScrollOffset(rect.y, rect.height, viewport.y, viewport.height, contentHeight, alignment),
|
|
324
|
+
}));
|
|
325
|
+
}
|
|
326
|
+
function createState(input) {
|
|
327
|
+
const built = buildGrid(input.rows, input.columns, input.regions, input);
|
|
328
|
+
if (!built.ok)
|
|
329
|
+
return built;
|
|
330
|
+
const mutable = { ...input, rows: built.value.rows.view, columns: built.value.columns.view };
|
|
331
|
+
Object.defineProperty(mutable, partitionedTrackGridLayoutStateBrand, { value: true });
|
|
332
|
+
const state = Object.freeze(mutable);
|
|
333
|
+
internals.set(state, built.value);
|
|
334
|
+
return ok(state);
|
|
335
|
+
}
|
|
336
|
+
function createMeasuredState(previous, data, rows, columns, grid, generation) {
|
|
337
|
+
const mutable = { ...previous, rows: rows.view, columns: columns.view, generation };
|
|
338
|
+
Object.defineProperty(mutable, partitionedTrackGridLayoutStateBrand, { value: true });
|
|
339
|
+
const state = Object.freeze(mutable);
|
|
340
|
+
internals.set(state, {
|
|
341
|
+
...data,
|
|
342
|
+
grid,
|
|
343
|
+
rows,
|
|
344
|
+
columns,
|
|
345
|
+
rowRanges: partitionRangesFromTemplate(data.rowRanges, grid.rows, state.rowGap),
|
|
346
|
+
columnRanges: partitionRangesFromTemplate(data.columnRanges, grid.columns, state.columnGap),
|
|
347
|
+
});
|
|
348
|
+
return state;
|
|
349
|
+
}
|
|
350
|
+
function buildGrid(rows, columns, regions, input) {
|
|
351
|
+
const rowIndex = new Map(rows.map((track, index) => [track.id, index]));
|
|
352
|
+
const columnIndex = new Map(columns.map((track, index) => [track.id, index]));
|
|
353
|
+
const numericRegions = [];
|
|
354
|
+
for (const region of regions) {
|
|
355
|
+
const row = rowIndex.get(region.row);
|
|
356
|
+
const column = columnIndex.get(region.column);
|
|
357
|
+
const rowSpan = region.rowSpan ?? 1;
|
|
358
|
+
const columnSpan = region.columnSpan ?? 1;
|
|
359
|
+
if (row === undefined || column === undefined) {
|
|
360
|
+
return fail('construction', 'virtual-layout-region-invalid', 'Partitioned grid regions must reference existing row and column tracks.', { region });
|
|
361
|
+
}
|
|
362
|
+
if (!spanWithinPartition(rows, row, rowSpan) || !spanWithinPartition(columns, column, columnSpan)) {
|
|
363
|
+
return fail('construction', 'virtual-layout-region-invalid', 'Partitioned grid regions cannot span logical partition boundaries.', { region });
|
|
364
|
+
}
|
|
365
|
+
numericRegions.push(Object.freeze({
|
|
366
|
+
id: region.id,
|
|
367
|
+
row,
|
|
368
|
+
column,
|
|
369
|
+
...(rowSpan === 1 ? {} : { rowSpan }),
|
|
370
|
+
...(columnSpan === 1 ? {} : { columnSpan }),
|
|
371
|
+
}));
|
|
372
|
+
}
|
|
373
|
+
const rowExtents = tryCreateExtentIndex(rows.map(({ extent }) => extent), { maxItems: input.maxTracks });
|
|
374
|
+
if (!rowExtents.ok)
|
|
375
|
+
return rowExtents;
|
|
376
|
+
const columnExtents = tryCreateExtentIndex(columns.map(({ extent }) => extent), { maxItems: input.maxTracks });
|
|
377
|
+
if (!columnExtents.ok)
|
|
378
|
+
return columnExtents;
|
|
379
|
+
const grid = tryCreateTrackGridLayout(rowExtents.value, columnExtents.value, numericRegions, {
|
|
380
|
+
rowGap: input.rowGap,
|
|
381
|
+
columnGap: input.columnGap,
|
|
382
|
+
maxRegions: input.maxRegions,
|
|
383
|
+
});
|
|
384
|
+
if (!grid.ok)
|
|
385
|
+
return grid;
|
|
386
|
+
return ok(Object.freeze({
|
|
387
|
+
grid: grid.value,
|
|
388
|
+
rowIndex,
|
|
389
|
+
columnIndex,
|
|
390
|
+
regionIndex: new Map(regions.map((region, index) => [region.id, index])),
|
|
391
|
+
rowRanges: partitionRanges(rows, rowExtents.value, input.rowGap),
|
|
392
|
+
columnRanges: partitionRanges(columns, columnExtents.value, input.columnGap),
|
|
393
|
+
rows: createBlockedVector(rows),
|
|
394
|
+
columns: createBlockedVector(columns),
|
|
395
|
+
rowPartitions: Object.freeze(rows.map((track) => track.partition)),
|
|
396
|
+
columnPartitions: Object.freeze(columns.map((track) => track.partition)),
|
|
397
|
+
}));
|
|
398
|
+
}
|
|
399
|
+
function normalizeTracks(tracks, maxTracks) {
|
|
400
|
+
if (tracks.length > maxTracks)
|
|
401
|
+
return fail('resource-rejection', 'extent-index-ceiling-exceeded', 'Partitioned tracks exceed maxTracks.', { size: tracks.length, maxTracks });
|
|
402
|
+
const domain = tryCreateSequence(tracks.map(({ id }) => id), { maxItems: Math.max(1, maxTracks) });
|
|
403
|
+
if (!domain.ok)
|
|
404
|
+
return domain;
|
|
405
|
+
const grouped = [];
|
|
406
|
+
for (const partition of ['start', 'center', 'end']) {
|
|
407
|
+
for (const track of tracks) {
|
|
408
|
+
if (!validPartition(track.partition))
|
|
409
|
+
return fail('construction', 'virtual-layout-domain-mismatch', 'Track partition must be start, center, or end.', { track });
|
|
410
|
+
if (track.partition === partition)
|
|
411
|
+
grouped.push(Object.freeze({ id: track.id, partition, extent: Object.freeze({ ...track.extent }) }));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
const extents = tryCreateExtentIndex(grouped.map(({ extent }) => extent), { maxItems: maxTracks });
|
|
415
|
+
if (!extents.ok)
|
|
416
|
+
return extents;
|
|
417
|
+
return ok(Object.freeze(grouped));
|
|
418
|
+
}
|
|
419
|
+
function partitionRanges(tracks, extents, gap) {
|
|
420
|
+
return partitionRangesFromPartitions(tracks.map((track) => track.partition), extents, gap);
|
|
421
|
+
}
|
|
422
|
+
function partitionRangesFromPartitions(partitions, extents, gap) {
|
|
423
|
+
const starts = partitions.filter((partition) => partition === 'start').length;
|
|
424
|
+
const centers = partitions.filter((partition) => partition === 'center').length;
|
|
425
|
+
const start = makeRange(extents, gap, 0, starts);
|
|
426
|
+
const center = makeRange(extents, gap, starts, starts + centers);
|
|
427
|
+
const end = makeRange(extents, gap, starts + centers, partitions.length);
|
|
428
|
+
return Object.freeze({ start, center, end });
|
|
429
|
+
}
|
|
430
|
+
function partitionRangesFromTemplate(template, extents, gap) {
|
|
431
|
+
return Object.freeze({
|
|
432
|
+
start: makeRange(extents, gap, template.start.startIndex, template.start.endIndex),
|
|
433
|
+
center: makeRange(extents, gap, template.center.startIndex, template.center.endIndex),
|
|
434
|
+
end: makeRange(extents, gap, template.end.startIndex, template.end.endIndex),
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
function makeRange(extents, gap, startIndex, endIndex) {
|
|
438
|
+
const offset = extents.offsetAt(startIndex) ?? extents.totalExtent;
|
|
439
|
+
const endOffset = extents.offsetAt(endIndex) ?? extents.totalExtent;
|
|
440
|
+
return Object.freeze({
|
|
441
|
+
startIndex,
|
|
442
|
+
endIndex,
|
|
443
|
+
offset: offset + gap * startIndex,
|
|
444
|
+
extent: Math.max(0, endOffset - offset + gapExtent(endIndex - startIndex, gap)),
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function querySegments(ranges, renderStart, renderExtent) {
|
|
448
|
+
const result = [];
|
|
449
|
+
if (ranges.start.endIndex > ranges.start.startIndex)
|
|
450
|
+
result.push(ranges.start);
|
|
451
|
+
if (ranges.center.endIndex > ranges.center.startIndex) {
|
|
452
|
+
const start = Math.max(ranges.center.offset, renderStart);
|
|
453
|
+
const end = Math.min(ranges.center.offset + ranges.center.extent, renderStart + renderExtent);
|
|
454
|
+
if (end > start)
|
|
455
|
+
result.push(Object.freeze({ ...ranges.center, offset: start, extent: end - start }));
|
|
456
|
+
}
|
|
457
|
+
if (ranges.end.endIndex > ranges.end.startIndex)
|
|
458
|
+
result.push(ranges.end);
|
|
459
|
+
return Object.freeze(result);
|
|
460
|
+
}
|
|
461
|
+
function projectRect(rect, rowPartition, columnPartition, rowRanges, columnRanges, viewport) {
|
|
462
|
+
return Object.freeze({
|
|
463
|
+
x: projectAxis(rect.x, rect.width, columnPartition, columnRanges, viewport.x, viewport.width),
|
|
464
|
+
y: projectAxis(rect.y, rect.height, rowPartition, rowRanges, viewport.y, viewport.height),
|
|
465
|
+
width: rect.width,
|
|
466
|
+
height: rect.height,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
function projectAxis(start, extent, partition, ranges, viewportStart, viewportExtent) {
|
|
470
|
+
if (partition === 'start')
|
|
471
|
+
return viewportStart + start - ranges.start.offset;
|
|
472
|
+
if (partition === 'end')
|
|
473
|
+
return viewportStart + viewportExtent - ranges.end.extent + start - ranges.end.offset;
|
|
474
|
+
return start;
|
|
475
|
+
}
|
|
476
|
+
function preserveExtents(next, previous) {
|
|
477
|
+
const current = new Map();
|
|
478
|
+
previous.forEach((track) => current.set(track.id, track.extent));
|
|
479
|
+
return Object.freeze(next.map((track) => Object.freeze({ ...track, extent: current.get(track.id) ?? track.extent })));
|
|
480
|
+
}
|
|
481
|
+
function baseAnchorRect(state, anchor) {
|
|
482
|
+
if (anchor === null || anchor === undefined)
|
|
483
|
+
return null;
|
|
484
|
+
const data = internals.get(state);
|
|
485
|
+
return data === undefined ? null : trackGridRegionRect(data.grid, anchor.id);
|
|
486
|
+
}
|
|
487
|
+
function freezeTrackView(tracks) {
|
|
488
|
+
const output = [];
|
|
489
|
+
tracks.forEach((track) => output.push(Object.freeze({ id: track.id, partition: track.partition, extent: Object.freeze({ ...track.extent }) })));
|
|
490
|
+
return Object.freeze(output);
|
|
491
|
+
}
|
|
492
|
+
function materializeChanges(vector, changes) {
|
|
493
|
+
const output = new Array(vector.size);
|
|
494
|
+
let cursor = 0;
|
|
495
|
+
vector.forEach((value, index) => {
|
|
496
|
+
if (cursor < changes.length && changes[cursor][0] === index)
|
|
497
|
+
output[index] = changes[cursor++][1];
|
|
498
|
+
else
|
|
499
|
+
output[index] = value;
|
|
500
|
+
});
|
|
501
|
+
return Object.freeze(output);
|
|
502
|
+
}
|
|
503
|
+
function freezeRegions(regions) {
|
|
504
|
+
return Object.freeze(regions.map((region) => Object.freeze({
|
|
505
|
+
id: region.id,
|
|
506
|
+
row: region.row,
|
|
507
|
+
column: region.column,
|
|
508
|
+
...(region.rowSpan === undefined || region.rowSpan === 1 ? {} : { rowSpan: region.rowSpan }),
|
|
509
|
+
...(region.columnSpan === undefined || region.columnSpan === 1 ? {} : { columnSpan: region.columnSpan }),
|
|
510
|
+
})));
|
|
511
|
+
}
|
|
512
|
+
function spanWithinPartition(tracks, start, span) {
|
|
513
|
+
if (!Number.isSafeInteger(span) || span <= 0 || start + span > tracks.length)
|
|
514
|
+
return false;
|
|
515
|
+
const partition = tracks[start]?.partition;
|
|
516
|
+
for (let index = start + 1; index < start + span; index += 1)
|
|
517
|
+
if (tracks[index]?.partition !== partition)
|
|
518
|
+
return false;
|
|
519
|
+
return partition !== undefined;
|
|
520
|
+
}
|
|
521
|
+
function getInternals(state) {
|
|
522
|
+
const value = internals.get(state);
|
|
523
|
+
return value === undefined
|
|
524
|
+
? domainMismatch('Partitioned grid layout state must be created by createPartitionedTrackGridLayout().')
|
|
525
|
+
: ok(value);
|
|
526
|
+
}
|
|
527
|
+
function requireInternals(state) {
|
|
528
|
+
if (!internals.has(state))
|
|
529
|
+
throw new TypeError('Partitioned grid layout state must be created by createPartitionedTrackGridLayout().');
|
|
530
|
+
}
|
|
531
|
+
function validSnapshotHeader(snapshot) {
|
|
532
|
+
return snapshot !== null
|
|
533
|
+
&& typeof snapshot === 'object'
|
|
534
|
+
&& snapshot.schemaVersion === 1
|
|
535
|
+
&& snapshot.kind === 'partitioned-track-grid'
|
|
536
|
+
&& Array.isArray(snapshot.rows)
|
|
537
|
+
&& Array.isArray(snapshot.columns)
|
|
538
|
+
&& Array.isArray(snapshot.regions)
|
|
539
|
+
&& nonNegativeSafe(snapshot.maxTracks)
|
|
540
|
+
&& snapshot.maxTracks >= snapshot.rows.length
|
|
541
|
+
&& snapshot.maxTracks >= snapshot.columns.length
|
|
542
|
+
&& nonNegativeSafe(snapshot.maxRegions)
|
|
543
|
+
&& snapshot.maxRegions >= snapshot.regions.length
|
|
544
|
+
&& nonNegativeSafe(snapshot.generation);
|
|
545
|
+
}
|
|
546
|
+
function transitionResult(result) {
|
|
547
|
+
return result.ok || result.error.class !== 'construction'
|
|
548
|
+
? result
|
|
549
|
+
: { ok: false, error: { ...result.error, class: 'transition-rejection' } };
|
|
550
|
+
}
|
|
551
|
+
function measurementFailure(message, measurement) {
|
|
552
|
+
return fail('transition-rejection', 'virtual-layout-measurement-invalid', message, { measurement });
|
|
553
|
+
}
|
|
554
|
+
function domainMismatch(message) {
|
|
555
|
+
return fail('construction', 'virtual-layout-domain-mismatch', message);
|
|
556
|
+
}
|
|
557
|
+
function snapshotFailure() {
|
|
558
|
+
return fail('construction', 'virtual-layout-snapshot-invalid', 'Partitioned track-grid layout snapshot is invalid.');
|
|
559
|
+
}
|
|
560
|
+
function nextGeneration(generation) {
|
|
561
|
+
return generation === Number.MAX_SAFE_INTEGER
|
|
562
|
+
? fail('resource-rejection', 'virtual-layout-generation-exhausted', 'Layout generation reached the safe-integer ceiling.')
|
|
563
|
+
: ok(generation + 1);
|
|
564
|
+
}
|
|
565
|
+
function anchorDelta(before, after) {
|
|
566
|
+
return before === null || after === null ? ZERO_POINT : pointDelta(before, after);
|
|
567
|
+
}
|
|
568
|
+
function gapExtent(size, gap) {
|
|
569
|
+
return Math.max(0, size - 1) * gap;
|
|
570
|
+
}
|
|
571
|
+
function validPartition(value) {
|
|
572
|
+
return value === 'start' || value === 'center' || value === 'end';
|
|
573
|
+
}
|
|
574
|
+
function finiteNonNegative(value) {
|
|
575
|
+
return Number.isFinite(value) && value >= 0;
|
|
576
|
+
}
|
|
577
|
+
function nonNegativeSafe(value) {
|
|
578
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
579
|
+
}
|
|
580
|
+
function sameExtent(left, right) {
|
|
581
|
+
return right !== null && typeof right === 'object' && left.kind === right.kind && (left.kind === 'unknown'
|
|
582
|
+
? left.fallback === (right.kind === 'unknown' ? right.fallback : Number.NaN)
|
|
583
|
+
: left.value === (right.kind === 'unknown' ? Number.NaN : right.value));
|
|
584
|
+
}
|
|
585
|
+
//# sourceMappingURL=partitioned-track-grid-layout.js.map
|