@fusion.dev/helix-headless 0.159.0 → 0.161.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/custom-elements.json +2980 -189
- package/dist/canvas/root/canvas-root.d.ts +59 -2
- package/dist/canvas/root/canvas-root.js +123 -8
- package/dist/chat/message/chat-message.d.ts +8 -0
- package/dist/chat/message/chat-message.js +11 -0
- package/dist/chat/root/chat-root.d.ts +47 -1
- package/dist/chat/root/chat-root.js +130 -4
- package/dist/code-block/lines.d.ts +12 -0
- package/dist/code-block/lines.js +41 -0
- package/dist/code-block/root/code-block-root.d.ts +88 -5
- package/dist/code-block/root/code-block-root.js +154 -10
- package/dist/date-input/root/date-input-root.d.ts +108 -19
- package/dist/date-input/root/date-input-root.js +112 -13
- package/dist/date-picker/root/date-picker-root.d.ts +81 -16
- package/dist/date-picker/root/date-picker-root.js +140 -36
- package/dist/element-events.generated.js +6 -0
- package/dist/format-date/fields.js +1 -0
- package/dist/format-date/options.d.ts +3 -1
- package/dist/format-date/options.js +4 -1
- package/dist/format-date/root/format-date-root.d.ts +27 -11
- package/dist/format-date/root/format-date-root.js +6 -0
- package/dist/listbox/root/listbox-root.js +5 -0
- package/dist/notifications/notification/notification.d.ts +1 -0
- package/dist/notifications/notification/notification.js +2 -1
- package/dist/slider/root/slider-root.d.ts +54 -3
- package/dist/slider/root/slider-root.js +124 -2
- package/dist/splitter/root/splitter-root.d.ts +15 -0
- package/dist/splitter/root/splitter-root.js +16 -2
- package/dist/table/grid-controller.d.ts +7 -1
- package/dist/table/grid-controller.js +9 -1
- package/dist/table/root/table-root.d.ts +122 -0
- package/dist/table/root/table-root.js +293 -17
- package/dist/table/row/table-row.d.ts +41 -0
- package/dist/table/row/table-row.js +56 -2
- package/dist/time-input/root/time-input-root.d.ts +55 -7
- package/dist/time-input/root/time-input-root.js +70 -10
- package/dist/time-picker/root/time-picker-root.d.ts +27 -5
- package/dist/time-picker/root/time-picker-root.js +52 -13
- package/dist/timeline/item/timeline-item.d.ts +47 -10
- package/dist/timeline/item/timeline-item.js +36 -4
- package/dist/toggle/group/toggle-group.d.ts +18 -1
- package/dist/toggle/group/toggle-group.js +8 -2
- package/dist/toggle/root/toggle-root.d.ts +2 -2
- package/dist/toggle/root/toggle-root.js +2 -1
- package/dist/video-playlist/item/video-playlist-item.d.ts +11 -0
- package/dist/video-playlist/item/video-playlist-item.js +5 -0
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +6 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +11 -0
- package/dist/video-playlist/root/video-playlist-root.d.ts +59 -5
- package/dist/video-playlist/root/video-playlist-root.js +82 -10
- package/dist/virtual-list/fill.d.ts +6 -1
- package/dist/virtual-list/fill.js +1 -1
- package/dist/virtual-list/keys.d.ts +8 -0
- package/dist/virtual-list/keys.js +20 -0
- package/dist/virtual-list/root/virtual-list-root.d.ts +98 -13
- package/dist/virtual-list/root/virtual-list-root.js +195 -20
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +1596 -224
- package/types/vue.d.ts +404 -49
- package/types/words.d.ts +151 -0
|
@@ -28,6 +28,10 @@ declare const HelixHeadlessSlider_base: (new (...args: any[]) => import("@fusion
|
|
|
28
28
|
* `min-steps-between-thumbs` keeps them that many steps apart, and the readout
|
|
29
29
|
* joins the two as `range-text` writes them.
|
|
30
30
|
*
|
|
31
|
+
* A press on a range's track takes the nearest thumb to the point pressed,
|
|
32
|
+
* and moving the pointer drags it, as React Aria's and Web Awesome's sliders
|
|
33
|
+
* do. The thumb takes focus, and `change` is sent as the press ends.
|
|
34
|
+
*
|
|
31
35
|
* `origin` sets where a single thumb's fill starts: at `min`, at the middle of
|
|
32
36
|
* the track, or at `max`. A balance or an offset from zero fills from the
|
|
33
37
|
* middle, as Zag's slider `origin` does. The fill's ends are published as
|
|
@@ -52,7 +56,7 @@ declare const HelixHeadlessSlider_base: (new (...args: any[]) => import("@fusion
|
|
|
52
56
|
* |---|---|
|
|
53
57
|
* | Right, Up | Moves the thumb up by `step`. |
|
|
54
58
|
* | Left, Down | Moves the thumb down by `step`. |
|
|
55
|
-
* | Page Up, Page Down | Moves the thumb by
|
|
59
|
+
* | Page Up, Page Down | Moves the thumb by `page-step`, or by the platform's larger step where it is not set. |
|
|
56
60
|
* | Home, End | Moves the thumb to its lowest or highest value. |
|
|
57
61
|
*
|
|
58
62
|
* Under `readonly` none of these move the thumb, and Tab still reaches it.
|
|
@@ -97,6 +101,8 @@ declare const HelixHeadlessSlider_base: (new (...args: any[]) => import("@fusion
|
|
|
97
101
|
* @cssproperty --slider-marker-at - Where one marker sits, as a percentage of the track.
|
|
98
102
|
* @cssproperty --slider-to - Where the filled span ends, as a fraction of the track, 0 to 1: a range's upper thumb, or the farther of a single thumb and its `origin`.
|
|
99
103
|
*
|
|
104
|
+
* @cssstate dragging - A thumb is being dragged, by its thumb or from a press on the track.
|
|
105
|
+
* @cssstate focus-visible - A thumb has focus that the keyboard brought, where the browser draws a focus ring.
|
|
100
106
|
* @cssstate invalid - The slider has an error to show, from the browser's validation or from your `feedback`.
|
|
101
107
|
*/
|
|
102
108
|
export declare class HelixHeadlessSlider extends HelixHeadlessSlider_base {
|
|
@@ -193,6 +199,12 @@ export declare class HelixHeadlessSlider extends HelixHeadlessSlider_base {
|
|
|
193
199
|
* between its thumbs and ignores it.
|
|
194
200
|
*/
|
|
195
201
|
origin?: 'center' | 'end' | 'start';
|
|
202
|
+
/**
|
|
203
|
+
* How far Page Up and Page Down move a thumb, in the slider's own units, as
|
|
204
|
+
* React Aria's `pageSize` and Zag's `largeStep` do. Absent leaves the keys
|
|
205
|
+
* to the platform, which moves a tenth of the track.
|
|
206
|
+
*/
|
|
207
|
+
pageStep?: number;
|
|
196
208
|
/**
|
|
197
209
|
* Adds a second thumb, so the value is a pair.
|
|
198
210
|
*
|
|
@@ -298,13 +310,52 @@ export declare class HelixHeadlessSlider extends HelixHeadlessSlider_base {
|
|
|
298
310
|
/** Where one value sits along the track, as a fraction of it. */
|
|
299
311
|
private along;
|
|
300
312
|
private readonly handleInput;
|
|
313
|
+
/** Whether a pointer is dragging a thumb, which `:state(dragging)` says. */
|
|
314
|
+
private dragging;
|
|
315
|
+
/** Ends a drag when its pointer lifts, wherever it lifts. */
|
|
316
|
+
private readonly endDrag;
|
|
317
|
+
private startDrag;
|
|
318
|
+
/**
|
|
319
|
+
* Half a thumb, which is where the thumb's center stops short of each end
|
|
320
|
+
* of its input. Measured off the thumb's length as the stylesheet sets it,
|
|
321
|
+
* or a platform thumb's 16 pixels where none does.
|
|
322
|
+
*/
|
|
323
|
+
private thumbInset;
|
|
324
|
+
/**
|
|
325
|
+
* The value under a pointer, along a thumb's input, less `inset` at each
|
|
326
|
+
* end. Upward on a vertical track, and from the right on a right-to-left
|
|
327
|
+
* page.
|
|
328
|
+
*/
|
|
329
|
+
private valueAt;
|
|
330
|
+
/** Moves one of a range's thumbs, kept between its neighbors, and says whether it moved. */
|
|
331
|
+
private moveThumb;
|
|
332
|
+
/**
|
|
333
|
+
* A press on the field: on a thumb, the start of the platform's own drag,
|
|
334
|
+
* and on a range's track, the nearest thumb taken to the point and dragged
|
|
335
|
+
* from there.
|
|
336
|
+
*
|
|
337
|
+
* **A range's inputs take presses on their thumbs only**, being stacked on
|
|
338
|
+
* one track, so a press beside a thumb reached no input at all and did
|
|
339
|
+
* nothing. Measured before this: a press at 90 on a range holding 20 and 60
|
|
340
|
+
* left it at 20 and 60. React Aria's and Web Awesome's sliders move the
|
|
341
|
+
* nearest thumb there.
|
|
342
|
+
*/
|
|
343
|
+
private readonly handlePress;
|
|
344
|
+
disconnectedCallback(): void;
|
|
301
345
|
/** Relayed, because `change` does not cross a shadow boundary on its own. */
|
|
302
346
|
private readonly handleChange;
|
|
303
347
|
/**
|
|
304
|
-
* Keeps a read-only thumb still under the keys that move it
|
|
305
|
-
*
|
|
348
|
+
* Keeps a read-only thumb still under the keys that move it, and moves a
|
|
349
|
+
* thumb by `page-step` under Page Up and Page Down. Tab and every other key
|
|
350
|
+
* pass, so focus moves on.
|
|
306
351
|
*/
|
|
307
352
|
private readonly handleKeydown;
|
|
353
|
+
/**
|
|
354
|
+
* Says whether the keyboard brought focus to a thumb, as
|
|
355
|
+
* `:state(focus-visible)` on the host, where a stylesheet outside can reach
|
|
356
|
+
* the whole control: a ring around the track, or the readout in bold.
|
|
357
|
+
*/
|
|
358
|
+
private readonly handleFocusChange;
|
|
308
359
|
/**
|
|
309
360
|
* Reports the value: one entry, or for a named range one entry per thumb.
|
|
310
361
|
* The joined pair is the state the browser restores.
|
|
@@ -4,9 +4,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { AriaForwardController, customElement, formatNumber, formatWords, HelixFormElement, MixinControllerValue, MixinField, reportValues, restoreValue, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, customElement, formatNumber, formatWords, HelixFormElement, isRtl, MixinControllerValue, MixinField, reportValues, restoreValue, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
|
-
import { property } from 'lit/decorators.js';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
const SLIDER_KEYS = new Set([
|
|
11
11
|
'ArrowDown',
|
|
12
12
|
'ArrowLeft',
|
|
@@ -63,6 +63,54 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
63
63
|
this.requestValue(next);
|
|
64
64
|
restoreValue(control, this.value);
|
|
65
65
|
};
|
|
66
|
+
this.dragging = false;
|
|
67
|
+
this.endDrag = () => {
|
|
68
|
+
this.dragging = false;
|
|
69
|
+
window.removeEventListener('pointerup', this.endDrag);
|
|
70
|
+
window.removeEventListener('pointercancel', this.endDrag);
|
|
71
|
+
};
|
|
72
|
+
this.handlePress = (event) => {
|
|
73
|
+
if (this.isDisabled || this.readOnly || event.button !== 0) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const inputs = [...this.renderRoot.querySelectorAll('input[part="control"]')];
|
|
77
|
+
if (inputs.includes(event.composedPath()[0])) {
|
|
78
|
+
this.startDrag();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const first = inputs[0];
|
|
82
|
+
const field = event.currentTarget;
|
|
83
|
+
if (!this.range || !first) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const inset = this.thumbInset(field);
|
|
87
|
+
const wanted = this.valueAt(event, first, inset);
|
|
88
|
+
let at = 0;
|
|
89
|
+
this.points.forEach((one, index) => {
|
|
90
|
+
const distance = Math.abs(one - wanted);
|
|
91
|
+
const best = Math.abs((this.points[at] ?? 0) - wanted);
|
|
92
|
+
if (distance < best || (distance === best && wanted > one)) {
|
|
93
|
+
at = index;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
this.moveThumb(at, wanted);
|
|
98
|
+
inputs[at]?.focus({ focusVisible: false, preventScroll: true });
|
|
99
|
+
this.startDrag();
|
|
100
|
+
field.setPointerCapture?.(event.pointerId);
|
|
101
|
+
const move = (next) => {
|
|
102
|
+
this.moveThumb(at, this.valueAt(next, first, inset));
|
|
103
|
+
};
|
|
104
|
+
const end = () => {
|
|
105
|
+
field.removeEventListener('pointermove', move);
|
|
106
|
+
field.removeEventListener('pointerup', end);
|
|
107
|
+
field.removeEventListener('pointercancel', end);
|
|
108
|
+
this.announceChange();
|
|
109
|
+
};
|
|
110
|
+
field.addEventListener('pointermove', move);
|
|
111
|
+
field.addEventListener('pointerup', end);
|
|
112
|
+
field.addEventListener('pointercancel', end);
|
|
113
|
+
};
|
|
66
114
|
this.handleChange = (event) => {
|
|
67
115
|
event.stopPropagation();
|
|
68
116
|
if (!this.readOnly) {
|
|
@@ -72,7 +120,31 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
72
120
|
this.handleKeydown = (event) => {
|
|
73
121
|
if (this.readOnly && SLIDER_KEYS.has(event.key)) {
|
|
74
122
|
event.preventDefault();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const by = this.pageStep;
|
|
126
|
+
if ((event.key !== 'PageUp' && event.key !== 'PageDown') || !by || this.isDisabled) {
|
|
127
|
+
return;
|
|
75
128
|
}
|
|
129
|
+
event.preventDefault();
|
|
130
|
+
const sign = event.key === 'PageUp' ? 1 : -1;
|
|
131
|
+
if (this.range) {
|
|
132
|
+
const at = Number(event.target.dataset.index ?? 0);
|
|
133
|
+
if (!this.moveThumb(at, this.settled((this.points[at] ?? this.min) + sign * by))) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const next = String(this.settled(Number(this.value) + sign * by));
|
|
139
|
+
if (next === this.value || !this.requestValue(next)) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
this.announceChange();
|
|
144
|
+
};
|
|
145
|
+
this.handleFocusChange = (event) => {
|
|
146
|
+
const input = event.target;
|
|
147
|
+
this.toggleState('focus-visible', event.type === 'focusin' && input.matches?.(':focus-visible') === true);
|
|
76
148
|
};
|
|
77
149
|
this.valueText = '';
|
|
78
150
|
this.handleThumbSlot = () => {
|
|
@@ -132,6 +204,46 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
132
204
|
}
|
|
133
205
|
return Math.min(1, Math.max(0, (at - this.min) / span));
|
|
134
206
|
}
|
|
207
|
+
startDrag() {
|
|
208
|
+
this.dragging = true;
|
|
209
|
+
window.addEventListener('pointerup', this.endDrag);
|
|
210
|
+
window.addEventListener('pointercancel', this.endDrag);
|
|
211
|
+
}
|
|
212
|
+
thumbInset(field) {
|
|
213
|
+
const probe = document.createElement('span');
|
|
214
|
+
probe.style.cssText =
|
|
215
|
+
'position:absolute;visibility:hidden;inline-size:var(--_slider-thumb-length,16px)';
|
|
216
|
+
field.append(probe);
|
|
217
|
+
const length = probe.getBoundingClientRect().width;
|
|
218
|
+
probe.remove();
|
|
219
|
+
return length / 2;
|
|
220
|
+
}
|
|
221
|
+
valueAt(event, input, inset) {
|
|
222
|
+
const box = input.getBoundingClientRect();
|
|
223
|
+
const vertical = this.orientation === 'vertical';
|
|
224
|
+
const length = vertical ? box.height : box.width;
|
|
225
|
+
const from = vertical
|
|
226
|
+
? box.bottom - event.clientY
|
|
227
|
+
: isRtl(this)
|
|
228
|
+
? box.right - event.clientX
|
|
229
|
+
: event.clientX - box.left;
|
|
230
|
+
const fraction = Math.min(1, Math.max(0, (from - inset) / Math.max(1, length - inset * 2)));
|
|
231
|
+
return this.settled(this.min + fraction * (this.max - this.min));
|
|
232
|
+
}
|
|
233
|
+
moveThumb(at, wanted) {
|
|
234
|
+
const points = this.points;
|
|
235
|
+
const gap = this.gap;
|
|
236
|
+
const low = at > 0 ? (points[at - 1] ?? this.min) + gap : Number.NEGATIVE_INFINITY;
|
|
237
|
+
const high = at < points.length - 1 ? (points[at + 1] ?? this.max) - gap : Number.POSITIVE_INFINITY;
|
|
238
|
+
const next = points
|
|
239
|
+
.map((one, index) => (index === at ? Math.min(high, Math.max(low, wanted)) : one))
|
|
240
|
+
.join(',');
|
|
241
|
+
return next !== points.join(',') && this.requestValue(next);
|
|
242
|
+
}
|
|
243
|
+
disconnectedCallback() {
|
|
244
|
+
super.disconnectedCallback();
|
|
245
|
+
this.endDrag();
|
|
246
|
+
}
|
|
135
247
|
sync() {
|
|
136
248
|
if (this.range && this.name) {
|
|
137
249
|
reportValues(this.internals, this.name, this.points.map(String));
|
|
@@ -168,6 +280,7 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
168
280
|
}
|
|
169
281
|
willUpdate(changed) {
|
|
170
282
|
super.willUpdate(changed);
|
|
283
|
+
this.toggleState('dragging', this.dragging);
|
|
171
284
|
if (!this.hasUpdated && this.value === '') {
|
|
172
285
|
this.defaultValue = this.range
|
|
173
286
|
? `${this.min},${this.max}`
|
|
@@ -306,6 +419,9 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
306
419
|
: nothing}
|
|
307
420
|
<div
|
|
308
421
|
part="field"
|
|
422
|
+
@focusin=${this.handleFocusChange}
|
|
423
|
+
@focusout=${this.handleFocusChange}
|
|
424
|
+
@pointerdown=${this.handlePress}
|
|
309
425
|
data-markers=${this.markerValues.length > 0 ? '' : nothing}
|
|
310
426
|
data-range=${this.range ? '' : nothing}
|
|
311
427
|
data-thumbs=${this.thumbContent ? '' : nothing}
|
|
@@ -405,6 +521,9 @@ __decorate([
|
|
|
405
521
|
__decorate([
|
|
406
522
|
property({ reflect: true })
|
|
407
523
|
], HelixHeadlessSlider.prototype, "origin", void 0);
|
|
524
|
+
__decorate([
|
|
525
|
+
property({ attribute: 'page-step', type: Number })
|
|
526
|
+
], HelixHeadlessSlider.prototype, "pageStep", void 0);
|
|
408
527
|
__decorate([
|
|
409
528
|
property({ reflect: true, type: Boolean })
|
|
410
529
|
], HelixHeadlessSlider.prototype, "range", void 0);
|
|
@@ -428,6 +547,9 @@ __decorate([
|
|
|
428
547
|
__decorate([
|
|
429
548
|
property({ type: Boolean })
|
|
430
549
|
], HelixHeadlessSlider.prototype, "tooltip", void 0);
|
|
550
|
+
__decorate([
|
|
551
|
+
state()
|
|
552
|
+
], HelixHeadlessSlider.prototype, "dragging", void 0);
|
|
431
553
|
__decorate([
|
|
432
554
|
property({ attribute: 'value-text' })
|
|
433
555
|
], HelixHeadlessSlider.prototype, "valueText", void 0);
|
|
@@ -123,6 +123,16 @@ export declare class HelixHeadlessSplitter extends HelixElement {
|
|
|
123
123
|
* The name of the handle where two dividers cross, read by screen readers.
|
|
124
124
|
*/
|
|
125
125
|
cornerLabel: string;
|
|
126
|
+
/**
|
|
127
|
+
* What the handle where two dividers cross says it is set to, both dividers
|
|
128
|
+
* at once: `{across}` is where the divider that runs down stands, and
|
|
129
|
+
* `{down}` where the one that runs across stands, each a percentage.
|
|
130
|
+
*
|
|
131
|
+
* **Both, because the handle moves both**, and a number names one: the
|
|
132
|
+
* handle's `aria-valuenow` is the divider its `aria-orientation` names, and
|
|
133
|
+
* these words are the whole of where it is.
|
|
134
|
+
*/
|
|
135
|
+
cornerValueLabel: string;
|
|
126
136
|
/**
|
|
127
137
|
* Which pane starts shut, when `collapsed` is not set.
|
|
128
138
|
*/
|
|
@@ -410,6 +420,11 @@ export declare class HelixHeadlessSplitter extends HelixElement {
|
|
|
410
420
|
* a splitter told `horizontal` has been given its answer already.
|
|
411
421
|
*/
|
|
412
422
|
private watchWidth;
|
|
423
|
+
/**
|
|
424
|
+
* Where a crossing stands in words: this splitter's divider at `own` and the
|
|
425
|
+
* nested one's at `theirs`, as `corner-value-label` puts them.
|
|
426
|
+
*/
|
|
427
|
+
private cornerWords;
|
|
413
428
|
/**
|
|
414
429
|
* Whether the panes are stacked. Public because a nested splitter compares
|
|
415
430
|
* its axis with the one it is in.
|
|
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
7
|
var HelixHeadlessSplitter_1;
|
|
8
|
-
import { AriaForwardController, capturePointer, closestInstance, commaList, customElement, formatPercent, HelixBeforeCollapseEvent, HelixBeforeResizeEvent, HelixCollapseEvent, HelixElement, HelixResizedEvent, inlineStep, isRtl, requestChange, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { AriaForwardController, capturePointer, closestInstance, commaList, customElement, formatPercent, formatWords, HelixBeforeCollapseEvent, HelixBeforeResizeEvent, HelixCollapseEvent, HelixElement, HelixResizedEvent, inlineStep, isRtl, requestChange, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
11
11
|
import { snapTo } from '../snap.js';
|
|
@@ -168,6 +168,15 @@ let HelixHeadlessSplitter = class HelixHeadlessSplitter extends HelixElement {
|
|
|
168
168
|
});
|
|
169
169
|
this.sizes.observe(this);
|
|
170
170
|
}
|
|
171
|
+
cornerWords(own, theirs) {
|
|
172
|
+
const locale = wordsLocale(this);
|
|
173
|
+
const mine = formatPercent(Math.round(own) / 100, locale);
|
|
174
|
+
const nested = formatPercent(Math.round(theirs) / 100, locale);
|
|
175
|
+
return formatWords(this, 'cornerValueLabel', {
|
|
176
|
+
across: this.vertical ? nested : mine,
|
|
177
|
+
down: this.vertical ? mine : nested,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
171
180
|
get vertical() {
|
|
172
181
|
return this.orientation === 'vertical' || (this.orientation === 'auto' && this.stacked);
|
|
173
182
|
}
|
|
@@ -269,6 +278,7 @@ let HelixHeadlessSplitter = class HelixHeadlessSplitter extends HelixElement {
|
|
|
269
278
|
this.collapsedLabel = 'Collapsed';
|
|
270
279
|
this.collapsedSize = 0;
|
|
271
280
|
this.cornerLabel = 'Resize both';
|
|
281
|
+
this.cornerValueLabel = '{across} across, {down} down';
|
|
272
282
|
this.defaultPosition = 50;
|
|
273
283
|
this.label = 'Resize panes';
|
|
274
284
|
this.aria = new AriaForwardController(this, {
|
|
@@ -633,7 +643,7 @@ let HelixHeadlessSplitter = class HelixHeadlessSplitter extends HelixElement {
|
|
|
633
643
|
aria-valuemax="100"
|
|
634
644
|
aria-valuemin="0"
|
|
635
645
|
aria-valuenow=${Math.round(crossing.at)}
|
|
636
|
-
aria-valuetext=${
|
|
646
|
+
aria-valuetext=${this.cornerWords(position, crossing.at)}
|
|
637
647
|
data-cross=${index}
|
|
638
648
|
style="--splitter-corner-at: ${crossing.at}%; --splitter-position: ${position}%"
|
|
639
649
|
@dblclick=${this.handleCornerDoubleClick}
|
|
@@ -659,6 +669,10 @@ __decorate([
|
|
|
659
669
|
words,
|
|
660
670
|
property({ attribute: 'corner-label' })
|
|
661
671
|
], HelixHeadlessSplitter.prototype, "cornerLabel", void 0);
|
|
672
|
+
__decorate([
|
|
673
|
+
words,
|
|
674
|
+
property({ attribute: 'corner-value-label' })
|
|
675
|
+
], HelixHeadlessSplitter.prototype, "cornerValueLabel", void 0);
|
|
662
676
|
__decorate([
|
|
663
677
|
property({ attribute: 'default-collapsed' })
|
|
664
678
|
], HelixHeadlessSplitter.prototype, "defaultCollapsed", void 0);
|
|
@@ -5,7 +5,7 @@ import type { HelixHeadlessTableColumn } from './column/table-column.js';
|
|
|
5
5
|
* A column the table draws before the consumer's cells: the grips of a
|
|
6
6
|
* `reorderable` table, and the checkboxes of a `selectable` one.
|
|
7
7
|
*/
|
|
8
|
-
export type LeadColumn = 'grip' | 'select';
|
|
8
|
+
export type LeadColumn = 'expand' | 'grip' | 'select';
|
|
9
9
|
/** A place in the grid with the element that holds focus there. */
|
|
10
10
|
export interface GridCell extends GridPlace {
|
|
11
11
|
cell: HTMLElement;
|
|
@@ -36,6 +36,12 @@ export interface TableGridOptions {
|
|
|
36
36
|
selectRange: (to: number, from: number) => boolean;
|
|
37
37
|
/** Selects every row for Control or Command with A, and says whether it did. */
|
|
38
38
|
selectAll: () => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Focus moved to another row by a key with no Shift, row `to`, and
|
|
41
|
+
* `control` says whether Control or Command was held. A table whose moves
|
|
42
|
+
* carry the selection selects the row there.
|
|
43
|
+
*/
|
|
44
|
+
arrive?: (to: number, control: boolean) => void;
|
|
39
45
|
/** Whether the table is a grid now. */
|
|
40
46
|
on: () => boolean;
|
|
41
47
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { deepActiveElement, deepContains, focusableElements, gridMove, inlineStep, pageRows, scrollingAncestor, scrollRoom, selectsAll, Typeahead, typesCharacter, whenHydrated, } from '@fusion.dev/helix-core';
|
|
2
|
-
const HEADS = {
|
|
2
|
+
const HEADS = {
|
|
3
|
+
expand: 'expand-head',
|
|
4
|
+
grip: 'grip-head',
|
|
5
|
+
select: 'select-all',
|
|
6
|
+
};
|
|
3
7
|
const shown = (element) => {
|
|
4
8
|
const hidden = element.getAttribute('hidden');
|
|
5
9
|
return hidden === null || hidden === 'until-found';
|
|
@@ -83,6 +87,9 @@ export class TableGridController {
|
|
|
83
87
|
to.row >= 0) {
|
|
84
88
|
this.options.selectRange(to.row, place.row);
|
|
85
89
|
}
|
|
90
|
+
else if (!event.shiftKey && to.row >= 0 && to.row !== place.row) {
|
|
91
|
+
this.options.arrive?.(to.row, event.ctrlKey || event.metaKey);
|
|
92
|
+
}
|
|
86
93
|
if (to.row !== place.row || to.column !== place.column) {
|
|
87
94
|
this.moveTo(to);
|
|
88
95
|
}
|
|
@@ -246,6 +253,7 @@ export class TableGridController {
|
|
|
246
253
|
row.gridCell = on;
|
|
247
254
|
row.gripStop = here && lead[at.column] === 'grip';
|
|
248
255
|
row.selectStop = here && lead[at.column] === 'select';
|
|
256
|
+
row.expandStop = here && lead[at.column] === 'expand';
|
|
249
257
|
});
|
|
250
258
|
}
|
|
251
259
|
for (const [place, cell] of this.cellsOf(line).entries()) {
|
|
@@ -53,6 +53,27 @@ declare const HelixHeadlessTable_base: (new (...args: any[]) => import("@fusion.
|
|
|
53
53
|
* Tab reaches each checkbox, and Space ticks it. To act on one row, put a
|
|
54
54
|
* link or a button in a cell, or give the row an `href`.
|
|
55
55
|
*
|
|
56
|
+
* `selection-mode="single"` selects one row at a time: ticking a row lets go
|
|
57
|
+
* of the one before, the heading holds no checkbox, and Shift and Control with
|
|
58
|
+
* A take in nothing more. `selection-behavior="replace"` makes a press on a
|
|
59
|
+
* row, anywhere but its own controls, select it alone, as a file list does,
|
|
60
|
+
* and in a `grid` table the arrow keys carry the selection with them. Control
|
|
61
|
+
* or Command with a press adds or takes out the row, and with an arrow moves
|
|
62
|
+
* without selecting. Shift with a press takes in the rows from the one last
|
|
63
|
+
* chosen.
|
|
64
|
+
*
|
|
65
|
+
* **Nesting rows.** A row with `parent` sits under the row of that key, as
|
|
66
|
+
* a file sits in a folder. The table then draws a column of disclosure
|
|
67
|
+
* buttons, under a heading named by `expand-column-label`: a row with rows
|
|
68
|
+
* under it has a button named by `expand-row-label` ("Expand {row}") that
|
|
69
|
+
* says with `aria-expanded` whether it is open, and the rows under a shut row
|
|
70
|
+
* are not drawn. `expanded` holds the open rows' keys, comma separated, and
|
|
71
|
+
* `default-expanded` is where it starts. A press asks with
|
|
72
|
+
* `helix-before-toggle` and tells with `helix-toggle`, both from the row, so
|
|
73
|
+
* you can refuse it or load the rows on the way. A sort orders the rows
|
|
74
|
+
* under each row among themselves, and in a `grid` table the table is a
|
|
75
|
+
* `treegrid` whose rows say their depth.
|
|
76
|
+
*
|
|
56
77
|
* **Moving rows.** `reorderable` adds a column of grips before the checkboxes,
|
|
57
78
|
* under a heading named by `row-grips-label`.
|
|
58
79
|
*
|
|
@@ -89,13 +110,20 @@ declare const HelixHeadlessTable_base: (new (...args: any[]) => import("@fusion.
|
|
|
89
110
|
* | Enter on a cell of a row with `href` | Follows the row's link. |
|
|
90
111
|
* | Enter, Space on a sortable heading | Sorts by the column. |
|
|
91
112
|
* | Space on a row, Enter or Space on its checkbox cell | Selects the row, in a `selectable` table. |
|
|
113
|
+
* | Enter or Space on a disclosure cell | Shows or hides the rows under the row, in a table whose rows nest. |
|
|
92
114
|
* | Enter or Space on a grip cell | Picks the row up, or drops it, in a `reorderable` table. Up, Down, Home, and End move a row picked up, and Escape puts it back. |
|
|
115
|
+
* | Down, Up, Page Down, Page Up under `selection-behavior="replace"` | Selects the row it moves to alone. Control or Command with the key moves without selecting. |
|
|
93
116
|
* | Shift with Down or Up | Selects the rows it moves across, in a `selectable` table. |
|
|
94
117
|
* | Shift with Space | Selects every row from the one last chosen, in a `selectable` table. |
|
|
95
118
|
* | Control or Command with A | Selects every row, in a `selectable` table. |
|
|
96
119
|
* | A letter or a word | Moves to the next row whose first cell starts with what you typed. |
|
|
97
120
|
* | Tab | Leaves the table. |
|
|
98
121
|
*
|
|
122
|
+
* **Loading more.** `load-more` sends `helix-load-more` once the end of the
|
|
123
|
+
* table comes within a screen of view, in the box that scrolls it or the
|
|
124
|
+
* page, once for each number of rows: add rows and it can send it again. Set
|
|
125
|
+
* `loading` while you fetch, and the table asks for nothing until it clears.
|
|
126
|
+
*
|
|
99
127
|
* When the rows did not load, set `error-text`, such as "The orders did not
|
|
100
128
|
* load", and put a button that tries again in the `error` slot. The line shows
|
|
101
129
|
* after the rows in place of the empty and loading lines, the live region says
|
|
@@ -119,11 +147,14 @@ declare const HelixHeadlessTable_base: (new (...args: any[]) => import("@fusion.
|
|
|
119
147
|
* @slot loading - Content shown while the rows load, such as a spinner. Falls back to `loading-text`, which the live region still says.
|
|
120
148
|
*
|
|
121
149
|
* @event {HelixBeforeReorderEvent} helix-before-reorder - Fired when a row is dropped at a new place, in a `reorderable` table, with every row's key in the new order. Cancelable: cancel it to keep the row where it is.
|
|
150
|
+
* @event {HelixBeforeToggleEvent} helix-before-toggle - Fired from a row when its disclosure button is pressed, with whether it would be open in `detail.checked`. Cancelable: cancel it to keep the row as it is.
|
|
151
|
+
* @event {HelixLoadMoreEvent} helix-load-more - Fired, in a table with `load-more`, when the end of the table comes within a screen of view, once for each number of rows.
|
|
122
152
|
* @event {HelixReorderEvent} helix-reorder - Fired after `helix-before-reorder` when nobody canceled it. Move your rows to the order in `keys`. Cancelable: cancel it to say the move did not happen.
|
|
123
153
|
* @event {HelixSelectEvent} helix-select - The selection is about to change. The event carries every selected key, separated by commas. Cancelable: cancel it to keep the current selection.
|
|
124
154
|
* @event {HelixSelectedEvent} helix-selected - Fired after the selection changes, with every selected key, separated by commas.
|
|
125
155
|
* @event {HelixSortEvent} helix-sort - A different sort order was requested, as `field:direction`, or empty for the order the rows were written in. Cancelable: cancel it to keep the current order.
|
|
126
156
|
* @event {HelixSortedEvent} helix-sorted - Fired after the sort order changes, with the new order.
|
|
157
|
+
* @event {HelixToggleEvent} helix-toggle - Fired from a row once the rows under it are shown or hidden, with whether it is open in `detail.checked`.
|
|
127
158
|
*
|
|
128
159
|
* @csspart checkbox - The checkbox in the heading that selects every row, in a `selectable` table.
|
|
129
160
|
* @csspart checkbox-box - The box drawn for the checkbox's input.
|
|
@@ -139,6 +170,7 @@ declare const HelixHeadlessTable_base: (new (...args: any[]) => import("@fusion.
|
|
|
139
170
|
* @csspart drag-preview - A copy of the row a pointer drags by its grip, which follows the pointer in the top layer until the row is let go, in a `reorderable` table. Hidden from assistive technology.
|
|
140
171
|
* @csspart empty - The text shown when there are no rows.
|
|
141
172
|
* @csspart error - The line shown after the rows while `error-text` is set, in place of the empty and loading text.
|
|
173
|
+
* @csspart expand-head - The heading over the disclosure buttons, in a table whose rows nest.
|
|
142
174
|
* @csspart grid-end - An empty box after the rows, in a `grid` table. Tab focuses it on its way out of the table, so the key leaves from the end.
|
|
143
175
|
* @csspart grid-start - An empty box before the rows, in a `grid` table. Shift with Tab focuses it on its way out of the table.
|
|
144
176
|
* @csspart grip-head - The heading over the row grips, in a `reorderable` table.
|
|
@@ -205,6 +237,11 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
205
237
|
* declared in it has nowhere else to be sized from.
|
|
206
238
|
*/
|
|
207
239
|
columns: string;
|
|
240
|
+
/**
|
|
241
|
+
* The keys of the rows whose rows are shown at the start, separated by
|
|
242
|
+
* commas. Used until `expanded` is set.
|
|
243
|
+
*/
|
|
244
|
+
defaultExpanded: string;
|
|
208
245
|
/**
|
|
209
246
|
* The keys selected at the start, separated by commas. Used until `selected`
|
|
210
247
|
* is set.
|
|
@@ -234,6 +271,17 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
234
271
|
* says it.
|
|
235
272
|
*/
|
|
236
273
|
errorText: string;
|
|
274
|
+
/** The name of the heading over the disclosure buttons. */
|
|
275
|
+
expandColumnLabel: string;
|
|
276
|
+
/**
|
|
277
|
+
* The keys of the rows whose rows are shown, separated by commas, in a table
|
|
278
|
+
* whose rows nest. A press on a row's disclosure button asks first, and
|
|
279
|
+
* `default-expanded` seeds what this element then owns.
|
|
280
|
+
*/
|
|
281
|
+
get expanded(): string;
|
|
282
|
+
set expanded(next: string);
|
|
283
|
+
/** The name of a row's disclosure button. `{row}` is the words of the row's first cell. */
|
|
284
|
+
expandRowLabel: string;
|
|
237
285
|
/**
|
|
238
286
|
* The table's name where neither `label` nor an `aria-label` or
|
|
239
287
|
* `aria-labelledby` on the host gives it one.
|
|
@@ -256,6 +304,11 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
256
304
|
* wins over this.
|
|
257
305
|
*/
|
|
258
306
|
label: string;
|
|
307
|
+
/**
|
|
308
|
+
* Sends `helix-load-more` once the end of the table comes within a screen
|
|
309
|
+
* of view, so you can add the next rows as a reader nears them.
|
|
310
|
+
*/
|
|
311
|
+
loadMore: boolean;
|
|
259
312
|
/**
|
|
260
313
|
* What screen readers hear each time a key moves a held row, in a
|
|
261
314
|
* `reorderable` table. `{name}` is the row's words, `{position}` the place it
|
|
@@ -286,6 +339,11 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
286
339
|
selectable: boolean;
|
|
287
340
|
/** The name of the checkbox in the heading that selects every row. */
|
|
288
341
|
selectAllLabel: string;
|
|
342
|
+
/**
|
|
343
|
+
* The name of the heading over the checkboxes under
|
|
344
|
+
* `selection-mode="single"`, where it holds no checkbox of its own.
|
|
345
|
+
*/
|
|
346
|
+
selectColumnLabel: string;
|
|
289
347
|
/**
|
|
290
348
|
* The keys of the selected rows, separated by commas.
|
|
291
349
|
*
|
|
@@ -302,6 +360,17 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
302
360
|
* {# wybranych} other {# wybranego}}`.
|
|
303
361
|
*/
|
|
304
362
|
selectedLabel: string;
|
|
363
|
+
/**
|
|
364
|
+
* What a press on a row, or in a `grid` table a key that moves to one, does
|
|
365
|
+
* to the rows already selected: `toggle`, the default, leaves them to the
|
|
366
|
+
* checkboxes, and `replace` selects the row alone.
|
|
367
|
+
*/
|
|
368
|
+
selectionBehavior: 'replace' | 'toggle';
|
|
369
|
+
/**
|
|
370
|
+
* How many rows a `selectable` table selects: `multiple`, the default, or
|
|
371
|
+
* `single`, one at a time.
|
|
372
|
+
*/
|
|
373
|
+
selectionMode?: 'multiple' | 'single';
|
|
305
374
|
/**
|
|
306
375
|
* The name of a row's checkbox. `{row}` is replaced with the words of the
|
|
307
376
|
* row's first cell.
|
|
@@ -329,6 +398,16 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
329
398
|
unsortedLabel: string;
|
|
330
399
|
private sortState;
|
|
331
400
|
private selectedState;
|
|
401
|
+
private expandedState;
|
|
402
|
+
/** Whether any row sits under another, which makes the table a tree. */
|
|
403
|
+
private get nests();
|
|
404
|
+
/** The keys of the rows whose rows are shown. */
|
|
405
|
+
private get open();
|
|
406
|
+
/**
|
|
407
|
+
* Shows or hides the rows under the row of `key`, asking first from the row,
|
|
408
|
+
* and saying so from it once it stands.
|
|
409
|
+
*/
|
|
410
|
+
private toggleRow;
|
|
332
411
|
/** Whether the heading names the table, so taking it away clears only this element's own name. */
|
|
333
412
|
private headed;
|
|
334
413
|
/**
|
|
@@ -377,6 +456,15 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
377
456
|
* which is what a framework holding those rows needs.
|
|
378
457
|
*/
|
|
379
458
|
private get arrangedRows();
|
|
459
|
+
/**
|
|
460
|
+
* The rows of a tree in the order they are drawn: each row followed by the
|
|
461
|
+
* rows under it while it is open, at every depth, keeping the order `rows`
|
|
462
|
+
* is in among the rows under one row. A row under a key no row has is drawn
|
|
463
|
+
* at the top.
|
|
464
|
+
*/
|
|
465
|
+
private nested;
|
|
466
|
+
/** How deep each row sits, from 1, and whether rows sit under it, by key. */
|
|
467
|
+
private shape;
|
|
380
468
|
/** The sort last heard, so a render that changed nothing says nothing. */
|
|
381
469
|
private heardSort;
|
|
382
470
|
/**
|
|
@@ -419,6 +507,22 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
419
507
|
* them where each already is. A `disabled` row keeps what it had.
|
|
420
508
|
*/
|
|
421
509
|
private chooseAll;
|
|
510
|
+
/** Whether the table selects one row at a time. */
|
|
511
|
+
private get single();
|
|
512
|
+
/** Whether a press or a move on a row selects it alone. */
|
|
513
|
+
private get replaces();
|
|
514
|
+
/**
|
|
515
|
+
* Selects the drawn row `at` alone, and makes it where a range runs from.
|
|
516
|
+
* Asks nothing where it is already the only one selected.
|
|
517
|
+
*/
|
|
518
|
+
private chooseOnly;
|
|
519
|
+
/**
|
|
520
|
+
* A press on a row under `selection-behavior="replace"`: it selects the row
|
|
521
|
+
* alone, Control or Command adds or takes out the row, and Shift takes in
|
|
522
|
+
* the rows from the one last chosen. A press on the row's own controls, its
|
|
523
|
+
* checkbox or its grip is theirs.
|
|
524
|
+
*/
|
|
525
|
+
private readonly handleRowClick;
|
|
422
526
|
/** The key of the row a Shift range runs from: the one last chosen. */
|
|
423
527
|
private anchorKey;
|
|
424
528
|
/**
|
|
@@ -588,8 +692,26 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
588
692
|
private alignCells;
|
|
589
693
|
protected willUpdate(changed: PropertyValues): void;
|
|
590
694
|
protected updated(changed: PropertyValues): void;
|
|
695
|
+
/** Watches the end of a `load-more` table coming near the box that scrolls it. */
|
|
696
|
+
private endWatcher;
|
|
697
|
+
/** The end being watched, so a new one is watched again. */
|
|
698
|
+
private watchedEnd;
|
|
699
|
+
/** The number of rows `helix-load-more` was last sent for. */
|
|
700
|
+
private askedAt;
|
|
701
|
+
/**
|
|
702
|
+
* Starts or stops watching the end of the table, against the box that
|
|
703
|
+
* scrolls it, or the page, with a screen of room below it, so the next rows
|
|
704
|
+
* are asked for before a reader reaches the last. List does the same.
|
|
705
|
+
*/
|
|
706
|
+
private watchEnd;
|
|
707
|
+
/** Whether the end of the table was in reach when last measured. */
|
|
708
|
+
private endInView;
|
|
709
|
+
/** Sends `helix-load-more`, once for each number of rows, and never while loading. */
|
|
710
|
+
private askForMore;
|
|
591
711
|
/** The heading over the row grips, named by `row-grips-label`. */
|
|
592
712
|
private renderGripHead;
|
|
713
|
+
/** The heading over the disclosure buttons, in a table whose rows nest. */
|
|
714
|
+
private renderExpandHead;
|
|
593
715
|
/** The heading of the checkbox column, which selects every row. */
|
|
594
716
|
private renderSelectAll;
|
|
595
717
|
protected render(): TemplateResult;
|