@fusion.dev/helix-headless 0.150.0 → 0.152.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 +1388 -93
- package/dist/app-header/root/app-header-root.d.ts +75 -0
- package/dist/app-header/root/app-header-root.js +140 -5
- package/dist/area-chart/root/area-chart-root.d.ts +36 -0
- package/dist/area-chart/root/area-chart-root.js +17 -2
- package/dist/bar-chart/root/bar-chart-root.d.ts +28 -4
- package/dist/bar-chart/root/bar-chart-root.js +17 -4
- package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +11 -2
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +11 -2
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +14 -2
- package/dist/breadcrumbs/root/breadcrumbs-root.js +16 -3
- package/dist/callout/root/callout-root.d.ts +20 -0
- package/dist/callout/root/callout-root.js +19 -4
- package/dist/chat/composer/chat-composer.d.ts +30 -0
- package/dist/chat/composer/chat-composer.js +47 -2
- package/dist/chat/root/chat-root.d.ts +10 -0
- package/dist/chat/root/chat-root.js +14 -0
- package/dist/data-table/root/data-table-root.d.ts +34 -2
- package/dist/data-table/root/data-table-root.js +63 -2
- package/dist/dialog/description.d.ts +10 -0
- package/dist/dialog/description.js +22 -8
- package/dist/dialog/root/dialog-root.d.ts +17 -1
- package/dist/dialog/root/dialog-root.js +2 -2
- package/dist/divider/root/divider-root.d.ts +8 -6
- package/dist/divider/root/divider-root.js +10 -2
- package/dist/drawer/root/drawer-root.d.ts +38 -2
- package/dist/drawer/root/drawer-root.js +33 -6
- package/dist/element-events.generated.js +4 -0
- package/dist/kbd/root/kbd-root.d.ts +7 -1
- package/dist/kbd/root/kbd-root.js +8 -4
- package/dist/lightbox/root/lightbox-root.d.ts +44 -6
- package/dist/lightbox/root/lightbox-root.js +59 -11
- package/dist/lightbox/zoom-controller.d.ts +5 -0
- package/dist/lightbox/zoom-controller.js +8 -0
- package/dist/line-chart/root/line-chart-root.d.ts +36 -0
- package/dist/line-chart/root/line-chart-root.js +15 -2
- package/dist/menu/root/menu-root.js +2 -0
- package/dist/navigation/root/navigation-root.d.ts +11 -1
- package/dist/navigation/root/navigation-root.js +10 -5
- package/dist/notifications/root/notifications-root.d.ts +17 -0
- package/dist/notifications/root/notifications-root.js +21 -1
- package/dist/popover/root/popover-root.d.ts +18 -7
- package/dist/popover/root/popover-root.js +8 -1
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +18 -0
- package/dist/scatter-chart/root/scatter-chart-root.js +38 -15
- package/dist/splitter/root/splitter-root.d.ts +41 -0
- package/dist/splitter/root/splitter-root.js +77 -0
- package/dist/table/column/table-column.js +3 -2
- package/dist/table/grid-controller.d.ts +14 -2
- package/dist/table/grid-controller.js +27 -1
- package/dist/table/root/table-root.d.ts +30 -4
- package/dist/table/root/table-root.js +45 -7
- package/dist/table/row/table-row.d.ts +12 -2
- package/dist/table/row/table-row.js +8 -2
- package/dist/tabs/root/tabs-root.d.ts +25 -0
- package/dist/tabs/root/tabs-root.js +28 -1
- package/dist/tour/naming.d.ts +9 -3
- package/dist/tour/naming.js +9 -3
- package/dist/tour/root/tour-root.d.ts +29 -5
- package/dist/tour/root/tour-root.js +13 -1
- package/dist/tour/stop/tour-stop.d.ts +2 -1
- package/dist/tour/stop/tour-stop.js +4 -1
- package/dist/tree/root/tree-root.d.ts +13 -0
- package/dist/tree/root/tree-root.js +42 -2
- package/dist/video/root/video-root.d.ts +6 -0
- package/dist/video/root/video-root.js +5 -1
- package/dist/video-playlist/root/video-playlist-root.d.ts +4 -0
- package/dist/video-playlist/root/video-playlist-root.js +4 -1
- package/dist/virtual-list/root/virtual-list-root.d.ts +23 -1
- package/dist/virtual-list/root/virtual-list-root.js +21 -2
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +1098 -128
- package/types/vue.d.ts +248 -59
- package/types/words.d.ts +12 -2
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
-
import '../breadcrumb/breadcrumb.js';
|
|
4
3
|
/** One step of a trail written as data, for `items`. */
|
|
5
4
|
export interface BreadcrumbItem {
|
|
6
5
|
/** The URL the step links to. The last step is the current page and takes none. */
|
|
@@ -26,7 +25,8 @@ export interface BreadcrumbItem {
|
|
|
26
25
|
* markers.
|
|
27
26
|
*
|
|
28
27
|
* The items are slotted directly, so they are `<ol>` children in the flat tree,
|
|
29
|
-
* and each item sets `role="listitem"` on itself.
|
|
28
|
+
* and each item sets `role="listitem"` on itself. Where no slotted crumb is
|
|
29
|
+
* `current`, the last one is said to be the current page.
|
|
30
30
|
*
|
|
31
31
|
* The trail does not measure itself, and you write its overflow in markup: put
|
|
32
32
|
* the crumb that holds the folded items where the gap should appear.
|
|
@@ -41,6 +41,11 @@ export interface BreadcrumbItem {
|
|
|
41
41
|
* to the first crumb it brought back. A new `items` folds again. Slotted crumbs
|
|
42
42
|
* do not fold; write the folded crumb where the gap should be.
|
|
43
43
|
*
|
|
44
|
+
* **Keyboard.** Each crumb with an `href` is a link and one tab stop, and
|
|
45
|
+
* Enter follows it. The current page and the separators are not in the tab
|
|
46
|
+
* order. In a folded trail, Enter or Space on the fold's button shows the
|
|
47
|
+
* whole trail and moves focus to the first crumb it brought back.
|
|
48
|
+
*
|
|
44
49
|
* @since 0.1.0
|
|
45
50
|
* @status stable
|
|
46
51
|
* @category navigation
|
|
@@ -96,8 +101,15 @@ export declare class HelixHeadlessBreadcrumbs extends HelixElement {
|
|
|
96
101
|
/** Shows the whole trail and moves focus to the first crumb it brought back. */
|
|
97
102
|
private readonly expand;
|
|
98
103
|
protected willUpdate(changed: PropertyValues): void;
|
|
104
|
+
protected updated(changed: PropertyValues): void;
|
|
99
105
|
/** How many crumbs drawn from `items` are folded away, or 0. */
|
|
100
106
|
private get folded();
|
|
107
|
+
/**
|
|
108
|
+
* Says the last slotted crumb is the current page where none is marked
|
|
109
|
+
* `current`, as React Aria's trail does. Through a property the crumb does
|
|
110
|
+
* not reflect, so nothing is written onto your markup.
|
|
111
|
+
*/
|
|
112
|
+
private readonly markLast;
|
|
101
113
|
/** The crumbs `items` describes, each a crumb of this tier. */
|
|
102
114
|
protected renderItems(): TemplateResult | typeof nothing;
|
|
103
115
|
protected render(): TemplateResult;
|
|
@@ -4,12 +4,12 @@ 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, formatWords, HelixElement, hostAria, words } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, formatWords, HelixElement, hostAria, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { nothing } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
import { repeat } from 'lit/directives/repeat.js';
|
|
11
11
|
import { html } from 'lit/static-html.js';
|
|
12
|
-
import '../breadcrumb/breadcrumb.js';
|
|
12
|
+
import { HelixHeadlessBreadcrumb } from '../breadcrumb/breadcrumb.js';
|
|
13
13
|
let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixElement {
|
|
14
14
|
constructor() {
|
|
15
15
|
super(...arguments);
|
|
@@ -24,6 +24,15 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
|
|
|
24
24
|
crumb?.shadowRoot?.querySelector('a, [part="link"]')?.focus();
|
|
25
25
|
});
|
|
26
26
|
};
|
|
27
|
+
this.markLast = () => {
|
|
28
|
+
const crumbs = (this.renderRoot.querySelector('slot:not([name])')?.assignedElements() ?? []).filter((one) => one instanceof HelixHeadlessBreadcrumb);
|
|
29
|
+
const marked = crumbs.some((one) => one.current);
|
|
30
|
+
crumbs.forEach((crumb, index) => {
|
|
31
|
+
whenHydrated(crumb, () => {
|
|
32
|
+
crumb.implied = !marked && this.items.length === 0 && index === crumbs.length - 1;
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
};
|
|
27
36
|
}
|
|
28
37
|
static { this.words = 'helix-breadcrumbs'; }
|
|
29
38
|
willUpdate(changed) {
|
|
@@ -33,6 +42,10 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
|
|
|
33
42
|
this.expanded = false;
|
|
34
43
|
}
|
|
35
44
|
}
|
|
45
|
+
updated(changed) {
|
|
46
|
+
super.updated(changed);
|
|
47
|
+
this.markLast();
|
|
48
|
+
}
|
|
36
49
|
get folded() {
|
|
37
50
|
const most = this.maxItems ?? 0;
|
|
38
51
|
const count = this.items.length;
|
|
@@ -77,7 +90,7 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
|
|
|
77
90
|
}
|
|
78
91
|
render() {
|
|
79
92
|
return html `<ol part="list" role="list">
|
|
80
|
-
${this.renderItems()}<slot></slot>
|
|
93
|
+
${this.renderItems()}<slot @slotchange=${this.markLast}></slot>
|
|
81
94
|
</ol>`;
|
|
82
95
|
}
|
|
83
96
|
};
|
|
@@ -48,6 +48,16 @@ export declare class HelixHeadlessCallout extends HelixHeadlessCallout_base {
|
|
|
48
48
|
*/
|
|
49
49
|
static words: string;
|
|
50
50
|
protected willUpdate(changed: PropertyValues): void;
|
|
51
|
+
/**
|
|
52
|
+
* The dismiss button's name, read by screen readers, with `{label}` for
|
|
53
|
+
* what the callout is called: its heading, or the tone's name where it has
|
|
54
|
+
* none.
|
|
55
|
+
*
|
|
56
|
+
* "Dismiss Scheduled maintenance" says which of several callouts the button
|
|
57
|
+
* takes away, where every button was "Dismiss". A label without `{label}` is
|
|
58
|
+
* read as written.
|
|
59
|
+
*/
|
|
60
|
+
dismissLabel: string;
|
|
51
61
|
/**
|
|
52
62
|
* What kind of attention the passage needs.
|
|
53
63
|
*
|
|
@@ -96,6 +106,16 @@ export declare class HelixHeadlessCallout extends HelixHeadlessCallout_base {
|
|
|
96
106
|
* above.
|
|
97
107
|
*/
|
|
98
108
|
protected renderDismissIcon(): TemplateResult;
|
|
109
|
+
/**
|
|
110
|
+
* The dismiss button's name: `dismissLabel` with the heading's words, or the
|
|
111
|
+
* tone's name.
|
|
112
|
+
*
|
|
113
|
+
* The heading is read from the children, so a server and a hydrating render
|
|
114
|
+
* name the button for the tone, and the update after names it for the
|
|
115
|
+
* heading.
|
|
116
|
+
*/
|
|
117
|
+
private dismissName;
|
|
118
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
99
119
|
protected render(): TemplateResult;
|
|
100
120
|
}
|
|
101
121
|
declare global {
|
|
@@ -4,13 +4,14 @@ 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, drawDismiss, drawDismissGlyph, HelixElement, hostAria, MixinDismissible, TONE_MARKS, toneMark, words, wordsConverter, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, drawDismiss, drawDismissGlyph, formatWords, HelixElement, hostAria, MixinDismissible, slottedWords, TONE_MARKS, toneMark, words, wordsConverter, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
11
11
|
let HelixHeadlessCallout = class HelixHeadlessCallout extends MixinDismissible(HelixElement) {
|
|
12
12
|
constructor() {
|
|
13
13
|
super(...arguments);
|
|
14
|
+
this.dismissLabel = 'Dismiss {label}';
|
|
14
15
|
this.toneLabel = '';
|
|
15
16
|
this.toneLabels = {
|
|
16
17
|
danger: 'Error',
|
|
@@ -26,7 +27,7 @@ let HelixHeadlessCallout = class HelixHeadlessCallout extends MixinDismissible(H
|
|
|
26
27
|
hostAria(this.internals, { role: 'note' });
|
|
27
28
|
}
|
|
28
29
|
renderIcon(icon, _label) {
|
|
29
|
-
return html `<slot name="icon">
|
|
30
|
+
return html `<slot name="icon" aria-hidden="true">
|
|
30
31
|
<svg part="icon-mark" fill="currentColor" viewBox=${icon.viewBox} aria-hidden="true">
|
|
31
32
|
${unsafeSVG(icon.content)}
|
|
32
33
|
</svg>
|
|
@@ -37,23 +38,37 @@ let HelixHeadlessCallout = class HelixHeadlessCallout extends MixinDismissible(H
|
|
|
37
38
|
${drawDismissGlyph()}
|
|
38
39
|
</slot>`;
|
|
39
40
|
}
|
|
41
|
+
dismissName(tone) {
|
|
42
|
+
const heading = this.childrenKnown ? slottedWords(this, ['heading']) : '';
|
|
43
|
+
return this.dismissLabel.includes('{label}')
|
|
44
|
+
? formatWords(this, 'dismissLabel', { label: heading || tone }).trim()
|
|
45
|
+
: this.dismissLabel;
|
|
46
|
+
}
|
|
47
|
+
firstUpdated(changed) {
|
|
48
|
+
super.firstUpdated(changed);
|
|
49
|
+
this.finishHydration();
|
|
50
|
+
}
|
|
40
51
|
render() {
|
|
41
52
|
const tone = this.tone === 'neutral' ? 'neutral' : toneMark(this.tone) ? (this.tone ?? 'info') : 'info';
|
|
42
53
|
const { icon } = toneMark(tone) ?? TONE_MARKS.info;
|
|
43
54
|
const named = this.toneLabel || (this.toneLabels[tone] ?? '');
|
|
44
55
|
return html `<span part="icon" role="img" aria-label=${named}>${this.renderIcon(icon, named)}</span>
|
|
45
56
|
<div part="content">
|
|
46
|
-
<slot name="heading"></slot>
|
|
57
|
+
<slot name="heading" @slotchange=${() => this.requestUpdate()}></slot>
|
|
47
58
|
<slot></slot>
|
|
48
59
|
</div>${this.dismissible
|
|
49
60
|
? drawDismiss({
|
|
50
|
-
label: this.
|
|
61
|
+
label: this.dismissName(named),
|
|
51
62
|
mark: this.renderDismissIcon(),
|
|
52
63
|
onDismiss: () => this.dismiss(),
|
|
53
64
|
})
|
|
54
65
|
: nothing}`;
|
|
55
66
|
}
|
|
56
67
|
};
|
|
68
|
+
__decorate([
|
|
69
|
+
words,
|
|
70
|
+
property({ attribute: 'dismiss-label' })
|
|
71
|
+
], HelixHeadlessCallout.prototype, "dismissLabel", void 0);
|
|
57
72
|
__decorate([
|
|
58
73
|
property({ reflect: true })
|
|
59
74
|
], HelixHeadlessCallout.prototype, "tone", void 0);
|
|
@@ -20,6 +20,13 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
20
20
|
* disabled>` turns the composer off. Sending is still `helix-send`, and it
|
|
21
21
|
* does not submit the form.
|
|
22
22
|
*
|
|
23
|
+
* **`required`, `minlength` and `maxlength` are checked by the browser**, as
|
|
24
|
+
* on a `<textarea>`, and the message is the browser's. `maxlength` stops the
|
|
25
|
+
* field taking more. A draft that fails them does not send, and the composer
|
|
26
|
+
* takes `:state(invalid)` and `aria-invalid` once the reader has tried to send
|
|
27
|
+
* it, the form was submitted, or the reader edited the field and left it.
|
|
28
|
+
* Tabbing past an empty composer marks nothing.
|
|
29
|
+
*
|
|
23
30
|
* **`typing` says whether the user is writing**, and `helix-typing` is sent
|
|
24
31
|
* each time it changes: when they start, and when they send or stop for
|
|
25
32
|
* `typing-timeout` milliseconds. Pass it on to show "is typing" to the other
|
|
@@ -45,6 +52,8 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
45
52
|
* @csspart send - The send button.
|
|
46
53
|
* @csspart start - The box around the `start` slot.
|
|
47
54
|
* @csspart tools - The row under the field.
|
|
55
|
+
*
|
|
56
|
+
* @cssstate invalid - The draft fails `required`, `minlength`, `maxlength`, or a custom error, and the reader has tried to send or submit it, or edited the field and left it.
|
|
48
57
|
*/
|
|
49
58
|
export declare class HelixHeadlessChatComposer extends HelixFormElement {
|
|
50
59
|
/**
|
|
@@ -56,10 +65,24 @@ export declare class HelixHeadlessChatComposer extends HelixFormElement {
|
|
|
56
65
|
* The field's name, read by screen readers.
|
|
57
66
|
*/
|
|
58
67
|
label: string;
|
|
68
|
+
/**
|
|
69
|
+
* The most characters a draft can have. The field stops taking more.
|
|
70
|
+
*/
|
|
71
|
+
maxLength?: number;
|
|
72
|
+
/**
|
|
73
|
+
* The fewest characters a draft can have, once the reader has written in
|
|
74
|
+
* the field, as on a `<textarea>`.
|
|
75
|
+
*/
|
|
76
|
+
minLength?: number;
|
|
59
77
|
/**
|
|
60
78
|
* Placeholder text shown in an empty field.
|
|
61
79
|
*/
|
|
62
80
|
placeholder: string;
|
|
81
|
+
/**
|
|
82
|
+
* Whether the form needs a draft to submit. An empty draft never sends,
|
|
83
|
+
* required or not.
|
|
84
|
+
*/
|
|
85
|
+
required: boolean;
|
|
63
86
|
/**
|
|
64
87
|
* How many rows the field opens at.
|
|
65
88
|
*
|
|
@@ -97,6 +120,10 @@ export declare class HelixHeadlessChatComposer extends HelixFormElement {
|
|
|
97
120
|
readonly isChatComposer = true;
|
|
98
121
|
/** Whether the user is writing, which `helix-typing` announces. */
|
|
99
122
|
private writing;
|
|
123
|
+
/** Whether the reader has tried to send or submit the draft, or edited it and left. */
|
|
124
|
+
private touched;
|
|
125
|
+
/** Whether the composer shows that its draft is wrong. */
|
|
126
|
+
private shownInvalid;
|
|
100
127
|
/** The pause after which the user has stopped writing. */
|
|
101
128
|
private readonly pause;
|
|
102
129
|
/**
|
|
@@ -121,6 +148,8 @@ export declare class HelixHeadlessChatComposer extends HelixFormElement {
|
|
|
121
148
|
private get declared();
|
|
122
149
|
/** Puts back the draft the markup declared when the form is reset. */
|
|
123
150
|
formResetCallback(): void;
|
|
151
|
+
/** The reader has had a go at the draft, so whether it is wrong can be shown. */
|
|
152
|
+
private readonly touch;
|
|
124
153
|
/** Brings back the draft when the browser restores the form. */
|
|
125
154
|
formStateRestoreCallback(state: string | File | FormData | null): void;
|
|
126
155
|
/**
|
|
@@ -133,6 +162,7 @@ export declare class HelixHeadlessChatComposer extends HelixFormElement {
|
|
|
133
162
|
*/
|
|
134
163
|
private markEmpty;
|
|
135
164
|
protected updated(changed: PropertyValues): void;
|
|
165
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
136
166
|
protected willUpdate(changed: PropertyValues): void;
|
|
137
167
|
/**
|
|
138
168
|
* The control that offers the turn, behind a seam so a tier can draw its own.
|
|
@@ -6,8 +6,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
};
|
|
7
7
|
import { customElement, DelayController, HelixFormElement, HelixSendEvent, HelixTypingEvent, hostAria, markRegion, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { arrowElbowDownLeft } from '@fusion.dev/helix-icons';
|
|
9
|
-
import { html } from 'lit';
|
|
10
|
-
import { property } from 'lit/decorators.js';
|
|
9
|
+
import { html, nothing } from 'lit';
|
|
10
|
+
import { property, state } from 'lit/decorators.js';
|
|
11
11
|
import { live } from 'lit/directives/live.js';
|
|
12
12
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
13
13
|
let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFormElement {
|
|
@@ -15,6 +15,7 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFor
|
|
|
15
15
|
super(...arguments);
|
|
16
16
|
this.label = 'Message';
|
|
17
17
|
this.placeholder = '';
|
|
18
|
+
this.required = false;
|
|
18
19
|
this.rows = 1;
|
|
19
20
|
this.sendLabel = 'Send';
|
|
20
21
|
this.submitOn = 'enter';
|
|
@@ -22,7 +23,13 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFor
|
|
|
22
23
|
this.value = '';
|
|
23
24
|
this.isChatComposer = true;
|
|
24
25
|
this.writing = false;
|
|
26
|
+
this.touched = false;
|
|
27
|
+
this.shownInvalid = false;
|
|
25
28
|
this.pause = new DelayController(this, { renders: false });
|
|
29
|
+
this.touch = () => {
|
|
30
|
+
this.touched = true;
|
|
31
|
+
this.requestUpdate();
|
|
32
|
+
};
|
|
26
33
|
this.handleInput = (event) => {
|
|
27
34
|
event.stopPropagation();
|
|
28
35
|
this.value = event.target.value;
|
|
@@ -65,10 +72,15 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFor
|
|
|
65
72
|
if (value === '' || this.isDisabled) {
|
|
66
73
|
return false;
|
|
67
74
|
}
|
|
75
|
+
if (!this.internals.validity.valid) {
|
|
76
|
+
this.touch();
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
68
79
|
if (!this.dispatchEvent(new HelixSendEvent({ value }))) {
|
|
69
80
|
return false;
|
|
70
81
|
}
|
|
71
82
|
this.value = '';
|
|
83
|
+
this.touched = false;
|
|
72
84
|
this.markTyping(false);
|
|
73
85
|
return true;
|
|
74
86
|
}
|
|
@@ -78,6 +90,8 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFor
|
|
|
78
90
|
formResetCallback() {
|
|
79
91
|
super.formResetCallback();
|
|
80
92
|
this.value = this.declared;
|
|
93
|
+
this.touched = false;
|
|
94
|
+
this.requestUpdate();
|
|
81
95
|
}
|
|
82
96
|
formStateRestoreCallback(state) {
|
|
83
97
|
this.value = typeof state === 'string' ? state : this.declared;
|
|
@@ -93,6 +107,20 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFor
|
|
|
93
107
|
if (changed.has('value')) {
|
|
94
108
|
this.internals.setFormValue(this.value === '' ? null : this.value);
|
|
95
109
|
}
|
|
110
|
+
const field = this.partNamed('field');
|
|
111
|
+
if (field?.validity) {
|
|
112
|
+
this.internals.setValidity(field.validity, field.validationMessage, field);
|
|
113
|
+
}
|
|
114
|
+
const invalid = this.touched && !this.internals.validity.valid;
|
|
115
|
+
this.toggleState('invalid', invalid);
|
|
116
|
+
this.internals.ariaInvalid = invalid ? 'true' : null;
|
|
117
|
+
if (invalid !== this.shownInvalid) {
|
|
118
|
+
this.shownInvalid = invalid;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
firstUpdated(changed) {
|
|
122
|
+
super.firstUpdated(changed);
|
|
123
|
+
this.addEventListener('invalid', this.touch);
|
|
96
124
|
}
|
|
97
125
|
willUpdate(changed) {
|
|
98
126
|
super.willUpdate(changed);
|
|
@@ -124,7 +152,12 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFor
|
|
|
124
152
|
aria-label=${this.label}
|
|
125
153
|
placeholder=${this.placeholder}
|
|
126
154
|
rows=${this.rows}
|
|
155
|
+
?required=${this.required}
|
|
156
|
+
minlength=${this.minLength ?? nothing}
|
|
157
|
+
maxlength=${this.maxLength ?? nothing}
|
|
158
|
+
aria-invalid=${this.shownInvalid ? 'true' : nothing}
|
|
127
159
|
?disabled=${this.isDisabled}
|
|
160
|
+
@change=${this.touch}
|
|
128
161
|
@input=${this.handleInput}
|
|
129
162
|
@keydown=${this.handleKey}
|
|
130
163
|
></textarea>
|
|
@@ -140,9 +173,18 @@ __decorate([
|
|
|
140
173
|
words,
|
|
141
174
|
property()
|
|
142
175
|
], HelixHeadlessChatComposer.prototype, "label", void 0);
|
|
176
|
+
__decorate([
|
|
177
|
+
property({ attribute: 'maxlength', type: Number })
|
|
178
|
+
], HelixHeadlessChatComposer.prototype, "maxLength", void 0);
|
|
179
|
+
__decorate([
|
|
180
|
+
property({ attribute: 'minlength', type: Number })
|
|
181
|
+
], HelixHeadlessChatComposer.prototype, "minLength", void 0);
|
|
143
182
|
__decorate([
|
|
144
183
|
property()
|
|
145
184
|
], HelixHeadlessChatComposer.prototype, "placeholder", void 0);
|
|
185
|
+
__decorate([
|
|
186
|
+
property({ type: Boolean })
|
|
187
|
+
], HelixHeadlessChatComposer.prototype, "required", void 0);
|
|
146
188
|
__decorate([
|
|
147
189
|
property({ type: Number })
|
|
148
190
|
], HelixHeadlessChatComposer.prototype, "rows", void 0);
|
|
@@ -159,6 +201,9 @@ __decorate([
|
|
|
159
201
|
__decorate([
|
|
160
202
|
property()
|
|
161
203
|
], HelixHeadlessChatComposer.prototype, "value", void 0);
|
|
204
|
+
__decorate([
|
|
205
|
+
state()
|
|
206
|
+
], HelixHeadlessChatComposer.prototype, "shownInvalid", void 0);
|
|
162
207
|
HelixHeadlessChatComposer = __decorate([
|
|
163
208
|
customElement('helix-headless-chat-composer')
|
|
164
209
|
], HelixHeadlessChatComposer);
|
|
@@ -331,6 +331,16 @@ export declare class HelixHeadlessChat extends HelixHeadlessChat_base {
|
|
|
331
331
|
get messages(): HelixHeadlessChatMessage[];
|
|
332
332
|
/** The element that scrolls, once there is one. */
|
|
333
333
|
private get log();
|
|
334
|
+
/**
|
|
335
|
+
* Makes the log a tab stop while it scrolls, and takes the stop away when it
|
|
336
|
+
* does not.
|
|
337
|
+
*
|
|
338
|
+
* **A reader on a keyboard scrolls the history from the log itself.**
|
|
339
|
+
* Chromium and Firefox make a box that scrolls focusable on their own, and
|
|
340
|
+
* Safari does not, so there the arrow keys could not reach a long history.
|
|
341
|
+
* A log that fits is no stop at all, so Tab goes straight to the composer.
|
|
342
|
+
*/
|
|
343
|
+
private markScroller;
|
|
334
344
|
/** Whether the reader is within `threshold` of the bottom right now. */
|
|
335
345
|
private get atBottom();
|
|
336
346
|
/** What identifies a turn fed as data: its key, or the object it is. */
|
|
@@ -78,6 +78,7 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
78
78
|
});
|
|
79
79
|
this.filled = undefined;
|
|
80
80
|
this.handleResize = () => {
|
|
81
|
+
this.markScroller();
|
|
81
82
|
if (this.pinned) {
|
|
82
83
|
this.stick();
|
|
83
84
|
}
|
|
@@ -141,6 +142,18 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
141
142
|
get log() {
|
|
142
143
|
return this.partNamed('log');
|
|
143
144
|
}
|
|
145
|
+
markScroller() {
|
|
146
|
+
const log = this.log;
|
|
147
|
+
if (!log) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (log.scrollHeight > log.clientHeight) {
|
|
151
|
+
log.tabIndex = 0;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
log.removeAttribute('tabindex');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
144
157
|
get atBottom() {
|
|
145
158
|
const log = this.log;
|
|
146
159
|
if (!log) {
|
|
@@ -421,6 +434,7 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
421
434
|
this.virtualize.measure();
|
|
422
435
|
}
|
|
423
436
|
this.watchSizes();
|
|
437
|
+
this.markScroller();
|
|
424
438
|
this.remember();
|
|
425
439
|
this.reachHistory();
|
|
426
440
|
this.announce();
|
|
@@ -40,6 +40,10 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
|
|
|
40
40
|
* and shows a dash while only some are chosen.
|
|
41
41
|
* - Shift with a press on a checkbox selects every row between it and the row
|
|
42
42
|
* last chosen.
|
|
43
|
+
* - `selection-behavior="replace"` makes a press or a move select the row
|
|
44
|
+
* alone, as a file list does. Control or Command with a press adds or takes
|
|
45
|
+
* out one row, and Shift with a press selects every row from the one last
|
|
46
|
+
* chosen.
|
|
43
47
|
*
|
|
44
48
|
* To act on a single row, such as opening it, put a link or a button in a cell.
|
|
45
49
|
*
|
|
@@ -56,7 +60,8 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
|
|
|
56
60
|
* | Enter, F2 | Moves into the cell's controls. Escape moves back to the cell, and Tab moves between the controls of one cell. |
|
|
57
61
|
* | Enter, Space on a sortable heading | Sorts by the column. |
|
|
58
62
|
* | Enter, Space on the `load-more` row | Sends `helix-load-more`. |
|
|
59
|
-
* | Space on a row | Selects the row, in a `selectable` table. |
|
|
63
|
+
* | Space on a row | Selects the row, in a `selectable` table. Under `selection-behavior="replace"`, selects it alone, and Control or Command with Space adds or takes out the row. |
|
|
64
|
+
* | 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. |
|
|
60
65
|
* | Shift with Down or Up | Selects the rows it moves across, in a `selectable` table. |
|
|
61
66
|
* | Shift with Space | Selects every row from the one last chosen, in a `selectable` table. |
|
|
62
67
|
* | Control or Command with A | Selects every row, in a `selectable` table. |
|
|
@@ -123,7 +128,9 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
|
|
|
123
128
|
* @documentation https://fusion.dev/helix/components/data-table
|
|
124
129
|
*
|
|
125
130
|
* @slot - The columns, as `TableColumn` elements.
|
|
131
|
+
* @slot empty - Content shown when there are no rows to show and none are loading, such as a picture and a link. Falls back to `empty-text`, which the live region still says.
|
|
126
132
|
* @slot footer - Content below the grid. It replaces the built-in pagination.
|
|
133
|
+
* @slot loading - Content shown while the rows load, such as a spinner. Falls back to `loading-text`, which the live region still says.
|
|
127
134
|
* @slot toolbar - Content above the grid, such as a search field or actions.
|
|
128
135
|
*
|
|
129
136
|
* @event {HelixActivateEvent} helix-activate - Fired after the page changes, with the new page as a string in `detail.value`.
|
|
@@ -216,6 +223,8 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
|
|
|
216
223
|
* `registerWords('de', { 'helix-data-table': { nextLabel: 'Nächste Seite' } })`.
|
|
217
224
|
*/
|
|
218
225
|
static words: string;
|
|
226
|
+
/** The empty and loading lines are the `empty` and `loading` slots. */
|
|
227
|
+
protected statusSlots: boolean;
|
|
219
228
|
/**
|
|
220
229
|
* The word for an ascending sort, in a `sorted-label` that writes `{order}`.
|
|
221
230
|
*
|
|
@@ -490,6 +499,15 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
|
|
|
490
499
|
* {# wybranych} other {# wybranego}}`.
|
|
491
500
|
*/
|
|
492
501
|
selectedLabel: string;
|
|
502
|
+
/**
|
|
503
|
+
* What a key or a press on a row does to the rows already selected, in a
|
|
504
|
+
* `selectable` table. `toggle`, the default, keeps them: Space adds or takes
|
|
505
|
+
* out the row. `replace` selects the row alone, as a file list does: Down and
|
|
506
|
+
* Up carry the selection with them, and a press on a row selects it. Control
|
|
507
|
+
* or Command with a press or Space adds or takes out one row, and with a move
|
|
508
|
+
* goes without selecting. The checkboxes add or take out one row either way.
|
|
509
|
+
*/
|
|
510
|
+
selectionBehavior: 'replace' | 'toggle';
|
|
493
511
|
/**
|
|
494
512
|
* The name of a row's checkbox. `{row}` is replaced with the row's value in
|
|
495
513
|
* the first column that has a field, or with its key.
|
|
@@ -954,7 +972,7 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
|
|
|
954
972
|
*/
|
|
955
973
|
toggleSort(field: string): boolean;
|
|
956
974
|
/**
|
|
957
|
-
* The columns into the header row, everything else into the
|
|
975
|
+
* The columns into the header row, everything else into the slot it names.
|
|
958
976
|
*
|
|
959
977
|
* Manual assignment, so a consumer's own markup is never written to.
|
|
960
978
|
*/
|
|
@@ -1124,6 +1142,20 @@ export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base
|
|
|
1124
1142
|
* request carries only the state it asks for.
|
|
1125
1143
|
*/
|
|
1126
1144
|
private readonly notePress;
|
|
1145
|
+
/** Whether a press or a move on a row selects it alone. */
|
|
1146
|
+
private get replaces();
|
|
1147
|
+
/**
|
|
1148
|
+
* Selects the row at `row` on the page alone, and makes it where a range
|
|
1149
|
+
* runs from. Asks nothing where it is already the only one selected.
|
|
1150
|
+
*/
|
|
1151
|
+
private chooseOnly;
|
|
1152
|
+
/**
|
|
1153
|
+
* A press on a row under `selection-behavior="replace"`: it selects the row
|
|
1154
|
+
* alone, Control or Command adds or takes out the row, and Shift selects
|
|
1155
|
+
* every row from the one last chosen. A press on a control in a cell, or on
|
|
1156
|
+
* the checkbox or grip column, is the control's.
|
|
1157
|
+
*/
|
|
1158
|
+
private readonly handleRowClick;
|
|
1127
1159
|
/**
|
|
1128
1160
|
* A click on a sortable heading, anywhere but its grip or its edge.
|
|
1129
1161
|
*
|
|
@@ -30,6 +30,7 @@ const isControl = (element) => element.matches(CONTROL) || element.shadowRoot?.d
|
|
|
30
30
|
let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollection(HelixElement) {
|
|
31
31
|
constructor() {
|
|
32
32
|
super(...arguments);
|
|
33
|
+
this.statusSlots = true;
|
|
33
34
|
this.ascendingLabel = 'ascending';
|
|
34
35
|
this.canceledLabel = 'Canceled';
|
|
35
36
|
this.defaultExpanded = '';
|
|
@@ -64,6 +65,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
|
|
|
64
65
|
this.selectable = false;
|
|
65
66
|
this.selectAllLabel = 'Select every row';
|
|
66
67
|
this.selectedLabel = '{count} selected';
|
|
68
|
+
this.selectionBehavior = 'toggle';
|
|
67
69
|
this.selectRowLabel = 'Select {row}';
|
|
68
70
|
this.sortedLabel = '{direction, select, ascending {Sorted by {column}, ascending} descending {Sorted by {column}, descending} other {Sorted by {column}}}';
|
|
69
71
|
this.total = 0;
|
|
@@ -178,6 +180,30 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
|
|
|
178
180
|
this.notePress = (event) => {
|
|
179
181
|
this.shiftPressed = event.shiftKey;
|
|
180
182
|
};
|
|
183
|
+
this.handleRowClick = (event) => {
|
|
184
|
+
if (!this.replaces || event.defaultPrevented) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const place = this.placeOf(event);
|
|
188
|
+
if (!place || place.row < 0 || place.row >= this.drawnPage.length || place.column < this.lead) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const origin = event.composedPath()[0];
|
|
192
|
+
if (origin instanceof Node && this.controlsIn(place).some((one) => deepContains(one, origin))) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (event.shiftKey && this.anchorKey !== undefined) {
|
|
196
|
+
this.selectRange(place.row);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (event.ctrlKey || event.metaKey) {
|
|
200
|
+
const data = this.drawnPage[place.row];
|
|
201
|
+
const key = this.keyOf(data, this.rowIndex(place.row) - 2);
|
|
202
|
+
this.chooseRow(key, !this.chosen.has(key));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
this.chooseOnly(place.row);
|
|
206
|
+
};
|
|
181
207
|
this.handleHeadClick = (event) => {
|
|
182
208
|
if (this.partIn(event, 'grip') || this.partIn(event, 'resizer')) {
|
|
183
209
|
return;
|
|
@@ -251,14 +277,22 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
|
|
|
251
277
|
}
|
|
252
278
|
const { column, row } = to;
|
|
253
279
|
event.preventDefault();
|
|
280
|
+
const onRow = row >= 0 && row < this.drawnPage.length;
|
|
254
281
|
if (event.shiftKey &&
|
|
255
282
|
this.selectable &&
|
|
256
283
|
(event.key === 'ArrowUp' || event.key === 'ArrowDown') &&
|
|
257
284
|
place.row >= 0 &&
|
|
258
|
-
|
|
259
|
-
row < this.drawnPage.length) {
|
|
285
|
+
onRow) {
|
|
260
286
|
this.selectRange(row, place.row);
|
|
261
287
|
}
|
|
288
|
+
else if (this.replaces &&
|
|
289
|
+
onRow &&
|
|
290
|
+
row !== place.row &&
|
|
291
|
+
!event.shiftKey &&
|
|
292
|
+
!event.ctrlKey &&
|
|
293
|
+
!event.metaKey) {
|
|
294
|
+
this.chooseOnly(row);
|
|
295
|
+
}
|
|
262
296
|
if (row !== place.row || column !== place.column) {
|
|
263
297
|
this.moveTo(row, column);
|
|
264
298
|
}
|
|
@@ -878,6 +912,8 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
|
|
|
878
912
|
for (const [name, wanted] of [
|
|
879
913
|
['toolbar', true],
|
|
880
914
|
['footer', true],
|
|
915
|
+
['empty', true],
|
|
916
|
+
['loading', true],
|
|
881
917
|
]) {
|
|
882
918
|
const slot = root.querySelector(`slot[name="${name}"]`);
|
|
883
919
|
if (slot?.assign && wanted) {
|
|
@@ -1201,6 +1237,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
|
|
|
1201
1237
|
connectedCallback() {
|
|
1202
1238
|
super.connectedCallback();
|
|
1203
1239
|
this.addEventListener('click', this.handleHeadClick);
|
|
1240
|
+
this.addEventListener('click', this.handleRowClick);
|
|
1204
1241
|
this.addEventListener('pointerdown', this.notePress, true);
|
|
1205
1242
|
this.addEventListener('keydown', this.notePress, true);
|
|
1206
1243
|
this.addEventListener('focusin', this.handleFocusIn);
|
|
@@ -1212,6 +1249,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
|
|
|
1212
1249
|
disconnectedCallback() {
|
|
1213
1250
|
super.disconnectedCallback();
|
|
1214
1251
|
this.removeEventListener('click', this.handleHeadClick);
|
|
1252
|
+
this.removeEventListener('click', this.handleRowClick);
|
|
1215
1253
|
this.removeEventListener('pointerdown', this.notePress, true);
|
|
1216
1254
|
this.removeEventListener('keydown', this.notePress, true);
|
|
1217
1255
|
this.removeEventListener('focusin', this.handleFocusIn);
|
|
@@ -1220,6 +1258,22 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
|
|
|
1220
1258
|
this.removeEventListener('keydown', this.handleGripKey);
|
|
1221
1259
|
this.removeEventListener('helix-before-resize', this.handleResize);
|
|
1222
1260
|
}
|
|
1261
|
+
get replaces() {
|
|
1262
|
+
return this.selectable && this.selectionBehavior === 'replace';
|
|
1263
|
+
}
|
|
1264
|
+
chooseOnly(row) {
|
|
1265
|
+
const data = this.drawnPage[row];
|
|
1266
|
+
if (!data) {
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
const key = this.keyOf(data, this.rowIndex(row) - 2);
|
|
1270
|
+
this.anchorKey = key;
|
|
1271
|
+
const now = this.chosen;
|
|
1272
|
+
if (now.size === 1 && now.has(key)) {
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
this.requestSelected(new Set([key]));
|
|
1276
|
+
}
|
|
1223
1277
|
cellAt(row, column) {
|
|
1224
1278
|
const offset = this.lead;
|
|
1225
1279
|
if (row === -1) {
|
|
@@ -1402,6 +1456,10 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
|
|
|
1402
1456
|
event.preventDefault();
|
|
1403
1457
|
this.selectRange(place.row);
|
|
1404
1458
|
}
|
|
1459
|
+
else if (this.replaces && row && !event.ctrlKey && !event.metaKey) {
|
|
1460
|
+
event.preventDefault();
|
|
1461
|
+
this.chooseOnly(place.row);
|
|
1462
|
+
}
|
|
1405
1463
|
else if (this.selectable && row) {
|
|
1406
1464
|
event.preventDefault();
|
|
1407
1465
|
const key = this.keyOf(row, this.rowIndex(place.row) - 2);
|
|
@@ -1595,6 +1653,9 @@ __decorate([
|
|
|
1595
1653
|
words,
|
|
1596
1654
|
property({ attribute: 'selected-label' })
|
|
1597
1655
|
], HelixHeadlessDataTable.prototype, "selectedLabel", void 0);
|
|
1656
|
+
__decorate([
|
|
1657
|
+
property({ attribute: 'selection-behavior' })
|
|
1658
|
+
], HelixHeadlessDataTable.prototype, "selectionBehavior", void 0);
|
|
1598
1659
|
__decorate([
|
|
1599
1660
|
words,
|
|
1600
1661
|
property({ attribute: 'select-row-label' })
|