@fusion.dev/helix-core 0.210.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.
- package/custom-elements.json +1 -1
- package/dist/controllers/aria-forward-controller/aria-forward-controller.js +1 -1
- package/dist/controllers/virtualize-controller/heights.d.ts +9 -0
- package/dist/controllers/virtualize-controller/heights.js +10 -0
- package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +25 -1
- package/dist/controllers/virtualize-controller/virtualize-controller.js +35 -3
- package/dist/index.d.ts +1 -1
- package/dist/mixins/mixin-viewport/mixin-viewport.d.ts +18 -0
- package/dist/mixins/mixin-viewport/mixin-viewport.js +79 -17
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -3,6 +3,7 @@ import { elementById } from '../../utils/element-by-id/element-by-id.js';
|
|
|
3
3
|
import { labelsOf } from '../../utils/labels-of/labels-of.js';
|
|
4
4
|
import { nameText } from '../../utils/name-text/name-text.js';
|
|
5
5
|
const RELATIONSHIPS = [
|
|
6
|
+
['aria-controls', 'ariaControlsElements'],
|
|
6
7
|
['aria-describedby', 'ariaDescribedByElements'],
|
|
7
8
|
['aria-labelledby', 'ariaLabelledByElements'],
|
|
8
9
|
];
|
|
@@ -10,7 +11,6 @@ const STATES = [
|
|
|
10
11
|
'aria-current',
|
|
11
12
|
'aria-expanded',
|
|
12
13
|
'aria-haspopup',
|
|
13
|
-
'aria-controls',
|
|
14
14
|
'aria-invalid',
|
|
15
15
|
'aria-pressed',
|
|
16
16
|
'aria-required',
|
|
@@ -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
|
|
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
|
-
|
|
219
|
-
|
|
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/index.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export { MixinPickerField, type MixinPickerFieldInterface, } from './mixins/mixi
|
|
|
60
60
|
export { MixinSegmentedField, type MixinSegmentedFieldInterface, } from './mixins/mixin-segmented-field/mixin-segmented-field.js';
|
|
61
61
|
export { MixinSelectableItem, type MixinSelectableItemInterface, } from './mixins/mixin-selectable-item/mixin-selectable-item.js';
|
|
62
62
|
export { MixinTokens, type MixinTokensInterface } from './mixins/mixin-tokens/mixin-tokens.js';
|
|
63
|
-
export { MixinViewport, type ViewportControlsPlacement, type ViewportElement, type ViewportGestureLabels, type ViewportGestureMode, type ViewportGestures, type ViewportWay, } from './mixins/mixin-viewport/mixin-viewport.js';
|
|
63
|
+
export { MixinViewport, type ViewportControlAxes, type ViewportControlsPlacement, type ViewportElement, type ViewportGestureLabels, type ViewportGestureMode, type ViewportGestures, type ViewportWay, } from './mixins/mixin-viewport/mixin-viewport.js';
|
|
64
64
|
export { anchorElement } from './utils/anchor-element/anchor-element.js';
|
|
65
65
|
export { arrowReach, sideOf } from './utils/arrow/arrow.js';
|
|
66
66
|
export { assignSlot, assignSlots } from './utils/assign-slots/assign-slots.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { nothing, type TemplateResult } from 'lit';
|
|
2
|
+
import { type StaticValue } from 'lit/static-html.js';
|
|
2
3
|
import { FullscreenController } from '../../controllers/fullscreen-controller/fullscreen-controller.js';
|
|
3
4
|
import { IdleController } from '../../controllers/idle-controller/idle-controller.js';
|
|
4
5
|
import type { ViewportPanDetail, ViewportZoomDetail } from '../../events/events.js';
|
|
@@ -8,6 +9,15 @@ type Constructor<T> = new (...args: any[]) => T;
|
|
|
8
9
|
export type ViewportWay = 'up' | 'down' | 'start' | 'end';
|
|
9
10
|
/** Where the controls sit, in the corner words the rest of the library uses. */
|
|
10
11
|
export type ViewportControlsPlacement = 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';
|
|
12
|
+
/**
|
|
13
|
+
* The styled axes a surface's controls are drawn with, which the styled tier
|
|
14
|
+
* says and the headless tier leaves unset, having nothing to paint: `raised`,
|
|
15
|
+
* since the controls float over the drawing, and the `material` under them.
|
|
16
|
+
*/
|
|
17
|
+
export interface ViewportControlAxes {
|
|
18
|
+
material?: 'frosted' | 'glass' | 'transparent' | undefined;
|
|
19
|
+
raised: boolean;
|
|
20
|
+
}
|
|
11
21
|
/** Which gestures a surface takes from the page. */
|
|
12
22
|
export type ViewportGestures = 'activate' | 'always' | 'cooperative';
|
|
13
23
|
/**
|
|
@@ -112,6 +122,14 @@ export declare class ViewportElement {
|
|
|
112
122
|
protected drawsControls(group: string): boolean;
|
|
113
123
|
/** More buttons for the group of controls, after zoom, for a family that adds its own. */
|
|
114
124
|
protected renderMoreControls(): TemplateResult | typeof nothing;
|
|
125
|
+
/**
|
|
126
|
+
* The element each control is drawn as: the family's own Icon Button, which
|
|
127
|
+
* the family names with `this.tag('icon-button')` so its source says what it
|
|
128
|
+
* renders and its imports register it.
|
|
129
|
+
*/
|
|
130
|
+
protected get controlTag(): StaticValue;
|
|
131
|
+
/** The styled axes each control is drawn with, unset on the headless tier. */
|
|
132
|
+
protected get controlAxes(): ViewportControlAxes;
|
|
115
133
|
/** Answers a key the family adds, and says whether it did. */
|
|
116
134
|
protected handleMoreKeys(event: KeyboardEvent): boolean;
|
|
117
135
|
/** What one pan control is called, for a family that draws its own. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { html, nothing } from 'lit';
|
|
3
3
|
import { property, state } from 'lit/decorators.js';
|
|
4
|
+
import { html as staticHtml } from 'lit/static-html.js';
|
|
4
5
|
import { FullscreenController } from '../../controllers/fullscreen-controller/fullscreen-controller.js';
|
|
5
6
|
import { IdleController } from '../../controllers/idle-controller/idle-controller.js';
|
|
6
7
|
import { ToolbarController } from '../../controllers/toolbar-controller/toolbar-controller.js';
|
|
@@ -8,6 +9,7 @@ import { inlineStep } from '../../utils/inline-step/inline-step.js';
|
|
|
8
9
|
import { isRtl } from '../../utils/is-rtl/is-rtl.js';
|
|
9
10
|
import { numberConverter, timeConverter } from '../../utils/number-converter/number-converter.js';
|
|
10
11
|
import { formatWords, words } from '../../words/words.js';
|
|
12
|
+
const stopPress = (event) => event.stopPropagation();
|
|
11
13
|
const HINT_SHOWS = 1600;
|
|
12
14
|
export const MixinViewport = (Base) => {
|
|
13
15
|
class Viewport extends Base {
|
|
@@ -260,6 +262,12 @@ export const MixinViewport = (Base) => {
|
|
|
260
262
|
? this.panLeftLabel
|
|
261
263
|
: this.panRightLabel;
|
|
262
264
|
}
|
|
265
|
+
get controlTag() {
|
|
266
|
+
return this.tag('icon-button');
|
|
267
|
+
}
|
|
268
|
+
get controlAxes() {
|
|
269
|
+
return { raised: false };
|
|
270
|
+
}
|
|
263
271
|
panGlyph(way) {
|
|
264
272
|
return { down: '↓', left: '←', right: '→', up: '↑' }[this.sideOf(way)] ?? '';
|
|
265
273
|
}
|
|
@@ -283,40 +291,94 @@ export const MixinViewport = (Base) => {
|
|
|
283
291
|
return nothing;
|
|
284
292
|
}
|
|
285
293
|
const [up, start, end, down] = ['up', 'start', 'end', 'down'].map((way) => this.panGlyph(way));
|
|
286
|
-
|
|
294
|
+
const button = this.controlTag;
|
|
295
|
+
const { material, raised } = this.controlAxes;
|
|
296
|
+
const ground = material ?? nothing;
|
|
297
|
+
return staticHtml `<div part="controls" role="toolbar" aria-label=${this.controlsLabel}>
|
|
287
298
|
${pan
|
|
288
|
-
?
|
|
299
|
+
? staticHtml `<${button}
|
|
300
|
+
part="pan-up"
|
|
301
|
+
exportparts="button: pan-up-button, live: pan-up-live, spinner: pan-up-spinner, text: pan-up-text"
|
|
302
|
+
label=${this.panLabel('up')}
|
|
303
|
+
material=${ground}
|
|
304
|
+
?raised=${raised}
|
|
305
|
+
@click=${() => this.pan('up')}
|
|
306
|
+
@helix-press=${stopPress}
|
|
307
|
+
>
|
|
289
308
|
<slot name="pan-up-icon">${up}</slot>
|
|
290
|
-
|
|
291
|
-
|
|
309
|
+
</${button}>
|
|
310
|
+
<${button}
|
|
311
|
+
part="pan-start"
|
|
312
|
+
exportparts="button: pan-start-button, live: pan-start-live, spinner: pan-start-spinner, text: pan-start-text"
|
|
313
|
+
label=${this.panLabel('start')}
|
|
314
|
+
material=${ground}
|
|
315
|
+
?raised=${raised}
|
|
316
|
+
@click=${() => this.pan('start')}
|
|
317
|
+
@helix-press=${stopPress}
|
|
318
|
+
>
|
|
292
319
|
<slot name="pan-start-icon">${start}</slot>
|
|
293
|
-
|
|
294
|
-
|
|
320
|
+
</${button}>
|
|
321
|
+
<${button}
|
|
322
|
+
part="pan-end"
|
|
323
|
+
exportparts="button: pan-end-button, live: pan-end-live, spinner: pan-end-spinner, text: pan-end-text"
|
|
324
|
+
label=${this.panLabel('end')}
|
|
325
|
+
material=${ground}
|
|
326
|
+
?raised=${raised}
|
|
327
|
+
@click=${() => this.pan('end')}
|
|
328
|
+
@helix-press=${stopPress}
|
|
329
|
+
>
|
|
295
330
|
<slot name="pan-end-icon">${end}</slot>
|
|
296
|
-
|
|
297
|
-
|
|
331
|
+
</${button}>
|
|
332
|
+
<${button}
|
|
333
|
+
part="pan-down"
|
|
334
|
+
exportparts="button: pan-down-button, live: pan-down-live, spinner: pan-down-spinner, text: pan-down-text"
|
|
335
|
+
label=${this.panLabel('down')}
|
|
336
|
+
material=${ground}
|
|
337
|
+
?raised=${raised}
|
|
338
|
+
@click=${() => this.pan('down')}
|
|
339
|
+
@helix-press=${stopPress}
|
|
340
|
+
>
|
|
298
341
|
<slot name="pan-down-icon">${down}</slot>
|
|
299
|
-
|
|
342
|
+
</${button}>`
|
|
300
343
|
: nothing}
|
|
301
344
|
${zoom
|
|
302
|
-
?
|
|
345
|
+
? staticHtml `<${button}
|
|
346
|
+
part="zoom-in"
|
|
347
|
+
exportparts="button: zoom-in-button, live: zoom-in-live, spinner: zoom-in-spinner, text: zoom-in-text"
|
|
348
|
+
label=${this.zoomInLabel}
|
|
349
|
+
material=${ground}
|
|
350
|
+
?raised=${raised}
|
|
351
|
+
@click=${() => this.zoom(1)}
|
|
352
|
+
@helix-press=${stopPress}
|
|
353
|
+
>
|
|
303
354
|
<slot name="zoom-in-icon">+</slot>
|
|
304
|
-
|
|
305
|
-
|
|
355
|
+
</${button}>
|
|
356
|
+
<${button}
|
|
357
|
+
part="zoom-out"
|
|
358
|
+
exportparts="button: zoom-out-button, live: zoom-out-live, spinner: zoom-out-spinner, text: zoom-out-text"
|
|
359
|
+
label=${this.zoomOutLabel}
|
|
360
|
+
material=${ground}
|
|
361
|
+
?raised=${raised}
|
|
362
|
+
@click=${() => this.zoom(-1)}
|
|
363
|
+
@helix-press=${stopPress}
|
|
364
|
+
>
|
|
306
365
|
<slot name="zoom-out-icon">−</slot>
|
|
307
|
-
|
|
366
|
+
</${button}>`
|
|
308
367
|
: nothing}
|
|
309
368
|
${more}
|
|
310
369
|
${fullscreen
|
|
311
|
-
?
|
|
370
|
+
? staticHtml `<${button}
|
|
312
371
|
part="fullscreen"
|
|
313
|
-
|
|
372
|
+
exportparts="button: fullscreen-button, live: fullscreen-live, spinner: fullscreen-spinner, text: fullscreen-text"
|
|
314
373
|
aria-pressed=${this.filled ? 'true' : 'false'}
|
|
315
|
-
|
|
374
|
+
label=${this.fullscreenLabel}
|
|
375
|
+
material=${ground}
|
|
376
|
+
?raised=${raised}
|
|
316
377
|
@click=${this.toggleFullscreen}
|
|
378
|
+
@helix-press=${stopPress}
|
|
317
379
|
>
|
|
318
380
|
<slot name="fullscreen-icon">⤢</slot>
|
|
319
|
-
|
|
381
|
+
</${button}>`
|
|
320
382
|
: nothing}
|
|
321
383
|
</div>`;
|
|
322
384
|
}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '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.
|
|
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.
|
|
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.
|
|
89
|
-
"@fusion.dev/helix-testing": "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
|
},
|