@fusion.dev/helix-core 0.211.0 → 0.211.1

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.
@@ -68,6 +68,15 @@ export declare class Heights {
68
68
  * one of them back at the estimate at the moment the reader is looking at them.
69
69
  */
70
70
  shift(by: number): void;
71
+ /**
72
+ * Adds `by` to every height that has been seen, for a gap that changed.
73
+ *
74
+ * Each item counts the gap after it as part of its own height, so a gap that
75
+ * grows by twelve pixels grows every measured item by twelve, the ones no
76
+ * longer drawn included. The unmeasured ones follow the estimate, which
77
+ * carries the gap already.
78
+ */
79
+ pad(by: number): void;
71
80
  /**
72
81
  * Drops everything, for a set that is no longer the same set.
73
82
  *
@@ -70,6 +70,16 @@ export class Heights {
70
70
  this.dirty = 0;
71
71
  this.changes += 1;
72
72
  }
73
+ pad(by) {
74
+ if (by === 0 || this.seen.size === 0) {
75
+ return;
76
+ }
77
+ for (const [index, size] of this.seen) {
78
+ this.seen.set(index, Math.max(1, Math.round((size + by) * 64) / 64));
79
+ }
80
+ this.dirty = 0;
81
+ this.changes += 1;
82
+ }
73
83
  forget() {
74
84
  this.seen.clear();
75
85
  this.dirty = 0;
@@ -172,12 +172,18 @@ export declare class VirtualizeController implements ReactiveController {
172
172
  /** Room `shift` added above the window, owed to the scroll offset once drawn. */
173
173
  private owed;
174
174
  /**
175
- * The drawn rows' height on the straight line, read once per estimate.
175
+ * The drawn rows' own height on the straight line, read once per estimate,
176
+ * and without the gap, which `line` adds as it is now.
176
177
  *
177
178
  * Undefined until a host that offers `rows` has drawn some on the uniform
178
179
  * path, and again after `forget`.
179
180
  */
180
181
  private sample;
182
+ /**
183
+ * The gap the heights and the line were last squared with, which `line`
184
+ * reads, so the gap the host has now is taken up in `respace` alone.
185
+ */
186
+ private spaced;
181
187
  /** The length of the set when `endInSight` last answered true. */
182
188
  private askedAt;
183
189
  /**
@@ -317,6 +323,24 @@ export declare class VirtualizeController implements ReactiveController {
317
323
  * Reading the drawn rows once corrects the slope, and it is still a line.
318
324
  */
319
325
  private line;
326
+ /** How tall one item's own row is on the straight line, without the gap. */
327
+ private row;
328
+ /**
329
+ * Moves everything to a gap that changed, and keeps the reader on the item
330
+ * they were on.
331
+ *
332
+ * **Each item counts the gap after it as part of its own height**, so a gap
333
+ * that changes changes every item. The straight line reads the gap as it is
334
+ * now, and every height measured with the old one moves by the difference,
335
+ * drawn or not. Left alone, both kept the old gap while the rows drawn
336
+ * between the spacers took the new one, so the set grew or shrank as the
337
+ * window did: in WebKit, End on 1,000 rows whose gap went from 0 to 12 left
338
+ * the last row 72 pixels below the box once the window lost six rows.
339
+ *
340
+ * The reader's place is owed to the scroll offset and written after the
341
+ * render, as `shift` does, because a gap that grows has no room for it yet.
342
+ */
343
+ private respace;
320
344
  /** The space the host draws between two items, which each item counts. */
321
345
  private gap;
322
346
  /**
@@ -154,6 +154,7 @@ export class VirtualizeController {
154
154
  this.watch();
155
155
  const box = this.options.container();
156
156
  const total = Math.max(0, this.options.count());
157
+ this.respace(box, total);
157
158
  const size = this.line();
158
159
  if (!box) {
159
160
  return;
@@ -196,8 +197,35 @@ export class VirtualizeController {
196
197
  return { after: Math.max(0, (total - last) * size), before: first * size, first, last };
197
198
  }
198
199
  line() {
200
+ return this.row() + (this.spaced ?? this.gap());
201
+ }
202
+ row() {
199
203
  const estimate = Math.max(1, this.options.estimate());
200
- return this.sample?.estimate === estimate ? this.sample.size : estimate + this.gap();
204
+ return this.sample?.estimate === estimate ? this.sample.size : estimate;
205
+ }
206
+ respace(box, total) {
207
+ const gap = this.gap();
208
+ const was = this.spaced;
209
+ if (was === undefined) {
210
+ this.spaced = gap;
211
+ return;
212
+ }
213
+ if (was === gap) {
214
+ return;
215
+ }
216
+ const top = box ? this.offset(box) : 0;
217
+ const anchor = this.measuring() ? (this.target ?? this.indexAt(top, total)) : 0;
218
+ const from = this.measuring() ? this.startOf(anchor, total) : this.line();
219
+ this.heights.pad(gap - was);
220
+ this.spaced = gap;
221
+ const moved = this.measuring()
222
+ ? this.startOf(anchor, total) - from
223
+ : (top / from) * this.line() - top;
224
+ this.settling = 0;
225
+ if (box && top > 0 && moved !== 0 && (this.options.anchor?.() ?? true)) {
226
+ this.owed += moved;
227
+ this.host.requestUpdate();
228
+ }
201
229
  }
202
230
  gap() {
203
231
  return Math.max(0, this.options.gap?.() ?? 0);
@@ -215,8 +243,11 @@ export class VirtualizeController {
215
243
  const uneven = Math.max(...seen) - Math.min(...seen) > 1;
216
244
  if (this.sample?.estimate !== estimate) {
217
245
  const was = this.line();
218
- const size = Math.max(1, seen.reduce((sum, height) => sum + height, 0) / seen.length) + this.gap();
219
- this.sample = { estimate, size };
246
+ this.sample = {
247
+ estimate,
248
+ size: Math.max(1, seen.reduce((sum, height) => sum + height, 0) / seen.length),
249
+ };
250
+ const size = this.line();
220
251
  if (Math.abs(size - was) >= 0.5) {
221
252
  const box = this.options.container();
222
253
  if (box && this.scrolled(box) > 0) {
@@ -337,6 +368,7 @@ export class VirtualizeController {
337
368
  this.settle();
338
369
  }
339
370
  settle() {
371
+ this.respace(this.options.container(), Math.max(0, this.options.count()));
340
372
  if (!this.measuring()) {
341
373
  this.sampleRows();
342
374
  return;
package/dist/version.d.ts CHANGED
@@ -6,4 +6,4 @@
6
6
  * release, so the two never disagree; a spec beside this holds them to it.
7
7
  * Nothing else in the running code knows which release it is.
8
8
  */
9
- export declare const VERSION = "0.211.0";
9
+ export declare const VERSION = "0.211.1";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.211.0';
1
+ export const VERSION = '0.211.1';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-core",
3
3
  "description": "Helix element primitives: base classes, mixins, decorators, utilities.",
4
- "version": "0.211.0",
4
+ "version": "0.211.1",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -81,12 +81,12 @@
81
81
  "bugs": "https://github.com/project-fusion/fusion/issues",
82
82
  "dependencies": {
83
83
  "@floating-ui/dom": "^1.8.0",
84
- "@fusion.dev/helix-icons": "0.211.0",
84
+ "@fusion.dev/helix-icons": "0.211.1",
85
85
  "tslib": "^2.8.1"
86
86
  },
87
87
  "devDependencies": {
88
- "@fusion.dev/helix-cem": "0.211.0",
89
- "@fusion.dev/helix-testing": "0.211.0",
88
+ "@fusion.dev/helix-cem": "0.211.1",
89
+ "@fusion.dev/helix-testing": "0.211.1",
90
90
  "@lit-labs/ssr": "^4.1.0",
91
91
  "lit": "^3.2.1"
92
92
  },