@fusion.dev/helix-headless 0.171.0 → 0.173.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 +1350 -124
- package/dist/button-group/root/button-group-root.d.ts +30 -0
- package/dist/button-group/root/button-group-root.js +50 -4
- package/dist/canvas/root/canvas-root.d.ts +7 -0
- package/dist/canvas/root/canvas-root.js +7 -0
- package/dist/chat/composer/chat-composer.d.ts +28 -1
- package/dist/chat/composer/chat-composer.js +22 -3
- package/dist/chat/root/chat-root.d.ts +38 -2
- package/dist/chat/root/chat-root.js +38 -1
- package/dist/color-input/root/color-input-root.d.ts +15 -1
- package/dist/color-input/root/color-input-root.js +19 -1
- package/dist/color-picker/root/color-picker-root.d.ts +5 -1
- package/dist/color-picker/root/color-picker-root.js +1 -1
- package/dist/copy-button/root/copy-button-root.d.ts +43 -3
- package/dist/copy-button/root/copy-button-root.js +49 -3
- package/dist/data-table/root/data-table-root.d.ts +5 -1
- package/dist/data-table/root/data-table-root.js +1 -0
- package/dist/dialog/root/dialog-root.js +1 -0
- package/dist/drawer/root/drawer-root.d.ts +4 -0
- package/dist/drawer/root/drawer-root.js +1 -0
- package/dist/element-events.generated.js +3 -0
- package/dist/graph/root/graph-root.d.ts +22 -0
- package/dist/graph/root/graph-root.js +21 -5
- package/dist/lightbox/root/lightbox-root.js +1 -0
- package/dist/list/root/list-root.d.ts +6 -1
- package/dist/list/root/list-root.js +3 -2
- package/dist/listbox/root/listbox-root.d.ts +2 -1
- package/dist/listbox/root/listbox-root.js +3 -2
- package/dist/menu/item/menu-item.d.ts +2 -0
- package/dist/menu/item/menu-item.js +2 -0
- package/dist/menu/root/menu-root.d.ts +73 -12
- package/dist/menu/root/menu-root.js +103 -7
- package/dist/outline/root/outline-root.d.ts +57 -0
- package/dist/outline/root/outline-root.js +98 -29
- package/dist/popover/root/popover-root.d.ts +59 -6
- package/dist/popover/root/popover-root.js +51 -3
- package/dist/select/root/select-root.d.ts +2 -2
- package/dist/select/root/select-root.js +2 -2
- package/dist/speed-dial/root/speed-dial-root.d.ts +14 -0
- package/dist/speed-dial/root/speed-dial-root.js +34 -2
- package/dist/splitter/root/splitter-root.d.ts +25 -2
- package/dist/splitter/root/splitter-root.js +19 -4
- package/dist/tag/group/tag-group.d.ts +8 -1
- package/dist/tag/group/tag-group.js +8 -0
- package/dist/tag/root/tag-root.d.ts +12 -0
- package/dist/tag/root/tag-root.js +12 -0
- package/dist/textarea/root/textarea-root.d.ts +8 -0
- package/dist/textarea/root/textarea-root.js +16 -0
- package/dist/time-picker/root/time-picker-root.d.ts +14 -0
- package/dist/time-picker/root/time-picker-root.js +8 -1
- package/dist/timeline/item/timeline-item.d.ts +5 -1
- package/dist/timeline/item/timeline-item.js +9 -0
- package/dist/timeline/root/timeline-root.d.ts +23 -0
- package/dist/timeline/root/timeline-root.js +17 -3
- package/dist/tooltip/root/tooltip-root.d.ts +10 -0
- package/dist/tooltip/root/tooltip-root.js +14 -2
- package/dist/virtual-list/reorder/index.d.ts +1 -0
- package/dist/virtual-list/reorder/index.js +4 -0
- package/dist/virtual-list/reorder/virtual-list-reorder.d.ts +86 -0
- package/dist/virtual-list/reorder/virtual-list-reorder.js +193 -0
- package/dist/virtual-list/root/virtual-list-root.d.ts +49 -11
- package/dist/virtual-list/root/virtual-list-root.js +10 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +880 -48
- package/types/vue.d.ts +174 -19
- package/types/words.d.ts +22 -0
|
@@ -55,7 +55,7 @@ let HelixHeadlessSplitter = class HelixHeadlessSplitter extends HelixElement {
|
|
|
55
55
|
return this.toPercent(this.min, 0);
|
|
56
56
|
}
|
|
57
57
|
get maxPercent() {
|
|
58
|
-
return this.toPercent(this.max, 100);
|
|
58
|
+
return Math.min(this.toPercent(this.max, 100), 100 - this.toPercent(this.endMin, 0));
|
|
59
59
|
}
|
|
60
60
|
get position() {
|
|
61
61
|
const shut = this.collapsed;
|
|
@@ -141,10 +141,14 @@ let HelixHeadlessSplitter = class HelixHeadlessSplitter extends HelixElement {
|
|
|
141
141
|
}
|
|
142
142
|
shutAt(position) {
|
|
143
143
|
const size = this.sliver;
|
|
144
|
-
if (position <= size) {
|
|
144
|
+
if (position <= size && this.canCollapse('start')) {
|
|
145
145
|
return 'start';
|
|
146
146
|
}
|
|
147
|
-
return position >= 100 - size ? 'end' : undefined;
|
|
147
|
+
return position >= 100 - size && this.canCollapse('end') ? 'end' : undefined;
|
|
148
|
+
}
|
|
149
|
+
canCollapse(pane) {
|
|
150
|
+
const collapsible = this.collapsible ?? 'both';
|
|
151
|
+
return collapsible === 'both' || collapsible === pane;
|
|
148
152
|
}
|
|
149
153
|
clamp(next) {
|
|
150
154
|
const min = Math.max(0, Math.min(100, this.minPercent));
|
|
@@ -250,6 +254,10 @@ let HelixHeadlessSplitter = class HelixHeadlessSplitter extends HelixElement {
|
|
|
250
254
|
}
|
|
251
255
|
toggleCollapse() {
|
|
252
256
|
const shut = this.collapsed;
|
|
257
|
+
const pane = this.collapsible === 'end' ? 'end' : 'start';
|
|
258
|
+
if (!shut && !this.canCollapse(pane)) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
253
261
|
const opened = shut ? undefined : this.position;
|
|
254
262
|
requestChange({
|
|
255
263
|
apply: (value) => {
|
|
@@ -266,7 +274,7 @@ let HelixHeadlessSplitter = class HelixHeadlessSplitter extends HelixElement {
|
|
|
266
274
|
},
|
|
267
275
|
event: (value) => new HelixBeforeCollapseEvent({ value }),
|
|
268
276
|
host: this,
|
|
269
|
-
next: shut ? '' :
|
|
277
|
+
next: shut ? '' : pane,
|
|
270
278
|
});
|
|
271
279
|
}
|
|
272
280
|
reset() {
|
|
@@ -280,6 +288,7 @@ let HelixHeadlessSplitter = class HelixHeadlessSplitter extends HelixElement {
|
|
|
280
288
|
this.cornerLabel = 'Resize both';
|
|
281
289
|
this.cornerValueLabel = '{across} across, {down} down';
|
|
282
290
|
this.defaultPosition = 50;
|
|
291
|
+
this.endMin = 0;
|
|
283
292
|
this.label = 'Resize panes';
|
|
284
293
|
this.aria = new AriaForwardController(this, {
|
|
285
294
|
label: true,
|
|
@@ -665,6 +674,9 @@ __decorate([
|
|
|
665
674
|
__decorate([
|
|
666
675
|
property({ attribute: 'collapsed-size', type: Number })
|
|
667
676
|
], HelixHeadlessSplitter.prototype, "collapsedSize", void 0);
|
|
677
|
+
__decorate([
|
|
678
|
+
property()
|
|
679
|
+
], HelixHeadlessSplitter.prototype, "collapsible", void 0);
|
|
668
680
|
__decorate([
|
|
669
681
|
words,
|
|
670
682
|
property({ attribute: 'corner-label' })
|
|
@@ -679,6 +691,9 @@ __decorate([
|
|
|
679
691
|
__decorate([
|
|
680
692
|
property({ attribute: 'default-position', type: Number })
|
|
681
693
|
], HelixHeadlessSplitter.prototype, "defaultPosition", void 0);
|
|
694
|
+
__decorate([
|
|
695
|
+
property({ attribute: 'end-min' })
|
|
696
|
+
], HelixHeadlessSplitter.prototype, "endMin", void 0);
|
|
682
697
|
__decorate([
|
|
683
698
|
words,
|
|
684
699
|
property()
|
|
@@ -22,7 +22,8 @@ export type TagSelectionMode = SelectionMode;
|
|
|
22
22
|
* not disabled. `selection-behavior="replace"` selects the tag a reader
|
|
23
23
|
* presses or moves to alone, as a file list does, and Control or Command
|
|
24
24
|
* with a press or Space adds or takes out one tag. `disallow-empty-selection`
|
|
25
|
-
* keeps one tag selected once one is
|
|
25
|
+
* keeps one tag selected once one is, and Escape lets go of the selection
|
|
26
|
+
* otherwise. `value` holds the
|
|
26
27
|
* selected tags' values, separated by commas, and a change asks with
|
|
27
28
|
* `helix-before-change` and tells with `change`. Enter on a tag with an
|
|
28
29
|
* `href` follows its link. A `disabled` tag is passed over. A selected tag
|
|
@@ -40,6 +41,12 @@ export type TagSelectionMode = SelectionMode;
|
|
|
40
41
|
* Where only disabled tags are left, the first of them holds the tab stop and
|
|
41
42
|
* takes focus, so the reader stays in the set.
|
|
42
43
|
*
|
|
44
|
+
* **Typing new tags.** A field where people type tags and press Enter to add
|
|
45
|
+
* each one is Combobox with `multiple` and `allows-custom-value`, or
|
|
46
|
+
* `creatable` to ask first with `helix-create`. It draws each value as a tag,
|
|
47
|
+
* Backspace on the empty field removes the last one, and the arrow keys move
|
|
48
|
+
* between them.
|
|
49
|
+
*
|
|
43
50
|
* @since 0.1.0
|
|
44
51
|
* @status stable
|
|
45
52
|
* @aliases Tag list, Chip group, Token list
|
|
@@ -46,6 +46,14 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
|
|
|
46
46
|
if (event.ctrlKey || event.metaKey) {
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
|
+
if (event.key === 'Escape') {
|
|
50
|
+
if (this.selectionMode && this.written !== '' && !this.disallowEmptySelection) {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
event.stopPropagation();
|
|
53
|
+
this.ask('');
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
49
57
|
if (MOVES.has(event.key)) {
|
|
50
58
|
const to = this.last;
|
|
51
59
|
if (multiple && event.shiftKey && to && to !== tag) {
|
|
@@ -28,6 +28,12 @@ declare const HelixHeadlessTag_base: (new (...args: any[]) => import("@fusion.de
|
|
|
28
28
|
* selected. Its remove button leaves the tab order there, Delete and Backspace
|
|
29
29
|
* doing its job from the row.
|
|
30
30
|
*
|
|
31
|
+
* **Styling a tag by what is happening to it.** `:state(selected)` while it is
|
|
32
|
+
* selected, `:state(focus-visible)` while its cell shows keyboard focus,
|
|
33
|
+
* `[disabled]` while it is out of reach, and the host's own `:hover` and
|
|
34
|
+
* `:active` under the pointer, as React Aria's `data-hovered` and
|
|
35
|
+
* `data-pressed` are.
|
|
36
|
+
*
|
|
31
37
|
* @since 0.1.0
|
|
32
38
|
* @status stable
|
|
33
39
|
* @category markers
|
|
@@ -47,6 +53,7 @@ declare const HelixHeadlessTag_base: (new (...args: any[]) => import("@fusion.de
|
|
|
47
53
|
* @csspart link - The link around the label, under `href`.
|
|
48
54
|
* @csspart start - The category before the label.
|
|
49
55
|
*
|
|
56
|
+
* @cssstate focus-visible - The tag's cell has keyboard focus that the browser shows, in a `Tag.Group`.
|
|
50
57
|
* @cssstate selected - The tag is selected in a `Tag.Group` that selects.
|
|
51
58
|
*/
|
|
52
59
|
export declare class HelixHeadlessTag extends HelixHeadlessTag_base {
|
|
@@ -131,6 +138,11 @@ export declare class HelixHeadlessTag extends HelixHeadlessTag_base {
|
|
|
131
138
|
*/
|
|
132
139
|
private get removeName();
|
|
133
140
|
protected firstUpdated(changed: PropertyValues): void;
|
|
141
|
+
/**
|
|
142
|
+
* Says whether the cell shows keyboard focus, as `:focus-visible` does, so a
|
|
143
|
+
* sheet outside can draw the ring on the tag by the host.
|
|
144
|
+
*/
|
|
145
|
+
private readonly handleCellFocus;
|
|
134
146
|
protected render(): TemplateResult;
|
|
135
147
|
/** The label, inside a link under `href`, behind a seam a tier can move. */
|
|
136
148
|
protected renderLabel(): TemplateResult;
|
|
@@ -15,6 +15,16 @@ let HelixHeadlessTag = class HelixHeadlessTag extends MixinDismissible(HelixElem
|
|
|
15
15
|
this.dismissTabStop = true;
|
|
16
16
|
this.href = '';
|
|
17
17
|
this.value = '';
|
|
18
|
+
this.handleCellFocus = (event) => {
|
|
19
|
+
const cell = event.currentTarget;
|
|
20
|
+
let shown = false;
|
|
21
|
+
try {
|
|
22
|
+
shown = event.type === 'focus' && cell.matches(':focus-visible');
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
}
|
|
26
|
+
this.toggleState('focus-visible', shown);
|
|
27
|
+
};
|
|
18
28
|
}
|
|
19
29
|
static { this.words = 'helix-tag'; }
|
|
20
30
|
get words() {
|
|
@@ -66,6 +76,8 @@ let HelixHeadlessTag = class HelixHeadlessTag extends MixinDismissible(HelixElem
|
|
|
66
76
|
part="cell"
|
|
67
77
|
role="gridcell"
|
|
68
78
|
tabindex=${this.focusable ? '0' : '-1'}
|
|
79
|
+
@focus=${this.handleCellFocus}
|
|
80
|
+
@blur=${this.handleCellFocus}
|
|
69
81
|
>${content}</span
|
|
70
82
|
>`
|
|
71
83
|
: content;
|
|
@@ -217,6 +217,14 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
|
|
|
217
217
|
* and so does this.
|
|
218
218
|
*/
|
|
219
219
|
setSelectionRange(start: number, end: number, direction?: SelectionDirection): void;
|
|
220
|
+
/**
|
|
221
|
+
* Replaces a range of the text, as the native method does: the selection
|
|
222
|
+
* where `start` and `end` are left out, and `selectMode` says what is
|
|
223
|
+
* selected after. It sends no event, as the native method sends none.
|
|
224
|
+
*/
|
|
225
|
+
setRangeText(replacement: string, start?: number, end?: number, selectMode?: SelectionMode): void;
|
|
226
|
+
/** Selects everything in the field. */
|
|
227
|
+
select(): void;
|
|
220
228
|
/** How many characters the value holds. */
|
|
221
229
|
get textLength(): number;
|
|
222
230
|
/** The control's type, which is always `textarea`. */
|
|
@@ -111,6 +111,22 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
|
|
|
111
111
|
setSelectionRange(start, end, direction) {
|
|
112
112
|
this.control?.setSelectionRange(start, end, direction);
|
|
113
113
|
}
|
|
114
|
+
setRangeText(replacement, start, end, selectMode) {
|
|
115
|
+
const control = this.control;
|
|
116
|
+
if (!control) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (start === undefined || end === undefined) {
|
|
120
|
+
control.setRangeText(replacement);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
control.setRangeText(replacement, start, end, selectMode);
|
|
124
|
+
}
|
|
125
|
+
this.value = control.value;
|
|
126
|
+
}
|
|
127
|
+
select() {
|
|
128
|
+
this.control?.select();
|
|
129
|
+
}
|
|
114
130
|
get textLength() {
|
|
115
131
|
return this.value.length;
|
|
116
132
|
}
|
|
@@ -469,6 +469,20 @@ export declare class HelixHeadlessTimePicker extends HelixHeadlessTimePicker_bas
|
|
|
469
469
|
* The validation message for a time that `step` skips.
|
|
470
470
|
*/
|
|
471
471
|
stepMismatchMessage: string;
|
|
472
|
+
/**
|
|
473
|
+
* What each time in the list shows: a function given the time as `HH:mm`,
|
|
474
|
+
* or `HH:mm:ss` with `seconds`, and the time as the row writes it, that
|
|
475
|
+
* returns text, an element or a Lit template.
|
|
476
|
+
*
|
|
477
|
+
* For the seats left at a time, or a caption under it, as React Aria's
|
|
478
|
+
* `ListBoxItem` takes children. Content with `slot="start"`, `slot="end"`
|
|
479
|
+
* or `slot="description"` goes to that slot of the row, and a description
|
|
480
|
+
* is read after the time. The row is still named by the written time, and
|
|
481
|
+
* typeahead still finds it by that. Return the written time with your
|
|
482
|
+
* content where it should still show. A property only, there being no way
|
|
483
|
+
* to write a function in markup. The wheels draw their own figures.
|
|
484
|
+
*/
|
|
485
|
+
timeContent?: ((time: string, written: string) => unknown) | undefined;
|
|
472
486
|
/**
|
|
473
487
|
* The validation message for a time that `disable-times` bars.
|
|
474
488
|
*/
|
|
@@ -480,6 +480,8 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
480
480
|
${this.times.map((seconds) => {
|
|
481
481
|
const at = this.valueFor(seconds);
|
|
482
482
|
const between = this.between(at);
|
|
483
|
+
const written = writeClock(at, this.clock, this.seconds);
|
|
484
|
+
const drawn = this.timeContent;
|
|
483
485
|
return html `<${this.tag('option')}
|
|
484
486
|
part="option${between ? ' option-between' : ''}"
|
|
485
487
|
exportparts="description: option-description, item: option-item,
|
|
@@ -489,7 +491,9 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
489
491
|
?disabled=${this.barred(at)}
|
|
490
492
|
?selected=${this.held.includes(at)}
|
|
491
493
|
data-between=${between ? '' : nothing}
|
|
492
|
-
|
|
494
|
+
aria-label=${drawn ? written : nothing}
|
|
495
|
+
text-value=${drawn ? written : nothing}
|
|
496
|
+
>${drawn ? drawn(at, written) : written}</${this.tag('option')}
|
|
493
497
|
>`;
|
|
494
498
|
})}
|
|
495
499
|
<slot name="empty" slot="empty">${this.emptyText}</slot>
|
|
@@ -600,6 +604,9 @@ __decorate([
|
|
|
600
604
|
words,
|
|
601
605
|
property({ attribute: 'step-mismatch-message' })
|
|
602
606
|
], HelixHeadlessTimePicker.prototype, "stepMismatchMessage", void 0);
|
|
607
|
+
__decorate([
|
|
608
|
+
property({ attribute: false })
|
|
609
|
+
], HelixHeadlessTimePicker.prototype, "timeContent", void 0);
|
|
603
610
|
__decorate([
|
|
604
611
|
words,
|
|
605
612
|
property({ attribute: 'unavailable-message' })
|
|
@@ -14,7 +14,9 @@ type TimelineTone = 'danger' | 'info' | 'neutral' | 'primary' | 'success' | 'war
|
|
|
14
14
|
* hours ago", from `datetime`, where the `time` slot is empty. The words move
|
|
15
15
|
* on as they go stale, "3 hours ago" becoming "4 hours ago", with one wakeup
|
|
16
16
|
* shared by every reading on the page, as `FormatDate` keeps its own. They are
|
|
17
|
-
* in the language of the page, or of the nearest `lang`.
|
|
17
|
+
* in the language of the page, or of the nearest `lang`. The whole date and
|
|
18
|
+
* time is the reading's `title`, so a pointer resting on "3 hours ago" shows
|
|
19
|
+
* when it was, and a screen reader reads it as the description.
|
|
18
20
|
*
|
|
19
21
|
* Each entry draws the connector line that arrives at it and hides it when it
|
|
20
22
|
* is the first entry, using `:host(:first-child)`. This needs no index, no
|
|
@@ -77,6 +79,8 @@ export declare class HelixHeadlessTimelineItem extends HelixElement {
|
|
|
77
79
|
* otherwise.
|
|
78
80
|
*/
|
|
79
81
|
private reading;
|
|
82
|
+
/** The whole date and time `datetime` names, the reading's title under `relative`. */
|
|
83
|
+
private whole;
|
|
80
84
|
/** The instant `datetime` names, where `relative` asks for a reading of it. */
|
|
81
85
|
private get moment();
|
|
82
86
|
/** What the reading says at `now`, and when it stops being true. */
|
|
@@ -21,6 +21,7 @@ let HelixHeadlessTimelineItem = class HelixHeadlessTimelineItem extends HelixEle
|
|
|
21
21
|
},
|
|
22
22
|
});
|
|
23
23
|
this.reading = '';
|
|
24
|
+
this.whole = '';
|
|
24
25
|
this.pendingLabel = 'Scheduled';
|
|
25
26
|
this.toneLabel = '';
|
|
26
27
|
this.toneLabels = {
|
|
@@ -52,6 +53,13 @@ let HelixHeadlessTimelineItem = class HelixHeadlessTimelineItem extends HelixEle
|
|
|
52
53
|
}
|
|
53
54
|
const moment = this.moment;
|
|
54
55
|
this.reading = moment ? this.readingAt(moment, new Date(this.clock.now)).text : '';
|
|
56
|
+
const dateOnly = /^\d{4}-\d{2}-\d{2}$/.test(this.datetime);
|
|
57
|
+
this.whole = moment
|
|
58
|
+
? new Intl.DateTimeFormat(wordsLocale(this), {
|
|
59
|
+
dateStyle: 'long',
|
|
60
|
+
...(dateOnly ? { timeZone: 'UTC' } : { timeStyle: 'short' }),
|
|
61
|
+
}).format(moment)
|
|
62
|
+
: '';
|
|
55
63
|
hostAria(this.internals, { role: 'listitem' });
|
|
56
64
|
}
|
|
57
65
|
renderMarker() {
|
|
@@ -78,6 +86,7 @@ let HelixHeadlessTimelineItem = class HelixHeadlessTimelineItem extends HelixEle
|
|
|
78
86
|
: html `<time
|
|
79
87
|
part="time"
|
|
80
88
|
datetime=${this.datetime}
|
|
89
|
+
title=${this.whole || nothing}
|
|
81
90
|
>${when}</time
|
|
82
91
|
>`}
|
|
83
92
|
<div part="content"><slot></slot></div>`;
|
|
@@ -26,6 +26,12 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
26
26
|
* Write the entries in the order the events happened, oldest or newest first,
|
|
27
27
|
* and the layout only moves where they are drawn.
|
|
28
28
|
*
|
|
29
|
+
* **A feed that loads more entries sets `loading` while it fetches.** The
|
|
30
|
+
* timeline is busy, and an entry after the last says "Loading more"
|
|
31
|
+
* (`loading-label`, or your own content in the `loading` slot). Put a button
|
|
32
|
+
* that asks for more after the timeline, and move focus to the first new
|
|
33
|
+
* entry when they arrive.
|
|
34
|
+
*
|
|
29
35
|
* @since 0.1.0
|
|
30
36
|
* @status stable
|
|
31
37
|
* @category data
|
|
@@ -33,12 +39,29 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
33
39
|
* @documentation https://fusion.dev/helix/components/timeline
|
|
34
40
|
*
|
|
35
41
|
* @slot - The entries, and any groups of them.
|
|
42
|
+
* @slot loading - Content for the entry after the last while `loading` is set. Falls back to `loading-label`.
|
|
43
|
+
*
|
|
44
|
+
* @csspart loading - The entry after the last while `loading` is set.
|
|
45
|
+
*
|
|
46
|
+
* @cssstate loading - While `loading` is set.
|
|
36
47
|
*/
|
|
37
48
|
export declare class HelixHeadlessTimeline extends HelixElement {
|
|
49
|
+
/**
|
|
50
|
+
* The key this family's words are registered under, its styled tag:
|
|
51
|
+
* `registerWords('de', { 'helix-timeline': { loadingLabel: 'Wird geladen' } })`.
|
|
52
|
+
*/
|
|
53
|
+
static words: string;
|
|
38
54
|
/**
|
|
39
55
|
* Accessible name for the timeline, for people who reach it out of context.
|
|
40
56
|
*/
|
|
41
57
|
label: string;
|
|
58
|
+
/**
|
|
59
|
+
* Marks the timeline as busy fetching more entries, and draws an entry
|
|
60
|
+
* after the last that says so.
|
|
61
|
+
*/
|
|
62
|
+
loading: boolean;
|
|
63
|
+
/** The text of the entry drawn while `loading` is set, where the `loading` slot is empty. */
|
|
64
|
+
loadingLabel: string;
|
|
42
65
|
protected render(): TemplateResult;
|
|
43
66
|
protected willUpdate(changed: PropertyValues): void;
|
|
44
67
|
}
|
|
@@ -4,25 +4,39 @@ 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 { customElement, HelixElement, hostAria } from '@fusion.dev/helix-core';
|
|
8
|
-
import { html } from 'lit';
|
|
7
|
+
import { customElement, HelixElement, hostAria, words } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessTimeline = class HelixHeadlessTimeline extends HelixElement {
|
|
11
11
|
constructor() {
|
|
12
12
|
super(...arguments);
|
|
13
13
|
this.label = '';
|
|
14
|
+
this.loading = false;
|
|
15
|
+
this.loadingLabel = 'Loading more';
|
|
14
16
|
}
|
|
17
|
+
static { this.words = 'helix-timeline'; }
|
|
15
18
|
render() {
|
|
16
|
-
return html `<slot></slot
|
|
19
|
+
return html `<slot></slot>${this.loading
|
|
20
|
+
? html `<div part="loading" role="listitem"><slot name="loading">${this.loadingLabel}</slot></div>`
|
|
21
|
+
: nothing}`;
|
|
17
22
|
}
|
|
18
23
|
willUpdate(changed) {
|
|
19
24
|
super.willUpdate(changed);
|
|
20
25
|
hostAria(this.internals, { label: this.label, role: 'list' });
|
|
26
|
+
this.internals.ariaBusy = this.loading ? 'true' : null;
|
|
27
|
+
this.toggleState('loading', this.loading);
|
|
21
28
|
}
|
|
22
29
|
};
|
|
23
30
|
__decorate([
|
|
24
31
|
property()
|
|
25
32
|
], HelixHeadlessTimeline.prototype, "label", void 0);
|
|
33
|
+
__decorate([
|
|
34
|
+
property({ reflect: true, type: Boolean })
|
|
35
|
+
], HelixHeadlessTimeline.prototype, "loading", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
words,
|
|
38
|
+
property({ attribute: 'loading-label' })
|
|
39
|
+
], HelixHeadlessTimeline.prototype, "loadingLabel", void 0);
|
|
26
40
|
HelixHeadlessTimeline = __decorate([
|
|
27
41
|
customElement('helix-headless-timeline')
|
|
28
42
|
], HelixHeadlessTimeline);
|
|
@@ -32,6 +32,9 @@ declare const HelixHeadlessTooltip_base: (new (...args: any[]) => import("@fusio
|
|
|
32
32
|
* describes a Helix component and leaves a plain element undescribed. In the
|
|
33
33
|
* `for` form the tooltip itself is out of the accessibility tree while its
|
|
34
34
|
* bubble is down, so you meet its words once, as the trigger's description.
|
|
35
|
+
* A trigger whose name is already the tooltip's words, such as an icon button
|
|
36
|
+
* named Settings under a tooltip reading Settings, is not described by them
|
|
37
|
+
* again.
|
|
35
38
|
*
|
|
36
39
|
* The tooltip shows on hover and on focus. It hides on pointer leave, on blur,
|
|
37
40
|
* on Escape, and when the trigger is activated. The pointer can move from the
|
|
@@ -394,6 +397,13 @@ export declare class HelixHeadlessTooltip extends HelixHeadlessTooltip_base {
|
|
|
394
397
|
* plain element in the consumer's markup. See `linkDescription`.
|
|
395
398
|
*/
|
|
396
399
|
private link;
|
|
400
|
+
/**
|
|
401
|
+
* Whether the bubble's words add anything to the trigger: they are not
|
|
402
|
+
* empty, and they are not the trigger's own name, such as an icon button
|
|
403
|
+
* named Settings under a tooltip reading Settings, which a screen reader
|
|
404
|
+
* read as "Settings, Settings".
|
|
405
|
+
*/
|
|
406
|
+
private get describes();
|
|
397
407
|
/** A reader arriving on the trigger, which is late enough to write the link. */
|
|
398
408
|
private readonly linkOnArrival;
|
|
399
409
|
private unbind;
|
|
@@ -213,17 +213,22 @@ let HelixHeadlessTooltip = class HelixHeadlessTooltip extends MixinControllerOpe
|
|
|
213
213
|
});
|
|
214
214
|
}
|
|
215
215
|
link(light, target = focusTarget(this.trigger)) {
|
|
216
|
-
|
|
216
|
+
const describes = this.describes;
|
|
217
|
+
if (this.linked === target && describes) {
|
|
217
218
|
return;
|
|
218
219
|
}
|
|
219
220
|
if (this.linked) {
|
|
220
221
|
unlinkDescription(this.linked, this);
|
|
221
222
|
this.linked = undefined;
|
|
222
223
|
}
|
|
223
|
-
if (linkDescription(target, this, light)) {
|
|
224
|
+
if (describes && linkDescription(target, this, light)) {
|
|
224
225
|
this.linked = target;
|
|
225
226
|
}
|
|
226
227
|
}
|
|
228
|
+
get describes() {
|
|
229
|
+
const text = this.describedText;
|
|
230
|
+
return text !== '' && !this.triggerSays(text);
|
|
231
|
+
}
|
|
227
232
|
unbind() {
|
|
228
233
|
if (this.linked) {
|
|
229
234
|
unlinkDescription(this.linked, this);
|
|
@@ -314,6 +319,13 @@ let HelixHeadlessTooltip = class HelixHeadlessTooltip extends MixinControllerOpe
|
|
|
314
319
|
if (changed.has('for')) {
|
|
315
320
|
this.bind();
|
|
316
321
|
}
|
|
322
|
+
if (this.linked && !this.describes) {
|
|
323
|
+
unlinkDescription(this.linked, this);
|
|
324
|
+
this.linked = undefined;
|
|
325
|
+
}
|
|
326
|
+
else if (!this.linked && this.bound) {
|
|
327
|
+
this.link(false);
|
|
328
|
+
}
|
|
317
329
|
if (changed.has('open')) {
|
|
318
330
|
const warm = warmth();
|
|
319
331
|
if (this.open) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { reorderVirtualList, VirtualListReorderController } from './virtual-list-reorder.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { HelixHeadlessVirtualList } from '../root/virtual-list-root.js';
|
|
2
|
+
import { reorderVirtualList } from './virtual-list-reorder.js';
|
|
3
|
+
reorderVirtualList(HelixHeadlessVirtualList);
|
|
4
|
+
export { reorderVirtualList, VirtualListReorderController } from './virtual-list-reorder.js';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { CSSResult, ReactiveController } from 'lit';
|
|
2
|
+
import type { HelixHeadlessVirtualList } from '../root/virtual-list-root.js';
|
|
3
|
+
/**
|
|
4
|
+
* Moves the items of a `reorderable` virtual list, by pointer and by keyboard,
|
|
5
|
+
* and says each move in a region screen readers announce.
|
|
6
|
+
*
|
|
7
|
+
* **Its own entry**, `@fusion.dev/helix-headless/virtual-list/reorder`, so a
|
|
8
|
+
* list that never moves items carries none of this. Importing the entry gives
|
|
9
|
+
* every virtual list one, and `reorderable` turns it on.
|
|
10
|
+
*
|
|
11
|
+
* - **Alt with Up or Down** moves the item the keys are on a place at once,
|
|
12
|
+
* and Alt with Home or End to either end, as the APG's rearrangeable
|
|
13
|
+
* listbox does. Across, Alt with Left or Right does, mirrored right to left.
|
|
14
|
+
* The keys stay on the item that moved.
|
|
15
|
+
* - **A pointer** drags a whole row, with no grip: a press that travels is a
|
|
16
|
+
* drag, and one that does not is still a press. The list scrolls near an
|
|
17
|
+
* edge.
|
|
18
|
+
* - **The page moves the items.** A drop sends `helix-before-reorder` and then
|
|
19
|
+
* `helix-reorder` with every key in the new order.
|
|
20
|
+
*
|
|
21
|
+
* A row being dragged carries `data-dragging`, and the row a drop would land
|
|
22
|
+
* beside `data-drop-before` or `data-drop-after`. While a pointer drags a row,
|
|
23
|
+
* a copy of it follows the pointer in the list's `drag-preview` part. The
|
|
24
|
+
* styled tier's entry hands in the sheet that draws those, which the list
|
|
25
|
+
* adopts while it reorders.
|
|
26
|
+
*/
|
|
27
|
+
export declare class VirtualListReorderController implements ReactiveController {
|
|
28
|
+
private readonly host;
|
|
29
|
+
/** The sheet the list adopts while it reorders, from the styled tier. */
|
|
30
|
+
sheet: CSSResult | undefined;
|
|
31
|
+
/** The rows marked last, so a mark that moves is taken off where it was. */
|
|
32
|
+
private marked;
|
|
33
|
+
/** The region each move is said in, drawn while the list reorders. */
|
|
34
|
+
private live;
|
|
35
|
+
private readonly reorder;
|
|
36
|
+
constructor(host: HelixHeadlessVirtualList, sheet?: CSSResult);
|
|
37
|
+
/** Whether the list moves its items now: one column, without sections. */
|
|
38
|
+
private get enabled();
|
|
39
|
+
/**
|
|
40
|
+
* The words of each drawn row by its item's key, as they were when the list
|
|
41
|
+
* last drew. A move is said after the page has moved the items and before
|
|
42
|
+
* the rows are drawn again, so a row's own words then belong to another item.
|
|
43
|
+
*/
|
|
44
|
+
private names;
|
|
45
|
+
hostUpdated(): void;
|
|
46
|
+
hostConnected(): void;
|
|
47
|
+
hostDisconnected(): void;
|
|
48
|
+
/** The rows the list draws now. */
|
|
49
|
+
private get rows();
|
|
50
|
+
/** The key of the item a row draws. */
|
|
51
|
+
private keyOf;
|
|
52
|
+
/** The row that draws the item with that key, where one is drawn. */
|
|
53
|
+
private rowFor;
|
|
54
|
+
/** The row an event passed through. */
|
|
55
|
+
private rowOf;
|
|
56
|
+
/** The place an item moved by a key goes to, until the page has moved it. */
|
|
57
|
+
private waiting;
|
|
58
|
+
/**
|
|
59
|
+
* Keeps the keys on the item that moved. The page moves the items as it
|
|
60
|
+
* hears this, and the rows are drawn again in place, so the row that had
|
|
61
|
+
* focus now draws another item. Focus goes to the item's new row once the
|
|
62
|
+
* list has drawn it.
|
|
63
|
+
*/
|
|
64
|
+
private readonly handleReorder;
|
|
65
|
+
/**
|
|
66
|
+
* Draws the list's `drag-preview` part and its region for the words while it
|
|
67
|
+
* reorders, and takes them away after. The list's own template draws
|
|
68
|
+
* neither.
|
|
69
|
+
*/
|
|
70
|
+
private draw;
|
|
71
|
+
/** Puts each row's mark in a move on it, and the styled tier's sheet on the list. */
|
|
72
|
+
private mark;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Gives every virtual list of a class a `VirtualListReorderController`, and
|
|
76
|
+
* the class the words it says, as properties that read and write their
|
|
77
|
+
* attributes: `movedLabel`, `canceledLabel`, `droppedLabel`, and
|
|
78
|
+
* `pickedUpLabel`.
|
|
79
|
+
*
|
|
80
|
+
* The entries call it for their own tier's class: the headless entry for
|
|
81
|
+
* `HelixHeadlessVirtualList`, and the styled one for `HelixVirtualList`, with
|
|
82
|
+
* the sheet that draws a move. **Lists already on the page are given one
|
|
83
|
+
* too**, so a page that imports the list and then this entry has its lists
|
|
84
|
+
* reorder.
|
|
85
|
+
*/
|
|
86
|
+
export declare const reorderVirtualList: (element: typeof HelixHeadlessVirtualList, sheet?: CSSResult) => void;
|