@foldkit/ui 0.166.0-canary.fe2701c2fa4b → 0.167.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/dragAndDrop/index.d.ts.map +1 -1
- package/dist/dragAndDrop/index.js +14 -28
- package/dist/internal/documentDragStyles.d.ts +3 -0
- package/dist/internal/documentDragStyles.d.ts.map +1 -0
- package/dist/internal/documentDragStyles.js +16 -0
- package/dist/listbox/multi.d.ts +7 -7
- package/dist/listbox/shared.d.ts +11 -11
- package/dist/listbox/single.d.ts +14 -14
- package/dist/menu/index.d.ts +10 -10
- package/dist/slider/index.d.ts.map +1 -1
- package/dist/slider/index.js +19 -32
- package/dist/test/apps/disabledButton.d.ts +2 -2
- package/dist/toast/update.d.ts.map +1 -1
- package/dist/toast/update.js +5 -4
- package/dist/virtualList/index.d.ts +374 -130
- package/dist/virtualList/index.d.ts.map +1 -1
- package/dist/virtualList/index.js +679 -289
- package/dist/virtualList/public.d.ts +2 -2
- package/dist/virtualList/public.d.ts.map +1 -1
- package/dist/virtualList/public.js +1 -1
- package/package.json +5 -5
|
@@ -1,28 +1,87 @@
|
|
|
1
1
|
import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
|
|
2
|
-
import { Array, Effect, Number, Option, Queue, Schema, Stream, pipe, } from 'effect';
|
|
2
|
+
import { Array, Effect, HashSet, Match, Number, Option, Queue, Record, Schema, Stream, pipe, } from 'effect';
|
|
3
|
+
import { Update } from 'foldkit';
|
|
3
4
|
import * as Command from 'foldkit/command';
|
|
4
5
|
import { childAttributes, } from 'foldkit/html';
|
|
5
6
|
import { defineMessageUnion } from 'foldkit/message';
|
|
7
|
+
import * as Mount from 'foldkit/mount';
|
|
8
|
+
import * as Render from 'foldkit/render';
|
|
6
9
|
import { defineTaggedUnion } from 'foldkit/schema';
|
|
7
10
|
import { modifyFields } from 'foldkit/struct';
|
|
8
11
|
import { defineView } from 'foldkit/submodel';
|
|
9
|
-
import * as Subscription from 'foldkit/subscription';
|
|
10
12
|
// MODEL
|
|
11
13
|
/** Measurement state of the virtual list's scrollable container.
|
|
12
14
|
*
|
|
13
|
-
* Before the container's
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* first measurement arrives.
|
|
15
|
+
* Before the Mount reports the container's dimensions we cannot compute a
|
|
16
|
+
* visible slice. The view handles `Unmeasured` by rendering an empty
|
|
17
|
+
* container until the first measurement arrives.
|
|
17
18
|
*/
|
|
18
19
|
const Measurement = defineTaggedUnion({
|
|
19
20
|
Unmeasured: {},
|
|
20
|
-
Measured: {
|
|
21
|
+
Measured: {
|
|
22
|
+
containerWidth: Schema.Number,
|
|
23
|
+
containerHeight: Schema.Number,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
/** Alignment of a row within the viewport after a programmatic scroll. */
|
|
27
|
+
export const ScrollAlignment = Schema.Literals([
|
|
28
|
+
'Start',
|
|
29
|
+
'Center',
|
|
30
|
+
'End',
|
|
31
|
+
'Nearest',
|
|
32
|
+
]);
|
|
33
|
+
/** Logical destination for initial and programmatic scrolling. */
|
|
34
|
+
export const ScrollTarget = defineTaggedUnion({
|
|
35
|
+
Index: { index: Schema.Number },
|
|
36
|
+
Key: { key: Schema.String },
|
|
37
|
+
Offset: { offset: Schema.Number },
|
|
38
|
+
End: {},
|
|
39
|
+
});
|
|
40
|
+
const ViewportAnchor = defineTaggedUnion({
|
|
41
|
+
Offset: { scrollTop: Schema.Number },
|
|
42
|
+
Row: {
|
|
43
|
+
key: Schema.String,
|
|
44
|
+
index: Schema.Number,
|
|
45
|
+
viewportOffset: Schema.Number,
|
|
46
|
+
},
|
|
47
|
+
End: {},
|
|
48
|
+
});
|
|
49
|
+
const ObservedAnchor = defineTaggedUnion({
|
|
50
|
+
None: {},
|
|
51
|
+
Row: {
|
|
52
|
+
key: Schema.String,
|
|
53
|
+
index: Schema.Number,
|
|
54
|
+
viewportOffset: Schema.Number,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
/** Outcome of applying a logical scroll request after the view commits. */
|
|
58
|
+
export const ApplyScrollOutcome = defineTaggedUnion({
|
|
59
|
+
Applied: {
|
|
60
|
+
scrollTop: Schema.Number,
|
|
61
|
+
scrollHeight: Schema.Number,
|
|
62
|
+
containerHeight: Schema.Number,
|
|
63
|
+
anchor: ObservedAnchor,
|
|
64
|
+
},
|
|
65
|
+
Skipped: {},
|
|
66
|
+
});
|
|
67
|
+
const ScrollRequest = defineTaggedUnion({
|
|
68
|
+
Target: {
|
|
69
|
+
target: ScrollTarget,
|
|
70
|
+
alignment: ScrollAlignment,
|
|
71
|
+
},
|
|
72
|
+
Anchor: { anchor: ViewportAnchor },
|
|
21
73
|
});
|
|
22
|
-
/** State of a programmatic scroll initiated by `scrollToIndex`. */
|
|
23
74
|
const PendingScroll = defineTaggedUnion({
|
|
24
75
|
Idle: {},
|
|
25
|
-
|
|
76
|
+
Pending: { request: ScrollRequest, version: Schema.Number },
|
|
77
|
+
});
|
|
78
|
+
const InitialScroll = defineTaggedUnion({
|
|
79
|
+
Applied: {},
|
|
80
|
+
Pending: { target: ScrollTarget, alignment: ScrollAlignment },
|
|
81
|
+
});
|
|
82
|
+
const EndBehavior = defineTaggedUnion({
|
|
83
|
+
PreserveAnchor: {},
|
|
84
|
+
Follow: { thresholdPx: Schema.Number },
|
|
26
85
|
});
|
|
27
86
|
/** Schema for the virtual list's state. Tracks scroll position, container
|
|
28
87
|
* measurement, and any in-flight programmatic scroll. */
|
|
@@ -31,316 +90,636 @@ export const Model = Schema.Struct({
|
|
|
31
90
|
rowHeightPx: Schema.Number,
|
|
32
91
|
scrollTop: Schema.Number,
|
|
33
92
|
measurement: Measurement,
|
|
93
|
+
viewportAnchor: ViewportAnchor,
|
|
94
|
+
measuredRowHeights: Schema.Record(Schema.String, Schema.Number),
|
|
95
|
+
layoutVersion: Schema.Number,
|
|
96
|
+
initialScroll: InitialScroll,
|
|
97
|
+
endBehavior: EndBehavior,
|
|
34
98
|
pendingScroll: PendingScroll,
|
|
35
99
|
pendingScrollVersion: Schema.Number,
|
|
36
100
|
});
|
|
37
101
|
// MESSAGE
|
|
38
102
|
/** Union of all messages the virtual list component can produce. */
|
|
39
103
|
export const Message = defineMessageUnion({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
104
|
+
ObservedContainerScroll: {
|
|
105
|
+
scrollTop: Schema.Number,
|
|
106
|
+
scrollHeight: Schema.Number,
|
|
107
|
+
containerHeight: Schema.Number,
|
|
108
|
+
anchor: ObservedAnchor,
|
|
109
|
+
},
|
|
110
|
+
ResizedContainer: {
|
|
111
|
+
containerWidth: Schema.Number,
|
|
112
|
+
containerHeight: Schema.Number,
|
|
113
|
+
},
|
|
114
|
+
MeasuredRows: {
|
|
115
|
+
measurements: Schema.Array(Schema.Struct({
|
|
116
|
+
key: Schema.String,
|
|
117
|
+
height: Schema.Number,
|
|
118
|
+
layoutVersion: Schema.Number,
|
|
119
|
+
})),
|
|
120
|
+
},
|
|
121
|
+
CompletedApplyScroll: {
|
|
122
|
+
version: Schema.Number,
|
|
123
|
+
outcome: ApplyScrollOutcome,
|
|
124
|
+
},
|
|
43
125
|
});
|
|
126
|
+
const initialScrollFromConfig = (config) => {
|
|
127
|
+
if (config.initialScroll !== undefined) {
|
|
128
|
+
return __foldkitBrandViewResult((InitialScroll.Pending({
|
|
129
|
+
target: config.initialScroll.target,
|
|
130
|
+
alignment: config.initialScroll.alignment ?? 'Start',
|
|
131
|
+
})), "@foldkit/ui/virtualList/index.js#initialScrollFromConfig");
|
|
132
|
+
}
|
|
133
|
+
if (config.initialScrollTop !== undefined) {
|
|
134
|
+
return __foldkitBrandViewResult((InitialScroll.Pending({
|
|
135
|
+
target: ScrollTarget.Offset({ offset: config.initialScrollTop }),
|
|
136
|
+
alignment: 'Start',
|
|
137
|
+
})), "@foldkit/ui/virtualList/index.js#initialScrollFromConfig");
|
|
138
|
+
}
|
|
139
|
+
return __foldkitBrandViewResult((InitialScroll.Applied()), "@foldkit/ui/virtualList/index.js#initialScrollFromConfig");
|
|
140
|
+
};
|
|
44
141
|
/** Creates an initial virtual list model from a config. The container starts
|
|
45
|
-
* in `Unmeasured` state
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
142
|
+
* in `Unmeasured` state until its Mount reports the first measurement. */
|
|
143
|
+
export const init = (config) => {
|
|
144
|
+
const initialScroll = initialScrollFromConfig(config);
|
|
145
|
+
return __foldkitBrandViewResult(({
|
|
146
|
+
id: config.id,
|
|
147
|
+
rowHeightPx: config.rowHeightPx,
|
|
148
|
+
scrollTop: config.initialScrollTop ?? 0,
|
|
149
|
+
measurement: Measurement.Unmeasured(),
|
|
150
|
+
viewportAnchor: ViewportAnchor.Offset({
|
|
151
|
+
scrollTop: config.initialScrollTop ?? 0,
|
|
152
|
+
}),
|
|
153
|
+
measuredRowHeights: Record.empty(),
|
|
154
|
+
layoutVersion: 0,
|
|
155
|
+
initialScroll,
|
|
156
|
+
endBehavior: config.followEnd === undefined
|
|
157
|
+
? EndBehavior.PreserveAnchor()
|
|
158
|
+
: EndBehavior.Follow({
|
|
159
|
+
thresholdPx: config.followEnd.thresholdPx ?? 1,
|
|
160
|
+
}),
|
|
161
|
+
pendingScroll: PendingScroll.Idle(),
|
|
162
|
+
pendingScrollVersion: 0,
|
|
163
|
+
}), "@foldkit/ui/virtualList/index.js#init");
|
|
164
|
+
};
|
|
55
165
|
// UPDATE
|
|
166
|
+
const scrollTopForNearestAlignment = (currentScrollTop, containerHeight, startOffset, endOffset) => {
|
|
167
|
+
const viewportEnd = currentScrollTop + containerHeight;
|
|
168
|
+
const rowSpansViewport = startOffset < currentScrollTop && endOffset > viewportEnd;
|
|
169
|
+
if (rowSpansViewport) {
|
|
170
|
+
return __foldkitBrandViewResult((currentScrollTop), "@foldkit/ui/virtualList/index.js#scrollTopForNearestAlignment");
|
|
171
|
+
}
|
|
172
|
+
if (startOffset < currentScrollTop) {
|
|
173
|
+
return __foldkitBrandViewResult((startOffset), "@foldkit/ui/virtualList/index.js#scrollTopForNearestAlignment");
|
|
174
|
+
}
|
|
175
|
+
if (endOffset > viewportEnd) {
|
|
176
|
+
return __foldkitBrandViewResult((endOffset - containerHeight), "@foldkit/ui/virtualList/index.js#scrollTopForNearestAlignment");
|
|
177
|
+
}
|
|
178
|
+
return __foldkitBrandViewResult((currentScrollTop), "@foldkit/ui/virtualList/index.js#scrollTopForNearestAlignment");
|
|
179
|
+
};
|
|
180
|
+
const scrollTopForRow = (currentScrollTop, containerHeight, startOffset, endOffset, alignment) => {
|
|
181
|
+
const targetScrollTop = Match.value(alignment).pipe(Match.withReturnType(), Match.when('Start', () => __foldkitBrandViewResult((startOffset), "@foldkit/ui/virtualList/index.js#anonymous")), Match.when('Center', () => __foldkitBrandViewResult((startOffset + (endOffset - startOffset - containerHeight) / 2), "@foldkit/ui/virtualList/index.js#anonymous~2")), Match.when('End', () => __foldkitBrandViewResult((endOffset - containerHeight), "@foldkit/ui/virtualList/index.js#anonymous~3")), Match.when('Nearest', () => __foldkitBrandViewResult((scrollTopForNearestAlignment(currentScrollTop, containerHeight, startOffset, endOffset)), "@foldkit/ui/virtualList/index.js#anonymous~4")), Match.exhaustive);
|
|
182
|
+
return __foldkitBrandViewResult((Math.max(0, targetScrollTop)), "@foldkit/ui/virtualList/index.js#scrollTopForRow");
|
|
183
|
+
};
|
|
184
|
+
const renderedRows = (element) => __foldkitBrandViewResult((pipe(element.children, Array.fromIterable, Array.filter((row) => __foldkitBrandViewResult((row instanceof HTMLElement &&
|
|
185
|
+
row.hasAttribute('data-virtual-list-item-key')), "@foldkit/ui/virtualList/index.js#anonymous~5")))), "@foldkit/ui/virtualList/index.js#renderedRows");
|
|
186
|
+
const rowIndex = (element) => __foldkitBrandViewResult((pipe(element.getAttribute('data-virtual-list-item-index'), Option.fromNullishOr, Option.flatMap(Number.parse))), "@foldkit/ui/virtualList/index.js#rowIndex");
|
|
187
|
+
const observedAnchor = (element) => {
|
|
188
|
+
const containerRect = element.getBoundingClientRect();
|
|
189
|
+
const viewportTop = containerRect.top + element.clientTop;
|
|
190
|
+
const viewportBottom = viewportTop + element.clientHeight;
|
|
191
|
+
const maybeRow = Array.findFirst(renderedRows(element), row => {
|
|
192
|
+
const rowRect = row.getBoundingClientRect();
|
|
193
|
+
return __foldkitBrandViewResult((rowRect.bottom > viewportTop && rowRect.top < viewportBottom), "@foldkit/ui/virtualList/index.js#anonymous~6");
|
|
194
|
+
});
|
|
195
|
+
if (Option.isNone(maybeRow)) {
|
|
196
|
+
return __foldkitBrandViewResult((ObservedAnchor.None()), "@foldkit/ui/virtualList/index.js#observedAnchor");
|
|
197
|
+
}
|
|
198
|
+
const key = maybeRow.value.getAttribute('data-virtual-list-item-key');
|
|
199
|
+
const maybeIndex = rowIndex(maybeRow.value);
|
|
200
|
+
if (key === null || Option.isNone(maybeIndex)) {
|
|
201
|
+
return __foldkitBrandViewResult((ObservedAnchor.None()), "@foldkit/ui/virtualList/index.js#observedAnchor");
|
|
202
|
+
}
|
|
203
|
+
return __foldkitBrandViewResult((ObservedAnchor.Row({
|
|
204
|
+
key,
|
|
205
|
+
index: maybeIndex.value,
|
|
206
|
+
viewportOffset: maybeRow.value.getBoundingClientRect().top - viewportTop,
|
|
207
|
+
})), "@foldkit/ui/virtualList/index.js#observedAnchor");
|
|
208
|
+
};
|
|
209
|
+
const maxScrollTop = (element) => __foldkitBrandViewResult((Math.max(0, element.scrollHeight - element.clientHeight)), "@foldkit/ui/virtualList/index.js#maxScrollTop");
|
|
210
|
+
const clampScrollTop = (element, scrollTop) => __foldkitBrandViewResult((Math.max(0, Math.min(scrollTop, maxScrollTop(element)))), "@foldkit/ui/virtualList/index.js#clampScrollTop");
|
|
211
|
+
const rowForIndex = (element, index) => {
|
|
212
|
+
const rows = renderedRows(element);
|
|
213
|
+
const maybeExact = Array.findFirst(rows, row => __foldkitBrandViewResult((Option.exists(rowIndex(row), rowIndex => __foldkitBrandViewResult((rowIndex === index), "@foldkit/ui/virtualList/index.js#anonymous~8"))), "@foldkit/ui/virtualList/index.js#anonymous~7"));
|
|
214
|
+
if (Option.isSome(maybeExact)) {
|
|
215
|
+
return __foldkitBrandViewResult((maybeExact), "@foldkit/ui/virtualList/index.js#rowForIndex");
|
|
216
|
+
}
|
|
217
|
+
const maybeFirst = Array.head(rows);
|
|
218
|
+
const maybeLast = Array.last(rows);
|
|
219
|
+
if (Option.isSome(maybeFirst)) {
|
|
220
|
+
const maybeFirstIndex = rowIndex(maybeFirst.value);
|
|
221
|
+
if (Option.isSome(maybeFirstIndex) && index < maybeFirstIndex.value) {
|
|
222
|
+
return __foldkitBrandViewResult((maybeFirst), "@foldkit/ui/virtualList/index.js#rowForIndex");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (Option.isSome(maybeLast)) {
|
|
226
|
+
const maybeLastIndex = rowIndex(maybeLast.value);
|
|
227
|
+
if (Option.isSome(maybeLastIndex) && index > maybeLastIndex.value) {
|
|
228
|
+
return __foldkitBrandViewResult((maybeLast), "@foldkit/ui/virtualList/index.js#rowForIndex");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return __foldkitBrandViewResult((Option.none()), "@foldkit/ui/virtualList/index.js#rowForIndex");
|
|
232
|
+
};
|
|
233
|
+
const rowForKey = (element, key) => __foldkitBrandViewResult((Array.findFirst(renderedRows(element), row => __foldkitBrandViewResult((row.getAttribute('data-virtual-list-item-key') === key), "@foldkit/ui/virtualList/index.js#anonymous~9"))), "@foldkit/ui/virtualList/index.js#rowForKey");
|
|
234
|
+
const rowOffsets = (container, row) => {
|
|
235
|
+
const viewportTop = container.getBoundingClientRect().top + container.clientTop;
|
|
236
|
+
const rowRect = row.getBoundingClientRect();
|
|
237
|
+
const startOffset = container.scrollTop + rowRect.top - viewportTop;
|
|
238
|
+
return __foldkitBrandViewResult(({ startOffset, endOffset: startOffset + rowRect.height }), "@foldkit/ui/virtualList/index.js#rowOffsets");
|
|
239
|
+
};
|
|
240
|
+
const rowForTarget = (element, target) => __foldkitBrandViewResult((ScrollTarget.matchOrElse(target, {
|
|
241
|
+
Index: ({ index }) => __foldkitBrandViewResult((rowForIndex(element, index)), "@foldkit/ui/virtualList/index.js#Index"),
|
|
242
|
+
Key: ({ key }) => __foldkitBrandViewResult((rowForKey(element, key)), "@foldkit/ui/virtualList/index.js#Key"),
|
|
243
|
+
}, () => __foldkitBrandViewResult((Option.none()), "@foldkit/ui/virtualList/index.js#anonymous~10"))), "@foldkit/ui/virtualList/index.js#rowForTarget");
|
|
244
|
+
const scrollTopForRowTarget = (element, target, alignment) => __foldkitBrandViewResult((Option.map(rowForTarget(element, target), row => {
|
|
245
|
+
const offsets = rowOffsets(element, row);
|
|
246
|
+
return __foldkitBrandViewResult((scrollTopForRow(element.scrollTop, element.clientHeight, offsets.startOffset, offsets.endOffset, alignment)), "@foldkit/ui/virtualList/index.js#anonymous~11");
|
|
247
|
+
})), "@foldkit/ui/virtualList/index.js#scrollTopForRowTarget");
|
|
248
|
+
const scrollTopForTarget = (element, target, alignment) => __foldkitBrandViewResult((ScrollTarget.match(target, {
|
|
249
|
+
Index: indexTarget => __foldkitBrandViewResult((scrollTopForRowTarget(element, indexTarget, alignment)), "@foldkit/ui/virtualList/index.js#Index~2"),
|
|
250
|
+
Key: keyTarget => __foldkitBrandViewResult((scrollTopForRowTarget(element, keyTarget, alignment)), "@foldkit/ui/virtualList/index.js#Key~2"),
|
|
251
|
+
Offset: ({ offset }) => __foldkitBrandViewResult((Option.some(offset)), "@foldkit/ui/virtualList/index.js#Offset"),
|
|
252
|
+
End: () => __foldkitBrandViewResult((Option.some(maxScrollTop(element))), "@foldkit/ui/virtualList/index.js#End"),
|
|
253
|
+
})), "@foldkit/ui/virtualList/index.js#scrollTopForTarget");
|
|
254
|
+
const rowForAnchor = (element, anchor) => __foldkitBrandViewResult((Option.orElse(rowForKey(element, anchor.key), () => __foldkitBrandViewResult((rowForIndex(element, anchor.index)), "@foldkit/ui/virtualList/index.js#anonymous~12"))), "@foldkit/ui/virtualList/index.js#rowForAnchor");
|
|
255
|
+
const scrollTopForAnchor = (element, anchor) => __foldkitBrandViewResult((ViewportAnchor.match(anchor, {
|
|
256
|
+
Offset: ({ scrollTop }) => __foldkitBrandViewResult((Option.some(scrollTop)), "@foldkit/ui/virtualList/index.js#Offset~2"),
|
|
257
|
+
Row: rowAnchor => __foldkitBrandViewResult((Option.map(rowForAnchor(element, rowAnchor), row => __foldkitBrandViewResult((Math.max(0, rowOffsets(element, row).startOffset - rowAnchor.viewportOffset)), "@foldkit/ui/virtualList/index.js#anonymous~13"))), "@foldkit/ui/virtualList/index.js#Row"),
|
|
258
|
+
End: () => __foldkitBrandViewResult((Option.some(maxScrollTop(element))), "@foldkit/ui/virtualList/index.js#End~2"),
|
|
259
|
+
})), "@foldkit/ui/virtualList/index.js#scrollTopForAnchor");
|
|
260
|
+
const scrollTopForRequest = (element, request) => __foldkitBrandViewResult((ScrollRequest.match(request, {
|
|
261
|
+
Target: ({ target, alignment }) => __foldkitBrandViewResult((scrollTopForTarget(element, target, alignment)), "@foldkit/ui/virtualList/index.js#Target"),
|
|
262
|
+
Anchor: ({ anchor }) => __foldkitBrandViewResult((scrollTopForAnchor(element, anchor)), "@foldkit/ui/virtualList/index.js#Anchor"),
|
|
263
|
+
})), "@foldkit/ui/virtualList/index.js#scrollTopForRequest");
|
|
264
|
+
const mountedContainers = new Map();
|
|
56
265
|
export const ApplyScroll = Command.define('ApplyScroll', {
|
|
57
|
-
args: {
|
|
266
|
+
args: {
|
|
267
|
+
id: Schema.String,
|
|
268
|
+
request: ScrollRequest,
|
|
269
|
+
version: Schema.Number,
|
|
270
|
+
},
|
|
58
271
|
messages: [Message.CompletedApplyScroll],
|
|
59
|
-
execute: ({ id,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
272
|
+
execute: ({ id, request, version }) => __foldkitBrandViewResult((Effect.gen(function* () {
|
|
273
|
+
yield* Render.afterCommit;
|
|
274
|
+
const containers = mountedContainers.get(id);
|
|
275
|
+
if (containers === undefined || containers.size !== 1) {
|
|
276
|
+
return __foldkitBrandViewResult((Message.CompletedApplyScroll({
|
|
277
|
+
version,
|
|
278
|
+
outcome: ApplyScrollOutcome.Skipped(),
|
|
279
|
+
})), "@foldkit/ui/virtualList/index.js#anonymous~14");
|
|
63
280
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
model: modifyFields(model, { scrollTop: () => __foldkitBrandViewResult((scrollTop), "@foldkit/ui/virtualList/index.js#scrollTop") }),
|
|
71
|
-
}), "@foldkit/ui/virtualList/index.js#ScrolledContainer")),
|
|
72
|
-
MeasuredContainer: ({ containerHeight }) => {
|
|
73
|
-
const wasUnmeasured = model.measurement._tag === 'Unmeasured';
|
|
74
|
-
const needsInitialApply = wasUnmeasured && model.scrollTop !== 0;
|
|
75
|
-
if (needsInitialApply) {
|
|
76
|
-
const nextVersion = Number.increment(model.pendingScrollVersion);
|
|
77
|
-
return __foldkitBrandViewResult(({
|
|
78
|
-
model: modifyFields(model, {
|
|
79
|
-
measurement: () => __foldkitBrandViewResult((Measurement.Measured({ containerHeight })), "@foldkit/ui/virtualList/index.js#measurement"),
|
|
80
|
-
pendingScrollVersion: () => __foldkitBrandViewResult((nextVersion), "@foldkit/ui/virtualList/index.js#pendingScrollVersion"),
|
|
81
|
-
pendingScroll: () => __foldkitBrandViewResult((PendingScroll.ScrollingToIndex({
|
|
82
|
-
index: Math.floor(model.scrollTop / model.rowHeightPx),
|
|
83
|
-
version: nextVersion,
|
|
84
|
-
})), "@foldkit/ui/virtualList/index.js#pendingScroll"),
|
|
85
|
-
}),
|
|
86
|
-
commands: [
|
|
87
|
-
ApplyScroll({
|
|
88
|
-
id: model.id,
|
|
89
|
-
scrollTop: model.scrollTop,
|
|
90
|
-
version: nextVersion,
|
|
91
|
-
}),
|
|
92
|
-
],
|
|
93
|
-
}), "@foldkit/ui/virtualList/index.js#MeasuredContainer");
|
|
281
|
+
const maybeElement = pipe(containers, Array.fromIterable, Array.head);
|
|
282
|
+
if (Option.isNone(maybeElement)) {
|
|
283
|
+
return __foldkitBrandViewResult((Message.CompletedApplyScroll({
|
|
284
|
+
version,
|
|
285
|
+
outcome: ApplyScrollOutcome.Skipped(),
|
|
286
|
+
})), "@foldkit/ui/virtualList/index.js#anonymous~14");
|
|
94
287
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
288
|
+
const element = maybeElement.value;
|
|
289
|
+
const maybeActiveVersion = pipe(element.getAttribute('data-virtual-list-scroll-version'), Option.fromNullishOr, Option.flatMap(Number.parse));
|
|
290
|
+
if (Option.isNone(maybeActiveVersion) ||
|
|
291
|
+
maybeActiveVersion.value !== version) {
|
|
292
|
+
return __foldkitBrandViewResult((Message.CompletedApplyScroll({
|
|
293
|
+
version,
|
|
294
|
+
outcome: ApplyScrollOutcome.Skipped(),
|
|
295
|
+
})), "@foldkit/ui/virtualList/index.js#anonymous~14");
|
|
101
296
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return __foldkitBrandViewResult(({
|
|
109
|
-
model: modifyFields(model, {
|
|
110
|
-
pendingScroll: () => __foldkitBrandViewResult((PendingScroll.Idle()), "@foldkit/ui/virtualList/index.js#pendingScroll~2"),
|
|
111
|
-
}),
|
|
112
|
-
}), "@foldkit/ui/virtualList/index.js#CompletedApplyScroll");
|
|
297
|
+
const maybeScrollTop = scrollTopForRequest(element, request);
|
|
298
|
+
if (Option.isNone(maybeScrollTop)) {
|
|
299
|
+
return __foldkitBrandViewResult((Message.CompletedApplyScroll({
|
|
300
|
+
version,
|
|
301
|
+
outcome: ApplyScrollOutcome.Skipped(),
|
|
302
|
+
})), "@foldkit/ui/virtualList/index.js#anonymous~14");
|
|
113
303
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
304
|
+
element.scrollTop = clampScrollTop(element, maybeScrollTop.value);
|
|
305
|
+
return __foldkitBrandViewResult((Message.CompletedApplyScroll({
|
|
306
|
+
version,
|
|
307
|
+
outcome: ApplyScrollOutcome.Applied({
|
|
308
|
+
scrollTop: element.scrollTop,
|
|
309
|
+
scrollHeight: element.scrollHeight,
|
|
310
|
+
containerHeight: element.clientHeight,
|
|
311
|
+
anchor: observedAnchor(element),
|
|
312
|
+
}),
|
|
313
|
+
})), "@foldkit/ui/virtualList/index.js#anonymous~14");
|
|
314
|
+
})), "@foldkit/ui/virtualList/index.js#execute"),
|
|
315
|
+
});
|
|
316
|
+
const buildScrollRequest = (model, request) => {
|
|
117
317
|
const nextVersion = Number.increment(model.pendingScrollVersion);
|
|
118
318
|
return __foldkitBrandViewResult(({
|
|
119
319
|
model: modifyFields(model, {
|
|
120
|
-
pendingScrollVersion: () => __foldkitBrandViewResult((nextVersion), "@foldkit/ui/virtualList/index.js#pendingScrollVersion
|
|
121
|
-
pendingScroll: () => __foldkitBrandViewResult((PendingScroll.
|
|
320
|
+
pendingScrollVersion: () => __foldkitBrandViewResult((nextVersion), "@foldkit/ui/virtualList/index.js#pendingScrollVersion"),
|
|
321
|
+
pendingScroll: () => __foldkitBrandViewResult((PendingScroll.Pending({ request, version: nextVersion })), "@foldkit/ui/virtualList/index.js#pendingScroll"),
|
|
122
322
|
}),
|
|
123
323
|
commands: [
|
|
124
324
|
ApplyScroll({
|
|
125
325
|
id: model.id,
|
|
126
|
-
|
|
326
|
+
request,
|
|
127
327
|
version: nextVersion,
|
|
128
328
|
}),
|
|
129
329
|
],
|
|
130
|
-
}), "@foldkit/ui/virtualList/index.js#
|
|
330
|
+
}), "@foldkit/ui/virtualList/index.js#buildScrollRequest");
|
|
331
|
+
};
|
|
332
|
+
const currentScrollRequest = (model) => __foldkitBrandViewResult((PendingScroll.match(model.pendingScroll, {
|
|
333
|
+
Idle: () => __foldkitBrandViewResult((InitialScroll.match(model.initialScroll, {
|
|
334
|
+
Applied: () => __foldkitBrandViewResult((ScrollRequest.Anchor({ anchor: model.viewportAnchor })), "@foldkit/ui/virtualList/index.js#Applied"),
|
|
335
|
+
Pending: ({ target, alignment }) => __foldkitBrandViewResult((ScrollRequest.Target({ target, alignment })), "@foldkit/ui/virtualList/index.js#Pending"),
|
|
336
|
+
})), "@foldkit/ui/virtualList/index.js#Idle"),
|
|
337
|
+
Pending: ({ request }) => __foldkitBrandViewResult((request), "@foldkit/ui/virtualList/index.js#Pending~2"),
|
|
338
|
+
})), "@foldkit/ui/virtualList/index.js#currentScrollRequest");
|
|
339
|
+
const reconcileLayout = (model) => __foldkitBrandViewResult((buildScrollRequest(model, currentScrollRequest(model))), "@foldkit/ui/virtualList/index.js#reconcileLayout");
|
|
340
|
+
const anchorFromSnapshot = (model, scrollTop, scrollHeight, containerHeight, anchor) => {
|
|
341
|
+
const distanceFromEnd = Math.max(0, scrollHeight - containerHeight - scrollTop);
|
|
342
|
+
const isFollowingEnd = EndBehavior.match(model.endBehavior, {
|
|
343
|
+
PreserveAnchor: () => __foldkitBrandViewResult((false), "@foldkit/ui/virtualList/index.js#PreserveAnchor"),
|
|
344
|
+
Follow: ({ thresholdPx }) => __foldkitBrandViewResult((distanceFromEnd <= thresholdPx), "@foldkit/ui/virtualList/index.js#Follow"),
|
|
345
|
+
});
|
|
346
|
+
if (isFollowingEnd) {
|
|
347
|
+
return __foldkitBrandViewResult((ViewportAnchor.End()), "@foldkit/ui/virtualList/index.js#anchorFromSnapshot");
|
|
348
|
+
}
|
|
349
|
+
return __foldkitBrandViewResult((ObservedAnchor.match(anchor, {
|
|
350
|
+
None: () => __foldkitBrandViewResult((ViewportAnchor.Offset({ scrollTop })), "@foldkit/ui/virtualList/index.js#None"),
|
|
351
|
+
Row: ({ key, index, viewportOffset }) => __foldkitBrandViewResult((ViewportAnchor.Row({ key, index, viewportOffset })), "@foldkit/ui/virtualList/index.js#Row~2"),
|
|
352
|
+
})), "@foldkit/ui/virtualList/index.js#anchorFromSnapshot");
|
|
353
|
+
};
|
|
354
|
+
const applyScrollSnapshot = (model, snapshot) => __foldkitBrandViewResult((modifyFields(model, {
|
|
355
|
+
scrollTop: () => __foldkitBrandViewResult((snapshot.scrollTop), "@foldkit/ui/virtualList/index.js#scrollTop"),
|
|
356
|
+
viewportAnchor: () => __foldkitBrandViewResult((anchorFromSnapshot(model, snapshot.scrollTop, snapshot.scrollHeight, snapshot.containerHeight, snapshot.anchor)), "@foldkit/ui/virtualList/index.js#viewportAnchor"),
|
|
357
|
+
})), "@foldkit/ui/virtualList/index.js#applyScrollSnapshot");
|
|
358
|
+
const measureContainer = (model, containerWidth, containerHeight) => {
|
|
359
|
+
const wasUnmeasured = model.measurement._tag === 'Unmeasured';
|
|
360
|
+
const didWidthChange = Measurement.match(model.measurement, {
|
|
361
|
+
Unmeasured: () => __foldkitBrandViewResult((false), "@foldkit/ui/virtualList/index.js#Unmeasured"),
|
|
362
|
+
Measured: measurement => __foldkitBrandViewResult((measurement.containerWidth !== containerWidth), "@foldkit/ui/virtualList/index.js#Measured"),
|
|
363
|
+
});
|
|
364
|
+
const recordContainerMeasurement = stepModel => (__foldkitBrandViewResult(({
|
|
365
|
+
model: modifyFields(stepModel, {
|
|
366
|
+
measurement: () => __foldkitBrandViewResult((Measurement.Measured({ containerWidth, containerHeight })), "@foldkit/ui/virtualList/index.js#measurement"),
|
|
367
|
+
measuredRowHeights: measuredRowHeights => __foldkitBrandViewResult((didWidthChange ? Record.empty() : measuredRowHeights), "@foldkit/ui/virtualList/index.js#measuredRowHeights"),
|
|
368
|
+
layoutVersion: layoutVersion => __foldkitBrandViewResult((didWidthChange ? Number.increment(layoutVersion) : layoutVersion), "@foldkit/ui/virtualList/index.js#layoutVersion"),
|
|
369
|
+
}),
|
|
370
|
+
}), "@foldkit/ui/virtualList/index.js#recordContainerMeasurement"));
|
|
371
|
+
const { initialScroll } = model;
|
|
372
|
+
if (initialScroll._tag === 'Pending') {
|
|
373
|
+
return __foldkitBrandViewResult((Update.combine(model, [
|
|
374
|
+
recordContainerMeasurement,
|
|
375
|
+
stepModel => __foldkitBrandViewResult((buildScrollRequest(stepModel, ScrollRequest.Target({
|
|
376
|
+
target: initialScroll.target,
|
|
377
|
+
alignment: initialScroll.alignment,
|
|
378
|
+
}))), "@foldkit/ui/virtualList/index.js#anonymous~15"),
|
|
379
|
+
])), "@foldkit/ui/virtualList/index.js#measureContainer");
|
|
380
|
+
}
|
|
381
|
+
if (wasUnmeasured) {
|
|
382
|
+
return __foldkitBrandViewResult((recordContainerMeasurement(model)), "@foldkit/ui/virtualList/index.js#measureContainer");
|
|
383
|
+
}
|
|
384
|
+
return __foldkitBrandViewResult((Update.combine(model, [recordContainerMeasurement, reconcileLayout])), "@foldkit/ui/virtualList/index.js#measureContainer");
|
|
385
|
+
};
|
|
386
|
+
const hasChangedMeasurement = (model, measurement) => {
|
|
387
|
+
if (measurement.layoutVersion !== model.layoutVersion) {
|
|
388
|
+
return __foldkitBrandViewResult((false), "@foldkit/ui/virtualList/index.js#hasChangedMeasurement");
|
|
389
|
+
}
|
|
390
|
+
return __foldkitBrandViewResult((pipe(model.measuredRowHeights, Record.get(measurement.key), Option.match({
|
|
391
|
+
onNone: () => __foldkitBrandViewResult((true), "@foldkit/ui/virtualList/index.js#onNone"),
|
|
392
|
+
onSome: height => __foldkitBrandViewResult((height !== measurement.height), "@foldkit/ui/virtualList/index.js#onSome"),
|
|
393
|
+
}))), "@foldkit/ui/virtualList/index.js#hasChangedMeasurement");
|
|
394
|
+
};
|
|
395
|
+
const applyRowMeasurements = (model, measurements) => {
|
|
396
|
+
const changedMeasurements = Array.filter(measurements, measurement => __foldkitBrandViewResult((hasChangedMeasurement(model, measurement)), "@foldkit/ui/virtualList/index.js#anonymous~16"));
|
|
397
|
+
if (Array.isArrayEmpty(changedMeasurements)) {
|
|
398
|
+
return __foldkitBrandViewResult(({ model }), "@foldkit/ui/virtualList/index.js#applyRowMeasurements");
|
|
399
|
+
}
|
|
400
|
+
const measuredRowHeights = Array.reduce(changedMeasurements, model.measuredRowHeights, (heights, measurement) => __foldkitBrandViewResult((Record.set(heights, measurement.key, measurement.height)), "@foldkit/ui/virtualList/index.js#anonymous~17"));
|
|
401
|
+
return __foldkitBrandViewResult((Update.combine(model, [
|
|
402
|
+
stepModel => (__foldkitBrandViewResult(({
|
|
403
|
+
model: modifyFields(stepModel, {
|
|
404
|
+
measuredRowHeights: () => __foldkitBrandViewResult((measuredRowHeights), "@foldkit/ui/virtualList/index.js#measuredRowHeights~2"),
|
|
405
|
+
}),
|
|
406
|
+
}), "@foldkit/ui/virtualList/index.js#anonymous~18")),
|
|
407
|
+
reconcileLayout,
|
|
408
|
+
])), "@foldkit/ui/virtualList/index.js#applyRowMeasurements");
|
|
131
409
|
};
|
|
410
|
+
/** Processes a VirtualList Message and returns the next Model and optional Commands. */
|
|
411
|
+
export const update = (model, message) => __foldkitBrandViewResult((Message.match(message, {
|
|
412
|
+
ObservedContainerScroll: snapshot => {
|
|
413
|
+
const nextPendingScrollVersion = PendingScroll.match(model.pendingScroll, {
|
|
414
|
+
Idle: () => __foldkitBrandViewResult((model.pendingScrollVersion), "@foldkit/ui/virtualList/index.js#Idle~2"),
|
|
415
|
+
Pending: () => __foldkitBrandViewResult((Number.increment(model.pendingScrollVersion)), "@foldkit/ui/virtualList/index.js#Pending~3"),
|
|
416
|
+
});
|
|
417
|
+
return __foldkitBrandViewResult(({
|
|
418
|
+
model: modifyFields(applyScrollSnapshot(model, snapshot), {
|
|
419
|
+
initialScroll: () => __foldkitBrandViewResult((InitialScroll.Applied()), "@foldkit/ui/virtualList/index.js#initialScroll"),
|
|
420
|
+
pendingScroll: () => __foldkitBrandViewResult((PendingScroll.Idle()), "@foldkit/ui/virtualList/index.js#pendingScroll~2"),
|
|
421
|
+
pendingScrollVersion: () => __foldkitBrandViewResult((nextPendingScrollVersion), "@foldkit/ui/virtualList/index.js#pendingScrollVersion~2"),
|
|
422
|
+
}),
|
|
423
|
+
}), "@foldkit/ui/virtualList/index.js#ObservedContainerScroll");
|
|
424
|
+
},
|
|
425
|
+
ResizedContainer: ({ containerWidth, containerHeight }) => __foldkitBrandViewResult((measureContainer(model, containerWidth, containerHeight)), "@foldkit/ui/virtualList/index.js#ResizedContainer"),
|
|
426
|
+
MeasuredRows: ({ measurements }) => __foldkitBrandViewResult((applyRowMeasurements(model, measurements)), "@foldkit/ui/virtualList/index.js#MeasuredRows"),
|
|
427
|
+
CompletedApplyScroll: ({ version, outcome }) => {
|
|
428
|
+
if (version !== model.pendingScrollVersion) {
|
|
429
|
+
return __foldkitBrandViewResult(({ model }), "@foldkit/ui/virtualList/index.js#CompletedApplyScroll");
|
|
430
|
+
}
|
|
431
|
+
return __foldkitBrandViewResult((ApplyScrollOutcome.match(outcome, {
|
|
432
|
+
Applied: snapshot => {
|
|
433
|
+
const scrolledModel = applyScrollSnapshot(model, snapshot);
|
|
434
|
+
return __foldkitBrandViewResult(({
|
|
435
|
+
model: modifyFields(scrolledModel, {
|
|
436
|
+
initialScroll: () => __foldkitBrandViewResult((snapshot.anchor._tag === 'Row'
|
|
437
|
+
? InitialScroll.Applied()
|
|
438
|
+
: model.initialScroll), "@foldkit/ui/virtualList/index.js#initialScroll~2"),
|
|
439
|
+
pendingScroll: () => __foldkitBrandViewResult((PendingScroll.Idle()), "@foldkit/ui/virtualList/index.js#pendingScroll~3"),
|
|
440
|
+
}),
|
|
441
|
+
}), "@foldkit/ui/virtualList/index.js#Applied~2");
|
|
442
|
+
},
|
|
443
|
+
Skipped: () => (__foldkitBrandViewResult(({
|
|
444
|
+
model: modifyFields(model, {
|
|
445
|
+
pendingScroll: () => __foldkitBrandViewResult((PendingScroll.Idle()), "@foldkit/ui/virtualList/index.js#pendingScroll~4"),
|
|
446
|
+
}),
|
|
447
|
+
}), "@foldkit/ui/virtualList/index.js#Skipped")),
|
|
448
|
+
})), "@foldkit/ui/virtualList/index.js#CompletedApplyScroll");
|
|
449
|
+
},
|
|
450
|
+
})), "@foldkit/ui/virtualList/index.js#update");
|
|
451
|
+
/** Programmatically scrolls to a logical target. */
|
|
452
|
+
export const scrollTo = (model, target, options = {}) => __foldkitBrandViewResult((Update.combine(model, [
|
|
453
|
+
stepModel => (__foldkitBrandViewResult(({
|
|
454
|
+
model: modifyFields(stepModel, {
|
|
455
|
+
initialScroll: () => __foldkitBrandViewResult((InitialScroll.Applied()), "@foldkit/ui/virtualList/index.js#initialScroll~3"),
|
|
456
|
+
}),
|
|
457
|
+
}), "@foldkit/ui/virtualList/index.js#anonymous~19")),
|
|
458
|
+
stepModel => __foldkitBrandViewResult((buildScrollRequest(stepModel, ScrollRequest.Target({
|
|
459
|
+
target,
|
|
460
|
+
alignment: options.alignment ?? 'Start',
|
|
461
|
+
}))), "@foldkit/ui/virtualList/index.js#anonymous~20"),
|
|
462
|
+
])), "@foldkit/ui/virtualList/index.js#scrollTo");
|
|
132
463
|
/** Programmatically scrolls the container so the row at `index` is visible.
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
*
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
*
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
* `model.rowHeightPx`) because items aren't reachable from the `update`
|
|
159
|
-
* function. Consumers who need an accurate initial scroll on a
|
|
160
|
-
* variable-height list should call `scrollToIndexVariable` after the first
|
|
161
|
-
* `MeasuredContainer` arrives. */
|
|
162
|
-
export const scrollToIndexVariable = (model, items, itemToRowHeightPx, index) => {
|
|
163
|
-
const cumulativeOffsets = prefixSum(items, itemToRowHeightPx);
|
|
164
|
-
const targetScrollTop = pipe(cumulativeOffsets, Array.get(Math.max(0, index)), Option.getOrElse(() => __foldkitBrandViewResult((lastOrZero(cumulativeOffsets)), "@foldkit/ui/virtualList/index.js#anonymous~2")));
|
|
165
|
-
return __foldkitBrandViewResult((buildScrollToIndex(model, index, targetScrollTop)), "@foldkit/ui/virtualList/index.js#scrollToIndexVariable");
|
|
464
|
+
* The next view resolves the logical index with its current sizing mode, and
|
|
465
|
+
* the Command aligns the live rendered row. */
|
|
466
|
+
export const scrollToIndex = (model, index, options = {}) => __foldkitBrandViewResult((scrollTo(model, ScrollTarget.Index({ index }), options)), "@foldkit/ui/virtualList/index.js#scrollToIndex");
|
|
467
|
+
/** Programmatically scrolls to the row whose `itemToKey` result matches
|
|
468
|
+
* `key`. The next view resolves the key against its current items. */
|
|
469
|
+
export const scrollToKey = (model, key, options = {}) => __foldkitBrandViewResult((scrollTo(model, ScrollTarget.Key({ key }), options)), "@foldkit/ui/virtualList/index.js#scrollToKey");
|
|
470
|
+
/** Programmatically scrolls to an exact pixel offset from the start of the
|
|
471
|
+
* list. Negative offsets clamp to zero. */
|
|
472
|
+
export const scrollToOffset = (model, offset) => __foldkitBrandViewResult((scrollTo(model, ScrollTarget.Offset({ offset }))), "@foldkit/ui/virtualList/index.js#scrollToOffset");
|
|
473
|
+
/** Programmatically scrolls to the end of the list. */
|
|
474
|
+
export const scrollToEnd = (model) => __foldkitBrandViewResult((scrollTo(model, ScrollTarget.End())), "@foldkit/ui/virtualList/index.js#scrollToEnd");
|
|
475
|
+
/** Notifies VirtualList that its parent-owned items changed. The next view
|
|
476
|
+
* resolves the stored stable-key anchor against the new items, and the
|
|
477
|
+
* Command restores that anchor after the DOM patch. */
|
|
478
|
+
export const informItemsChanged = (model, itemKeys) => {
|
|
479
|
+
const currentKeys = HashSet.fromIterable(itemKeys);
|
|
480
|
+
return __foldkitBrandViewResult((Update.combine(model, [
|
|
481
|
+
stepModel => (__foldkitBrandViewResult(({
|
|
482
|
+
model: modifyFields(stepModel, {
|
|
483
|
+
measuredRowHeights: Record.filter((_, key) => __foldkitBrandViewResult((HashSet.has(currentKeys, key)), "@foldkit/ui/virtualList/index.js#anonymous~22")),
|
|
484
|
+
layoutVersion: Number.increment,
|
|
485
|
+
}),
|
|
486
|
+
}), "@foldkit/ui/virtualList/index.js#anonymous~21")),
|
|
487
|
+
reconcileLayout,
|
|
488
|
+
])), "@foldkit/ui/virtualList/index.js#informItemsChanged");
|
|
166
489
|
};
|
|
167
490
|
const clampIndex = (index, itemCount) => __foldkitBrandViewResult((Math.max(0, Math.min(index, itemCount))), "@foldkit/ui/virtualList/index.js#clampIndex");
|
|
168
491
|
const prefixSum = (items, itemToRowHeightPx) => {
|
|
169
492
|
const heights = Array.map(items, itemToRowHeightPx);
|
|
170
|
-
return __foldkitBrandViewResult((Array.scan(heights, 0, (cumulative, height) => __foldkitBrandViewResult((cumulative + height), "@foldkit/ui/virtualList/index.js#anonymous~
|
|
493
|
+
return __foldkitBrandViewResult((Array.scan(heights, 0, (cumulative, height) => __foldkitBrandViewResult((cumulative + height), "@foldkit/ui/virtualList/index.js#anonymous~23"))), "@foldkit/ui/virtualList/index.js#prefixSum");
|
|
171
494
|
};
|
|
172
|
-
const lastOrZero = (values) => __foldkitBrandViewResult((pipe(values, Array.last, Option.getOrElse(() => __foldkitBrandViewResult((0), "@foldkit/ui/virtualList/index.js#anonymous~
|
|
173
|
-
/**
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
})), "@foldkit/ui/virtualList/index.js#Measured")
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
495
|
+
const lastOrZero = (values) => __foldkitBrandViewResult((pipe(values, Array.last, Option.getOrElse(() => __foldkitBrandViewResult((0), "@foldkit/ui/virtualList/index.js#anonymous~24")))), "@foldkit/ui/virtualList/index.js#lastOrZero");
|
|
496
|
+
/** Alignment of content when its total height is shorter than the viewport. */
|
|
497
|
+
export const ContentAlignment = Schema.Literals(['Start', 'End']);
|
|
498
|
+
const measuredRowHeight = (model, key) => __foldkitBrandViewResult((Record.get(model.measuredRowHeights, key)), "@foldkit/ui/virtualList/index.js#measuredRowHeight");
|
|
499
|
+
const listLayout = (model, items, itemToKey, itemToRowHeightPx, dynamicRowHeights, itemToEstimatedRowHeightPx, contentAlignment) => {
|
|
500
|
+
const rowHeightFor = (item, index) => {
|
|
501
|
+
if (dynamicRowHeights !== undefined) {
|
|
502
|
+
const key = itemToKey(item, index);
|
|
503
|
+
return __foldkitBrandViewResult((Option.getOrElse(measuredRowHeight(model, key), () => __foldkitBrandViewResult((itemToEstimatedRowHeightPx?.(item, index) ?? model.rowHeightPx), "@foldkit/ui/virtualList/index.js#anonymous~25"))), "@foldkit/ui/virtualList/index.js#rowHeightFor");
|
|
504
|
+
}
|
|
505
|
+
if (itemToRowHeightPx !== undefined) {
|
|
506
|
+
return __foldkitBrandViewResult((itemToRowHeightPx(item, index)), "@foldkit/ui/virtualList/index.js#rowHeightFor");
|
|
507
|
+
}
|
|
508
|
+
return __foldkitBrandViewResult((model.rowHeightPx), "@foldkit/ui/virtualList/index.js#rowHeightFor");
|
|
509
|
+
};
|
|
510
|
+
const maybeCumulativeOffsets = dynamicRowHeights === undefined && itemToRowHeightPx === undefined
|
|
511
|
+
? Option.none()
|
|
512
|
+
: Option.some(prefixSum(items, rowHeightFor));
|
|
513
|
+
const totalHeight = Option.match(maybeCumulativeOffsets, {
|
|
514
|
+
onNone: () => __foldkitBrandViewResult((items.length * model.rowHeightPx), "@foldkit/ui/virtualList/index.js#onNone~2"),
|
|
515
|
+
onSome: lastOrZero,
|
|
516
|
+
});
|
|
517
|
+
const containerHeight = Measurement.match(model.measurement, {
|
|
518
|
+
Unmeasured: () => __foldkitBrandViewResult((0), "@foldkit/ui/virtualList/index.js#Unmeasured~2"),
|
|
519
|
+
Measured: ({ containerHeight }) => __foldkitBrandViewResult((containerHeight), "@foldkit/ui/virtualList/index.js#Measured~2"),
|
|
520
|
+
});
|
|
521
|
+
const leadingInset = contentAlignment === 'End' ? Math.max(0, containerHeight - totalHeight) : 0;
|
|
522
|
+
return __foldkitBrandViewResult(({
|
|
523
|
+
maybeCumulativeOffsets,
|
|
524
|
+
rowHeightPx: model.rowHeightPx,
|
|
525
|
+
totalHeight,
|
|
526
|
+
leadingInset,
|
|
527
|
+
containerHeight,
|
|
528
|
+
maxScrollTop: Math.max(0, leadingInset + totalHeight - containerHeight),
|
|
529
|
+
}), "@foldkit/ui/virtualList/index.js#listLayout");
|
|
530
|
+
};
|
|
531
|
+
const offsetAt = (layout, index) => __foldkitBrandViewResult((Option.match(layout.maybeCumulativeOffsets, {
|
|
532
|
+
onNone: () => __foldkitBrandViewResult((index * layout.rowHeightPx), "@foldkit/ui/virtualList/index.js#onNone~3"),
|
|
533
|
+
onSome: cumulativeOffsets => __foldkitBrandViewResult((pipe(cumulativeOffsets, Array.get(index), Option.getOrElse(() => __foldkitBrandViewResult((layout.totalHeight), "@foldkit/ui/virtualList/index.js#anonymous~26")))), "@foldkit/ui/virtualList/index.js#onSome~2"),
|
|
534
|
+
})), "@foldkit/ui/virtualList/index.js#offsetAt");
|
|
535
|
+
const rowOffsetsForIndex = (layout, itemCount, index) => {
|
|
536
|
+
if (itemCount === 0) {
|
|
537
|
+
return __foldkitBrandViewResult((Option.none()), "@foldkit/ui/virtualList/index.js#rowOffsetsForIndex");
|
|
538
|
+
}
|
|
539
|
+
const clampedIndex = Math.max(0, Math.min(index, itemCount - 1));
|
|
540
|
+
return __foldkitBrandViewResult((Option.some({
|
|
541
|
+
startOffset: layout.leadingInset + offsetAt(layout, clampedIndex),
|
|
542
|
+
endOffset: layout.leadingInset + offsetAt(layout, clampedIndex + 1),
|
|
543
|
+
})), "@foldkit/ui/virtualList/index.js#rowOffsetsForIndex");
|
|
544
|
+
};
|
|
545
|
+
const indexForKey = (items, itemToKey, key) => __foldkitBrandViewResult((Array.findFirstIndex(items, (item, index) => __foldkitBrandViewResult((itemToKey(item, index) === key), "@foldkit/ui/virtualList/index.js#anonymous~27"))), "@foldkit/ui/virtualList/index.js#indexForKey");
|
|
546
|
+
const clampLayoutScrollTop = (layout, scrollTop) => __foldkitBrandViewResult((Math.max(0, Math.min(scrollTop, layout.maxScrollTop))), "@foldkit/ui/virtualList/index.js#clampLayoutScrollTop");
|
|
547
|
+
const alignedLayoutScrollTop = (model, layout, offsets, alignment) => __foldkitBrandViewResult((clampLayoutScrollTop(layout, scrollTopForRow(model.scrollTop, layout.containerHeight, offsets.startOffset, offsets.endOffset, alignment))), "@foldkit/ui/virtualList/index.js#alignedLayoutScrollTop");
|
|
548
|
+
const scrollTopForLayoutTarget = (model, layout, items, itemToKey, target, alignment) => __foldkitBrandViewResult((ScrollTarget.match(target, {
|
|
549
|
+
Index: ({ index }) => __foldkitBrandViewResult((Option.map(rowOffsetsForIndex(layout, items.length, index), offsets => __foldkitBrandViewResult((alignedLayoutScrollTop(model, layout, offsets, alignment)), "@foldkit/ui/virtualList/index.js#anonymous~28"))), "@foldkit/ui/virtualList/index.js#Index~3"),
|
|
550
|
+
Key: ({ key }) => __foldkitBrandViewResult((pipe(indexForKey(items, itemToKey, key), Option.flatMap(index => __foldkitBrandViewResult((rowOffsetsForIndex(layout, items.length, index)), "@foldkit/ui/virtualList/index.js#anonymous~29")), Option.map(offsets => __foldkitBrandViewResult((alignedLayoutScrollTop(model, layout, offsets, alignment)), "@foldkit/ui/virtualList/index.js#anonymous~30")))), "@foldkit/ui/virtualList/index.js#Key~3"),
|
|
551
|
+
Offset: ({ offset }) => __foldkitBrandViewResult((Option.some(clampLayoutScrollTop(layout, offset))), "@foldkit/ui/virtualList/index.js#Offset~3"),
|
|
552
|
+
End: () => __foldkitBrandViewResult((Option.some(layout.maxScrollTop)), "@foldkit/ui/virtualList/index.js#End~3"),
|
|
553
|
+
})), "@foldkit/ui/virtualList/index.js#scrollTopForLayoutTarget");
|
|
554
|
+
const scrollTopForLayoutAnchor = (layout, items, itemToKey, anchor) => __foldkitBrandViewResult((ViewportAnchor.match(anchor, {
|
|
555
|
+
Offset: ({ scrollTop }) => __foldkitBrandViewResult((clampLayoutScrollTop(layout, scrollTop)), "@foldkit/ui/virtualList/index.js#Offset~4"),
|
|
556
|
+
Row: ({ key, index, viewportOffset }) => {
|
|
557
|
+
const anchorIndex = Option.getOrElse(indexForKey(items, itemToKey, key), () => __foldkitBrandViewResult((index), "@foldkit/ui/virtualList/index.js#anonymous~31"));
|
|
558
|
+
const maybeOffsets = rowOffsetsForIndex(layout, items.length, anchorIndex);
|
|
559
|
+
return __foldkitBrandViewResult((Option.match(maybeOffsets, {
|
|
560
|
+
onNone: () => __foldkitBrandViewResult((0), "@foldkit/ui/virtualList/index.js#onNone~4"),
|
|
561
|
+
onSome: offsets => __foldkitBrandViewResult((clampLayoutScrollTop(layout, offsets.startOffset - viewportOffset)), "@foldkit/ui/virtualList/index.js#onSome~3"),
|
|
562
|
+
})), "@foldkit/ui/virtualList/index.js#Row~3");
|
|
232
563
|
},
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (
|
|
325
|
-
|
|
564
|
+
End: () => __foldkitBrandViewResult((layout.maxScrollTop), "@foldkit/ui/virtualList/index.js#End~4"),
|
|
565
|
+
})), "@foldkit/ui/virtualList/index.js#scrollTopForLayoutAnchor");
|
|
566
|
+
const scrollTopForView = (model, layout, items, itemToKey) => __foldkitBrandViewResult((PendingScroll.match(model.pendingScroll, {
|
|
567
|
+
Idle: () => __foldkitBrandViewResult((clampLayoutScrollTop(layout, model.scrollTop)), "@foldkit/ui/virtualList/index.js#Idle~3"),
|
|
568
|
+
Pending: ({ request }) => __foldkitBrandViewResult((ScrollRequest.match(request, {
|
|
569
|
+
Target: ({ target, alignment }) => __foldkitBrandViewResult((Option.getOrElse(scrollTopForLayoutTarget(model, layout, items, itemToKey, target, alignment), () => __foldkitBrandViewResult((clampLayoutScrollTop(layout, model.scrollTop)), "@foldkit/ui/virtualList/index.js#anonymous~32"))), "@foldkit/ui/virtualList/index.js#Target~2"),
|
|
570
|
+
Anchor: ({ anchor }) => __foldkitBrandViewResult((scrollTopForLayoutAnchor(layout, items, itemToKey, anchor)), "@foldkit/ui/virtualList/index.js#Anchor~2"),
|
|
571
|
+
})), "@foldkit/ui/virtualList/index.js#Pending~4"),
|
|
572
|
+
})), "@foldkit/ui/virtualList/index.js#scrollTopForView");
|
|
573
|
+
const visibleWindowForLayout = (layout, scrollTop, itemCount, overscan) => {
|
|
574
|
+
const contentScrollTop = Math.max(0, scrollTop - layout.leadingInset);
|
|
575
|
+
const contentViewportEnd = Math.max(0, scrollTop + layout.containerHeight - layout.leadingInset);
|
|
576
|
+
const firstVisibleIndex = Option.match(layout.maybeCumulativeOffsets, {
|
|
577
|
+
onNone: () => __foldkitBrandViewResult((Math.floor(contentScrollTop / layout.rowHeightPx)), "@foldkit/ui/virtualList/index.js#onNone~5"),
|
|
578
|
+
onSome: cumulativeOffsets => __foldkitBrandViewResult((pipe(cumulativeOffsets, Array.findFirstIndex(Number.isGreaterThan(contentScrollTop)), Option.match({
|
|
579
|
+
onNone: () => __foldkitBrandViewResult((itemCount), "@foldkit/ui/virtualList/index.js#onNone~6"),
|
|
580
|
+
onSome: index => __foldkitBrandViewResult((Math.max(0, index - 1)), "@foldkit/ui/virtualList/index.js#onSome~5"),
|
|
581
|
+
}))), "@foldkit/ui/virtualList/index.js#onSome~4"),
|
|
582
|
+
});
|
|
583
|
+
const lastVisibleIndex = Option.match(layout.maybeCumulativeOffsets, {
|
|
584
|
+
onNone: () => __foldkitBrandViewResult((Math.ceil(contentViewportEnd / layout.rowHeightPx)), "@foldkit/ui/virtualList/index.js#onNone~7"),
|
|
585
|
+
onSome: cumulativeOffsets => __foldkitBrandViewResult((pipe(cumulativeOffsets, Array.findFirstIndex(Number.isGreaterThanOrEqualTo(contentViewportEnd)), Option.getOrElse(() => __foldkitBrandViewResult((itemCount), "@foldkit/ui/virtualList/index.js#anonymous~33")))), "@foldkit/ui/virtualList/index.js#onSome~6"),
|
|
586
|
+
});
|
|
587
|
+
const startIndex = clampIndex(firstVisibleIndex - overscan, itemCount);
|
|
588
|
+
const endIndex = clampIndex(lastVisibleIndex + overscan, itemCount);
|
|
589
|
+
return __foldkitBrandViewResult(({
|
|
590
|
+
startIndex,
|
|
591
|
+
endIndex,
|
|
592
|
+
topSpacerHeight: layout.leadingInset + offsetAt(layout, startIndex),
|
|
593
|
+
bottomSpacerHeight: layout.totalHeight - offsetAt(layout, endIndex),
|
|
594
|
+
}), "@foldkit/ui/virtualList/index.js#visibleWindowForLayout");
|
|
595
|
+
};
|
|
596
|
+
// ELEMENT LIFECYCLE
|
|
597
|
+
const rowMeasurement = (entry) => {
|
|
598
|
+
if (!(entry.target instanceof HTMLElement)) {
|
|
599
|
+
return __foldkitBrandViewResult((Option.none()), "@foldkit/ui/virtualList/index.js#rowMeasurement");
|
|
600
|
+
}
|
|
601
|
+
const key = entry.target.getAttribute('data-virtual-list-item-key');
|
|
602
|
+
const layoutVersion = pipe(entry.target.getAttribute('data-virtual-list-layout-version'), Option.fromNullishOr, Option.flatMap(Number.parse));
|
|
603
|
+
if (key === null || Option.isNone(layoutVersion)) {
|
|
604
|
+
return __foldkitBrandViewResult((Option.none()), "@foldkit/ui/virtualList/index.js#rowMeasurement");
|
|
605
|
+
}
|
|
606
|
+
return __foldkitBrandViewResult((Option.some({
|
|
607
|
+
key,
|
|
608
|
+
height: entry.target.getBoundingClientRect().height,
|
|
609
|
+
layoutVersion: layoutVersion.value,
|
|
610
|
+
})), "@foldkit/ui/virtualList/index.js#rowMeasurement");
|
|
611
|
+
};
|
|
612
|
+
const observeVirtualList = (element, id) => __foldkitBrandViewResult((Stream.callback(queue => __foldkitBrandViewResult((Effect.acquireRelease(Effect.sync(() => {
|
|
613
|
+
if (!(element instanceof HTMLElement)) {
|
|
614
|
+
return __foldkitBrandViewResult((() => __foldkitBrandViewResult((undefined), "@foldkit/ui/virtualList/index.js#anonymous~36")), "@foldkit/ui/virtualList/index.js#anonymous~35");
|
|
615
|
+
}
|
|
616
|
+
const emitContainerMeasurement = () => __foldkitBrandViewResult((Queue.offerUnsafe(queue, Message.ResizedContainer({
|
|
617
|
+
containerWidth: element.clientWidth,
|
|
618
|
+
containerHeight: element.clientHeight,
|
|
619
|
+
}))), "@foldkit/ui/virtualList/index.js#emitContainerMeasurement");
|
|
620
|
+
emitContainerMeasurement();
|
|
621
|
+
const scrollListener = () => __foldkitBrandViewResult((Queue.offerUnsafe(queue, Message.ObservedContainerScroll({
|
|
622
|
+
scrollTop: element.scrollTop,
|
|
623
|
+
scrollHeight: element.scrollHeight,
|
|
624
|
+
containerHeight: element.clientHeight,
|
|
625
|
+
anchor: observedAnchor(element),
|
|
626
|
+
}))), "@foldkit/ui/virtualList/index.js#scrollListener");
|
|
627
|
+
element.addEventListener('scroll', scrollListener, { passive: true });
|
|
628
|
+
const containerResizeObserver = new ResizeObserver(entries => {
|
|
629
|
+
const lastEntry = Array.last(entries);
|
|
630
|
+
if (Option.isSome(lastEntry)) {
|
|
631
|
+
Queue.offerUnsafe(queue, Message.ResizedContainer({
|
|
632
|
+
containerWidth: lastEntry.value.contentRect.width,
|
|
633
|
+
containerHeight: lastEntry.value.contentRect.height,
|
|
634
|
+
}));
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
containerResizeObserver.observe(element);
|
|
638
|
+
const observedRows = new Map();
|
|
639
|
+
const rowResizeObserver = new ResizeObserver(entries => {
|
|
640
|
+
const measurements = Array.flatMap(entries, entry => __foldkitBrandViewResult((Option.match(rowMeasurement(entry), {
|
|
641
|
+
onNone: () => __foldkitBrandViewResult(([]), "@foldkit/ui/virtualList/index.js#onNone~8"),
|
|
642
|
+
onSome: measurement => __foldkitBrandViewResult(([measurement]), "@foldkit/ui/virtualList/index.js#onSome~7"),
|
|
643
|
+
})), "@foldkit/ui/virtualList/index.js#anonymous~39"));
|
|
644
|
+
if (Array.isArrayNonEmpty(measurements)) {
|
|
645
|
+
Queue.offerUnsafe(queue, Message.MeasuredRows({ measurements }));
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
const reconcileRows = () => {
|
|
649
|
+
if (element.childElementCount === 0) {
|
|
650
|
+
emitContainerMeasurement();
|
|
651
|
+
}
|
|
652
|
+
const rows = Array.filter(renderedRows(element), row => __foldkitBrandViewResult((row.getAttribute('data-virtual-list-measure') === 'true'), "@foldkit/ui/virtualList/index.js#anonymous~40"));
|
|
653
|
+
const measurableRows = new Set(rows);
|
|
654
|
+
for (const row of observedRows.keys()) {
|
|
655
|
+
if (!measurableRows.has(row)) {
|
|
656
|
+
rowResizeObserver.unobserve(row);
|
|
657
|
+
observedRows.delete(row);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
for (const row of rows) {
|
|
661
|
+
const layoutVersion = row.getAttribute('data-virtual-list-layout-version') ?? '';
|
|
662
|
+
if (observedRows.get(row) !== layoutVersion) {
|
|
663
|
+
rowResizeObserver.unobserve(row);
|
|
664
|
+
observedRows.set(row, layoutVersion);
|
|
665
|
+
rowResizeObserver.observe(row);
|
|
326
666
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
const mutationObserver = new MutationObserver(reconcileRows);
|
|
670
|
+
mutationObserver.observe(element, {
|
|
671
|
+
attributes: true,
|
|
672
|
+
attributeFilter: [
|
|
673
|
+
'data-virtual-list-layout-version',
|
|
674
|
+
'data-virtual-list-measure',
|
|
675
|
+
],
|
|
676
|
+
childList: true,
|
|
677
|
+
subtree: true,
|
|
678
|
+
});
|
|
679
|
+
reconcileRows();
|
|
680
|
+
const containers = mountedContainers.get(id) ?? new Set();
|
|
681
|
+
containers.add(element);
|
|
682
|
+
mountedContainers.set(id, containers);
|
|
683
|
+
return __foldkitBrandViewResult((() => {
|
|
684
|
+
const containers = mountedContainers.get(id);
|
|
685
|
+
if (containers !== undefined) {
|
|
686
|
+
containers.delete(element);
|
|
687
|
+
if (containers.size === 0) {
|
|
688
|
+
mountedContainers.delete(id);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
mutationObserver.disconnect();
|
|
692
|
+
rowResizeObserver.disconnect();
|
|
693
|
+
containerResizeObserver.disconnect();
|
|
694
|
+
element.removeEventListener('scroll', scrollListener);
|
|
695
|
+
}), "@foldkit/ui/virtualList/index.js#anonymous~35");
|
|
696
|
+
}), cleanup => __foldkitBrandViewResult((Effect.sync(cleanup)), "@foldkit/ui/virtualList/index.js#anonymous~42")).pipe(Effect.flatMap(() => __foldkitBrandViewResult((Effect.never), "@foldkit/ui/virtualList/index.js#anonymous~43")))), "@foldkit/ui/virtualList/index.js#anonymous~34"))), "@foldkit/ui/virtualList/index.js#observeVirtualList");
|
|
697
|
+
/** Container-owned Mount that tracks scrolling, container resizing, and
|
|
698
|
+
* rendered row measurements for dynamic-height lists. */
|
|
699
|
+
export const ObserveVirtualList = Mount.defineStream('ObserveVirtualList', {
|
|
700
|
+
args: { id: Schema.String },
|
|
701
|
+
messages: [
|
|
702
|
+
Message.ObservedContainerScroll,
|
|
703
|
+
Message.ResizedContainer,
|
|
704
|
+
Message.MeasuredRows,
|
|
705
|
+
],
|
|
706
|
+
execute: ({ element, id, viewStateChanges }) => __foldkitBrandViewResult((viewStateChanges.pipe(Stream.switchMap(viewState => __foldkitBrandViewResult((viewState === 'Live' ? observeVirtualList(element, id) : Stream.never), "@foldkit/ui/virtualList/index.js#anonymous~44")))), "@foldkit/ui/virtualList/index.js#execute~2"),
|
|
707
|
+
});
|
|
331
708
|
// VIEW
|
|
332
709
|
const DEFAULT_OVERSCAN = 5;
|
|
333
710
|
export const view = () =>
|
|
334
711
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
335
712
|
__foldkitBrandViewResult((viewImpl), "@foldkit/ui/virtualList/index.js#view");
|
|
336
713
|
const viewImpl = defineView((model, viewInputs, h) => {
|
|
337
|
-
const { items, itemToKey, itemToView, itemToRowHeightPx, overscan = DEFAULT_OVERSCAN, rowElement = 'li', containerClassName, containerAttributes = [], } = viewInputs;
|
|
714
|
+
const { items, itemToKey, itemToView, itemToRowHeightPx, dynamicRowHeights, itemToEstimatedRowHeightPx, overscan = DEFAULT_OVERSCAN, rowElement = 'li', containerClassName, containerAttributes = [], contentAlignment = 'Start', } = viewInputs;
|
|
338
715
|
const baseContainerAttributes = [
|
|
339
716
|
h.Id(model.id),
|
|
340
717
|
h.Role('list'),
|
|
341
|
-
h.DataAttribute('virtual-list-
|
|
718
|
+
h.DataAttribute('virtual-list-scroll-version', String(model.pendingScrollVersion)),
|
|
719
|
+
h.OnMount(ObserveVirtualList({ id: model.id })),
|
|
342
720
|
h.Style({
|
|
343
721
|
overflow: 'auto',
|
|
722
|
+
'overflow-anchor': 'none',
|
|
344
723
|
'list-style': 'none',
|
|
345
724
|
margin: '0',
|
|
346
725
|
padding: '0',
|
|
@@ -354,14 +733,13 @@ const viewImpl = defineView((model, viewInputs, h) => {
|
|
|
354
733
|
...containerAttributes,
|
|
355
734
|
];
|
|
356
735
|
const renderContainer = (children) => __foldkitBrandViewResult((h.keyed('ul')(model.id, allContainerAttributes, children)), "@foldkit/ui/virtualList/index.js#renderContainer");
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
:
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
: model.rowHeightPx), "@foldkit/ui/virtualList/index.js#rowHeightFor");
|
|
736
|
+
const layout = listLayout(model, items, itemToKey, itemToRowHeightPx, dynamicRowHeights, itemToEstimatedRowHeightPx, contentAlignment);
|
|
737
|
+
const maybeWindow = Measurement.match(model.measurement, {
|
|
738
|
+
Unmeasured: () => __foldkitBrandViewResult((Option.none()), "@foldkit/ui/virtualList/index.js#Unmeasured~3"),
|
|
739
|
+
Measured: () => __foldkitBrandViewResult((Option.some(visibleWindowForLayout(layout, scrollTopForView(model, layout, items, itemToKey), items.length, overscan))), "@foldkit/ui/virtualList/index.js#Measured~3"),
|
|
740
|
+
});
|
|
363
741
|
return __foldkitBrandViewResult((Option.match(maybeWindow, {
|
|
364
|
-
onNone: () => __foldkitBrandViewResult((renderContainer([])), "@foldkit/ui/virtualList/index.js#onNone~
|
|
742
|
+
onNone: () => __foldkitBrandViewResult((renderContainer([])), "@foldkit/ui/virtualList/index.js#onNone~9"),
|
|
365
743
|
onSome: ({ startIndex, endIndex, topSpacerHeight, bottomSpacerHeight, }) => {
|
|
366
744
|
const visibleItems = items.slice(startIndex, endIndex);
|
|
367
745
|
const topSpacer = h.keyed('li')(`${model.id}-top-spacer`, [
|
|
@@ -374,18 +752,30 @@ const viewImpl = defineView((model, viewInputs, h) => {
|
|
|
374
752
|
]);
|
|
375
753
|
const renderedRows = Array.map(visibleItems, (item, sliceIndex) => {
|
|
376
754
|
const dataIndex = startIndex + sliceIndex;
|
|
377
|
-
|
|
755
|
+
const key = itemToKey(item, dataIndex);
|
|
756
|
+
const rowHeight = offsetAt(layout, dataIndex + 1) - offsetAt(layout, dataIndex);
|
|
757
|
+
const rowSizingAttributes = dynamicRowHeights === undefined
|
|
758
|
+
? [
|
|
759
|
+
h.Style({
|
|
760
|
+
height: `${rowHeight}px`,
|
|
761
|
+
display: 'grid',
|
|
762
|
+
}),
|
|
763
|
+
]
|
|
764
|
+
: [
|
|
765
|
+
h.DataAttribute('virtual-list-measure', 'true'),
|
|
766
|
+
h.DataAttribute('virtual-list-layout-version', String(model.layoutVersion)),
|
|
767
|
+
h.Style({ display: 'grid' }),
|
|
768
|
+
];
|
|
769
|
+
return __foldkitBrandViewResult((h.keyed(rowElement)(key, [
|
|
378
770
|
h.Role('listitem'),
|
|
771
|
+
h.DataAttribute('virtual-list-item-key', key),
|
|
379
772
|
h.DataAttribute('virtual-list-item-index', String(dataIndex)),
|
|
380
773
|
h.AriaSetsize(items.length),
|
|
381
774
|
h.AriaPosinset(dataIndex + 1),
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
display: 'grid',
|
|
385
|
-
}),
|
|
386
|
-
], [itemToView(item, dataIndex)])), "@foldkit/ui/virtualList/index.js#anonymous~18");
|
|
775
|
+
...rowSizingAttributes,
|
|
776
|
+
], [itemToView(item, dataIndex)])), "@foldkit/ui/virtualList/index.js#anonymous~46");
|
|
387
777
|
});
|
|
388
|
-
return __foldkitBrandViewResult((renderContainer([topSpacer, ...renderedRows, bottomSpacer])), "@foldkit/ui/virtualList/index.js#onSome~
|
|
778
|
+
return __foldkitBrandViewResult((renderContainer([topSpacer, ...renderedRows, bottomSpacer])), "@foldkit/ui/virtualList/index.js#onSome~8");
|
|
389
779
|
},
|
|
390
|
-
})), "@foldkit/ui/virtualList/index.js#anonymous~
|
|
780
|
+
})), "@foldkit/ui/virtualList/index.js#anonymous~45");
|
|
391
781
|
});
|