@fusion.dev/helix-headless 0.218.0 → 0.219.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/api.json +1 -1
- package/custom-elements.json +1 -1
- package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +13 -2
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +19 -9
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +16 -1
- package/dist/breadcrumbs/root/breadcrumbs-root.js +6 -0
- package/dist/button/root/button-root.d.ts +33 -0
- package/dist/button/root/button-root.js +13 -3
- package/dist/canvas/root/canvas-root.d.ts +11 -0
- package/dist/canvas/root/canvas-root.js +1 -0
- package/dist/chat/composer/chat-composer.d.ts +221 -6
- package/dist/chat/composer/chat-composer.js +249 -19
- package/dist/chat/message/chat-message.d.ts +7 -0
- package/dist/chat/message/chat-message.js +2 -1
- package/dist/chat/root/chat-root.d.ts +1 -0
- package/dist/chat/root/chat-root.js +1 -1
- package/dist/copy-button/root/copy-button-root.js +3 -3
- package/dist/device-screen/root/device-screen-root.d.ts +11 -0
- package/dist/device-screen/root/device-screen-root.js +22 -10
- package/dist/dialog/root/dialog-root.js +6 -1
- package/dist/drilldown/item/drilldown-item.js +1 -1
- package/dist/element-events.generated.js +1 -0
- package/dist/filter-input/root/filter-input-root.d.ts +4 -3
- package/dist/filter-input/root/filter-input-root.js +1 -20
- package/dist/graph/root/graph-root.d.ts +11 -0
- package/dist/graph/root/graph-root.js +4 -1
- package/dist/input/root/input-root.d.ts +6 -0
- package/dist/input/root/input-root.js +7 -0
- package/dist/lightbox/root/lightbox-root.d.ts +4 -1
- package/dist/lightbox/root/lightbox-root.js +10 -8
- package/dist/listbox/root/listbox-root.d.ts +21 -0
- package/dist/listbox/root/listbox-root.js +37 -2
- package/dist/log/group/log-group.d.ts +7 -1
- package/dist/log/group/log-group.js +14 -1
- package/dist/log/root/log-root.d.ts +13 -3
- package/dist/log/root/log-root.js +5 -2
- package/dist/map/root/map-root.d.ts +11 -0
- package/dist/map/root/map-root.js +28 -12
- package/dist/menu/root/menu-root.d.ts +8 -0
- package/dist/menu/root/menu-root.js +22 -1
- package/dist/menu/section/menu-section.d.ts +10 -0
- package/dist/menu/section/menu-section.js +10 -0
- package/dist/meter/index.d.ts +1 -0
- package/dist/meter/index.js +1 -0
- package/dist/meter/root/meter-root.d.ts +16 -1
- package/dist/meter/root/meter-root.js +27 -11
- package/dist/meter/segment/meter-segment.d.ts +74 -0
- package/dist/meter/segment/meter-segment.js +47 -0
- package/dist/scroll-area/root/scroll-area-root.d.ts +20 -3
- package/dist/scroll-area/root/scroll-area-root.js +23 -4
- package/dist/spatial-bar-chart/bars.d.ts +125 -5
- package/dist/spatial-bar-chart/bars.js +194 -17
- package/dist/spatial-bar-chart/root/spatial-bar-chart-root.d.ts +148 -6
- package/dist/spatial-bar-chart/root/spatial-bar-chart-root.js +285 -16
- package/dist/splitter/root/splitter-root.js +1 -0
- package/dist/tabs/root/tabs-root.d.ts +4 -0
- package/dist/tabs/tab/tab.d.ts +1 -0
- package/dist/tabs/tab/tab.js +2 -2
- package/dist/tag/root/tag-root.d.ts +1 -1
- package/dist/terminal/root/terminal-root.d.ts +11 -0
- package/dist/terminal/root/terminal-root.js +23 -10
- package/dist/toolbar/root/toolbar-root.d.ts +4 -1
- package/dist/tooltip/root/tooltip-root.d.ts +23 -1
- package/dist/tooltip/root/tooltip-root.js +28 -5
- package/dist/tour/root/tour-root.d.ts +1 -0
- package/dist/tour/root/tour-root.js +4 -1
- package/dist/tour/stop/tour-stop.js +1 -0
- package/examples.json +1 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +1373 -52
- package/types/svelte.d.ts +1 -0
- package/types/vue.d.ts +312 -17
- package/types/words.d.ts +9 -1
|
@@ -37,7 +37,7 @@ import { type CSSResultGroup, nothing, type PropertyValues, type TemplateResult
|
|
|
37
37
|
* @slot end - Content after the label and outside the link, such as a switcher's
|
|
38
38
|
* menu trigger, a count, or a mark. It can hold a control, and it is not read as
|
|
39
39
|
* part of the link's accessible name.
|
|
40
|
-
* @slot separator - Replaces the
|
|
40
|
+
* @slot separator - Replaces the mark shown before this item, a chevron or the trail's `separator`.
|
|
41
41
|
* @slot start - Content before the label, such as an icon.
|
|
42
42
|
*
|
|
43
43
|
* @event {HelixSelectEvent} helix-select - A plain press on the crumb's link, with its `href` as the value, before the browser follows it. Cancelable: cancel it to keep the browser from following, as a client-side router does.
|
|
@@ -46,7 +46,7 @@ import { type CSSResultGroup, nothing, type PropertyValues, type TemplateResult
|
|
|
46
46
|
* @csspart fold-separator - The chevron before that button.
|
|
47
47
|
* @csspart link - The crumb itself, which is an `<a>` where `href` is set.
|
|
48
48
|
* @csspart separator - The separator preceding the item.
|
|
49
|
-
* @csspart separator-mark - The chevron between crumbs, shown when no separator is slotted.
|
|
49
|
+
* @csspart separator-mark - The chevron between crumbs, or the slash under the trail's `separator="slash"`, shown when no separator is slotted.
|
|
50
50
|
*
|
|
51
51
|
* @cssstate current - While the crumb is the current page, marked `current` or the last of a trail with none marked.
|
|
52
52
|
* @cssstate end - While the `end` slot holds something, so a stylesheet can join
|
|
@@ -96,6 +96,11 @@ export declare class HelixHeadlessBreadcrumb extends HelixElement {
|
|
|
96
96
|
* `current`, so this one is said to be the current page.
|
|
97
97
|
*/
|
|
98
98
|
implied: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* @internal Set by the trail from its `separator`: the mark this crumb draws
|
|
101
|
+
* before itself and before its fold button.
|
|
102
|
+
*/
|
|
103
|
+
mark: 'chevron' | 'slash';
|
|
99
104
|
/** @internal Set by the trail: what a press on the fold's button does. */
|
|
100
105
|
onFold: (() => void) | undefined;
|
|
101
106
|
/**
|
|
@@ -140,6 +145,12 @@ export declare class HelixHeadlessBreadcrumb extends HelixElement {
|
|
|
140
145
|
* without reimplementing the crumb, its link and its current-page branch.
|
|
141
146
|
*/
|
|
142
147
|
protected renderSeparator(): TemplateResult;
|
|
148
|
+
/**
|
|
149
|
+
* The trail's mark: a chevron, mirrored on a right-to-left page because it
|
|
150
|
+
* points the way the trail runs, or a slash, which is a character and is
|
|
151
|
+
* written the same way in either direction.
|
|
152
|
+
*/
|
|
153
|
+
private renderMark;
|
|
143
154
|
protected render(): TemplateResult;
|
|
144
155
|
}
|
|
145
156
|
declare global {
|
|
@@ -12,6 +12,7 @@ let HelixHeadlessBreadcrumb = class HelixHeadlessBreadcrumb extends HelixElement
|
|
|
12
12
|
this.folded = false;
|
|
13
13
|
this.foldLabel = '';
|
|
14
14
|
this.implied = false;
|
|
15
|
+
this.mark = 'chevron';
|
|
15
16
|
this.onFold = undefined;
|
|
16
17
|
this.rel = '';
|
|
17
18
|
this.target = '';
|
|
@@ -48,10 +49,7 @@ let HelixHeadlessBreadcrumb = class HelixHeadlessBreadcrumb extends HelixElement
|
|
|
48
49
|
if (this.fold === 0) {
|
|
49
50
|
return nothing;
|
|
50
51
|
}
|
|
51
|
-
return html `<span part="fold-separator" aria-hidden="true"
|
|
52
|
-
><svg data-mirror fill="currentColor" viewBox=${caretRight.viewBox}>
|
|
53
|
-
${unsafeSVG(caretRight.content)}
|
|
54
|
-
</svg></span
|
|
52
|
+
return html `<span part="fold-separator" aria-hidden="true">${this.renderMark()}</span
|
|
55
53
|
><button part="expand" type="button" aria-label=${this.foldLabel} @click=${() => this.onFold?.()}>
|
|
56
54
|
…
|
|
57
55
|
</button>`;
|
|
@@ -64,11 +62,20 @@ let HelixHeadlessBreadcrumb = class HelixHeadlessBreadcrumb extends HelixElement
|
|
|
64
62
|
this.toggleState('end', slotFilled(slotNamed(this.renderRoot, 'end')));
|
|
65
63
|
}
|
|
66
64
|
renderSeparator() {
|
|
67
|
-
return html `<slot name="separator"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
return html `<slot name="separator">${this.renderMark(true)}</slot>`;
|
|
66
|
+
}
|
|
67
|
+
renderMark(named = false) {
|
|
68
|
+
if (this.mark === 'slash') {
|
|
69
|
+
return named
|
|
70
|
+
? html `<span part="separator-mark" data-mark="slash">/</span>`
|
|
71
|
+
: html `<span data-mark="slash">/</span>`;
|
|
72
|
+
}
|
|
73
|
+
const chevron = unsafeSVG(caretRight.content);
|
|
74
|
+
return named
|
|
75
|
+
? html `<svg part="separator-mark" data-mirror fill="currentColor" viewBox=${caretRight.viewBox}>
|
|
76
|
+
${chevron}
|
|
77
|
+
</svg>`
|
|
78
|
+
: html `<svg data-mirror fill="currentColor" viewBox=${caretRight.viewBox}>${chevron}</svg>`;
|
|
72
79
|
}
|
|
73
80
|
render() {
|
|
74
81
|
const label = html `<slot name="start"></slot><slot></slot>`;
|
|
@@ -105,6 +112,9 @@ __decorate([
|
|
|
105
112
|
__decorate([
|
|
106
113
|
property({ attribute: false })
|
|
107
114
|
], HelixHeadlessBreadcrumb.prototype, "implied", void 0);
|
|
115
|
+
__decorate([
|
|
116
|
+
property({ attribute: false })
|
|
117
|
+
], HelixHeadlessBreadcrumb.prototype, "mark", void 0);
|
|
108
118
|
__decorate([
|
|
109
119
|
property()
|
|
110
120
|
], HelixHeadlessBreadcrumb.prototype, "rel", void 0);
|
|
@@ -64,7 +64,7 @@ export interface BreadcrumbItem {
|
|
|
64
64
|
* @csspart crumb-fold-separator - The chevron before that button.
|
|
65
65
|
* @csspart crumb-link - The link, or the current page's words, of a crumb drawn from `items`.
|
|
66
66
|
* @csspart crumb-separator - The separator before a crumb drawn from `items`.
|
|
67
|
-
* @csspart crumb-separator-mark - The chevron before a crumb drawn from `items`.
|
|
67
|
+
* @csspart crumb-separator-mark - The chevron, or the slash under `separator="slash"`, before a crumb drawn from `items`.
|
|
68
68
|
* @csspart expand - The button in the folded crumb that shows the whole trail.
|
|
69
69
|
* @csspart list - The ordered list.
|
|
70
70
|
*/
|
|
@@ -100,6 +100,21 @@ export declare class HelixHeadlessBreadcrumbs extends HelixElement {
|
|
|
100
100
|
* 2, so the first crumb and the current page always show.
|
|
101
101
|
*/
|
|
102
102
|
maxItems?: number;
|
|
103
|
+
/**
|
|
104
|
+
* The mark between one crumb and the next, for the whole trail. Unset is a
|
|
105
|
+
* chevron; `slash` is a slash, as a path or a repository and its branch is
|
|
106
|
+
* written.
|
|
107
|
+
*
|
|
108
|
+
* **On the trail rather than on each crumb**, because the marks are one
|
|
109
|
+
* decision: a trail whose separators differ from crumb to crumb is a
|
|
110
|
+
* mistake, and a trail drawn from `items` has no crumb of yours to set it
|
|
111
|
+
* on. A crumb's own `separator` slot still replaces its mark, for the rare
|
|
112
|
+
* crumb that needs something else.
|
|
113
|
+
*
|
|
114
|
+
* Optional and reflected, per the rule that a reflected property takes no
|
|
115
|
+
* default, so a stylesheet can select on it.
|
|
116
|
+
*/
|
|
117
|
+
separator?: 'slash';
|
|
103
118
|
/** Whether a reader has asked to see the whole of a folded trail. */
|
|
104
119
|
private expanded;
|
|
105
120
|
/** The crumbs slotted into the trail, in order. */
|
|
@@ -43,6 +43,7 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
|
|
|
43
43
|
crumb.fold = index === 0 ? fold : 0;
|
|
44
44
|
crumb.foldLabel = index === 0 ? label : '';
|
|
45
45
|
crumb.onFold = index === 0 && fold > 0 ? this.unfold : undefined;
|
|
46
|
+
crumb.mark = this.separator ?? 'chevron';
|
|
46
47
|
});
|
|
47
48
|
});
|
|
48
49
|
};
|
|
@@ -87,6 +88,7 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
|
|
|
87
88
|
: html `<${crumb}
|
|
88
89
|
part="crumb crumb-collapsed"
|
|
89
90
|
exportparts="expand: crumb-expand, fold-separator: crumb-fold-separator, link: crumb-link, separator: crumb-separator, separator-mark: crumb-separator-mark"
|
|
91
|
+
.mark=${this.separator ?? 'chevron'}
|
|
90
92
|
><button
|
|
91
93
|
part="expand"
|
|
92
94
|
type="button"
|
|
@@ -105,6 +107,7 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
|
|
|
105
107
|
rel=${item.rel ?? nothing}
|
|
106
108
|
target=${item.target ?? nothing}
|
|
107
109
|
?current=${index === last}
|
|
110
|
+
.mark=${this.separator ?? 'chevron'}
|
|
108
111
|
>${item.label}</${crumb}
|
|
109
112
|
>`;
|
|
110
113
|
return html `${repeat(first, ({ index }) => index, draw)}${fold}${repeat(rest, ({ index }) => index, draw)}`;
|
|
@@ -129,6 +132,9 @@ __decorate([
|
|
|
129
132
|
__decorate([
|
|
130
133
|
property({ attribute: 'max-items', converter: numberConverter('max-items', undefined) })
|
|
131
134
|
], HelixHeadlessBreadcrumbs.prototype, "maxItems", void 0);
|
|
135
|
+
__decorate([
|
|
136
|
+
property({ reflect: true })
|
|
137
|
+
], HelixHeadlessBreadcrumbs.prototype, "separator", void 0);
|
|
132
138
|
__decorate([
|
|
133
139
|
state()
|
|
134
140
|
], HelixHeadlessBreadcrumbs.prototype, "expanded", void 0);
|
|
@@ -35,6 +35,10 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
|
35
35
|
* Set `pending` while the action a press started is still running. The button
|
|
36
36
|
* keeps focus and refuses presses, and a reader on it hears `pending-label`.
|
|
37
37
|
*
|
|
38
|
+
* Set `focusable-when-disabled` on a disabled button whose reason a Tooltip
|
|
39
|
+
* gives. It stays in the tab order and takes hover, so the tooltip opens, and
|
|
40
|
+
* it refuses every press.
|
|
41
|
+
*
|
|
38
42
|
* Every press that acts sends `helix-press` with what pressed it in
|
|
39
43
|
* `detail.pointerType`: `mouse`, `pen`, `touch`, `keyboard`, or `virtual` for
|
|
40
44
|
* a screen reader's press or a script's `click()`, as React Aria's `onPress`
|
|
@@ -146,6 +150,35 @@ export declare class HelixHeadlessButton extends HelixFormElementBase {
|
|
|
146
150
|
* anything a consumer sets.
|
|
147
151
|
*/
|
|
148
152
|
focusable: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Whether a disabled button stays in the tab order, so a tooltip around it
|
|
155
|
+
* can say why it is disabled.
|
|
156
|
+
*
|
|
157
|
+
* Set it on a button whose reason a Tooltip gives, such as a Save with no
|
|
158
|
+
* changes to save. While the button is disabled, it keeps focus and takes
|
|
159
|
+
* hover, and it refuses every press: a click, Enter and Space, and Enter in
|
|
160
|
+
* a field of its form. The click does not reach your listeners, and a submit
|
|
161
|
+
* button submits nothing. The inner control carries `aria-disabled` in place
|
|
162
|
+
* of the native `disabled`, so a screen reader still says it is unavailable.
|
|
163
|
+
* The styled button keeps the disabled look.
|
|
164
|
+
*
|
|
165
|
+
* Leave it unset on a button with no reason to give. A plain disabled button
|
|
166
|
+
* leaves the tab order, as a native one does, and a keyboard reader skips a
|
|
167
|
+
* stop that does nothing. Set `pending` for the wait after a press.
|
|
168
|
+
*/
|
|
169
|
+
focusableWhenDisabled: boolean;
|
|
170
|
+
/**
|
|
171
|
+
* @internal Whether the button is disabled and kept in the tab order by
|
|
172
|
+
* `focusableWhenDisabled`, so the inner control takes `aria-disabled` and
|
|
173
|
+
* refuses presses itself.
|
|
174
|
+
*/
|
|
175
|
+
protected get heldDisabled(): boolean;
|
|
176
|
+
/**
|
|
177
|
+
* @internal Whether the inner control refuses a press it still receives:
|
|
178
|
+
* the button is pending, or disabled and kept in the tab order. A subclass
|
|
179
|
+
* that draws its own control, as `CopyButton` does, binds to it.
|
|
180
|
+
*/
|
|
181
|
+
protected get refusesPress(): boolean;
|
|
149
182
|
/**
|
|
150
183
|
* Where this button's submission goes, overriding the form's own.
|
|
151
184
|
*
|
|
@@ -30,6 +30,7 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElementBase
|
|
|
30
30
|
this.command = '';
|
|
31
31
|
this.commandFor = '';
|
|
32
32
|
this.focusable = true;
|
|
33
|
+
this.focusableWhenDisabled = false;
|
|
33
34
|
this.formAction = '';
|
|
34
35
|
this.formEnctype = '';
|
|
35
36
|
this.formMethod = '';
|
|
@@ -48,7 +49,7 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElementBase
|
|
|
48
49
|
}
|
|
49
50
|
};
|
|
50
51
|
this.handleClick = (event) => {
|
|
51
|
-
if (this.
|
|
52
|
+
if (this.refusesPress) {
|
|
52
53
|
event.preventDefault();
|
|
53
54
|
event.stopImmediatePropagation();
|
|
54
55
|
return;
|
|
@@ -83,6 +84,12 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElementBase
|
|
|
83
84
|
get forwardsLabel() {
|
|
84
85
|
return true;
|
|
85
86
|
}
|
|
87
|
+
get heldDisabled() {
|
|
88
|
+
return this.isDisabled && this.focusableWhenDisabled;
|
|
89
|
+
}
|
|
90
|
+
get refusesPress() {
|
|
91
|
+
return this.pending || this.heldDisabled;
|
|
92
|
+
}
|
|
86
93
|
sayPending() {
|
|
87
94
|
const focused = this.renderRoot?.activeElement != null;
|
|
88
95
|
if (!focused && this.status.said === '') {
|
|
@@ -207,11 +214,11 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElementBase
|
|
|
207
214
|
aria-expanded=${this.expanded === undefined ? nothing : String(this.expanded)}
|
|
208
215
|
aria-haspopup=${this.popup ?? nothing}
|
|
209
216
|
aria-busy=${this.pending ? 'true' : nothing}
|
|
210
|
-
aria-disabled=${this.
|
|
217
|
+
aria-disabled=${this.refusesPress ? 'true' : nothing}
|
|
211
218
|
aria-label=${this.label || nothing}
|
|
212
219
|
command=${this.command || nothing}
|
|
213
220
|
tabindex=${stop}
|
|
214
|
-
?disabled=${this.isDisabled}
|
|
221
|
+
?disabled=${this.isDisabled && !this.heldDisabled}
|
|
215
222
|
@click=${this.handleClick}
|
|
216
223
|
@keydown=${this.notePress}
|
|
217
224
|
@pointerdown=${this.notePress}
|
|
@@ -235,6 +242,9 @@ __decorate([
|
|
|
235
242
|
__decorate([
|
|
236
243
|
property({ attribute: false })
|
|
237
244
|
], HelixHeadlessButton.prototype, "focusable", void 0);
|
|
245
|
+
__decorate([
|
|
246
|
+
property({ attribute: 'focusable-when-disabled', type: Boolean })
|
|
247
|
+
], HelixHeadlessButton.prototype, "focusableWhenDisabled", void 0);
|
|
238
248
|
__decorate([
|
|
239
249
|
property({ attribute: 'formaction' })
|
|
240
250
|
], HelixHeadlessButton.prototype, "formAction", void 0);
|
|
@@ -2,6 +2,7 @@ import { type CanvasView, HelixElement, type ViewportPanDetail, type ViewportWay
|
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import type { StaticValue } from 'lit/static-html.js';
|
|
4
4
|
import '../../icon-button/index.js';
|
|
5
|
+
import '../../tooltip/index.js';
|
|
5
6
|
import { type CanvasArrangeOptions } from '../arrange.js';
|
|
6
7
|
import { type CanvasDocument, type CanvasRoute, type CanvasTone } from './canvas-document.js';
|
|
7
8
|
export type { CanvasArrangeOptions } from '../arrange.js';
|
|
@@ -210,6 +211,16 @@ declare const HelixHeadlessCanvas_base: (new (...args: any[]) => import("@fusion
|
|
|
210
211
|
* @csspart pan-up-spinner - The ring `pan-up` turns while it is pending.
|
|
211
212
|
* @csspart pan-up-text - The box `pan-up`'s glyph sits in.
|
|
212
213
|
* @csspart region - The named region the board is drawn in.
|
|
214
|
+
* @csspart tooltip-arrow - The arrow of the tooltip that names each button, forwarded from `Tooltip`.
|
|
215
|
+
* @csspart tooltip-arrow-bottom - The arrow while the bubble is below its button, on the bubble's top edge. Set together with `tooltip-arrow`.
|
|
216
|
+
* @csspart tooltip-arrow-left - The arrow while the bubble is left of its button, on the bubble's right edge. Set together with `tooltip-arrow`.
|
|
217
|
+
* @csspart tooltip-arrow-right - The arrow while the bubble is right of its button, on the bubble's left edge. Set together with `tooltip-arrow`.
|
|
218
|
+
* @csspart tooltip-arrow-top - The arrow while the bubble is above its button, on the bubble's bottom edge. Set together with `tooltip-arrow`.
|
|
219
|
+
* @csspart tooltip-surface - The bubble of the tooltip that names each button, forwarded from `Tooltip`.
|
|
220
|
+
* @csspart tooltip-surface-bottom - The bubble while it is below its button. Set together with `tooltip-surface`.
|
|
221
|
+
* @csspart tooltip-surface-left - The bubble while it is left of its button. Set together with `tooltip-surface`.
|
|
222
|
+
* @csspart tooltip-surface-right - The bubble while it is right of its button. Set together with `tooltip-surface`.
|
|
223
|
+
* @csspart tooltip-surface-top - The bubble while it is above its button. Set together with `tooltip-surface`.
|
|
213
224
|
* @csspart viewport - The box the board is drawn inside.
|
|
214
225
|
* @csspart zoom-in - The button that zooms in.
|
|
215
226
|
* @csspart zoom-in-button - `zoom-in`'s inner button.
|
|
@@ -3,6 +3,7 @@ import { assignSlots, auditName, capturePointer, customElement, formatPercent, f
|
|
|
3
3
|
import { html, nothing, svg } from 'lit';
|
|
4
4
|
import { property, state } from 'lit/decorators.js';
|
|
5
5
|
import '../../icon-button/index.js';
|
|
6
|
+
import '../../tooltip/index.js';
|
|
6
7
|
import { arrangement } from '../arrange.js';
|
|
7
8
|
import { descriptionOf } from '../describe.js';
|
|
8
9
|
import { HelixHeadlessCanvasEdge } from '../edge/canvas-edge.js';
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { HelixFormElement } from '@fusion.dev/helix-core';
|
|
2
|
-
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
2
|
+
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
+
import '../../tooltip/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* How long `example` shows before the next one replaces it, in milliseconds:
|
|
6
|
+
* `EXAMPLE_SHOWN`, and `EXAMPLE_PER_CHARACTER` more for each character past
|
|
7
|
+
* `EXAMPLE_SHORT`, up to `EXAMPLE_LONGEST`. A longer example takes longer to
|
|
8
|
+
* read.
|
|
9
|
+
*/
|
|
10
|
+
export declare function exampleShown(example: string): number;
|
|
3
11
|
declare const HelixHeadlessChatComposer_base: (new (...args: any[]) => import("@fusion.dev/helix-core").VoiceElement) & typeof HelixFormElement;
|
|
4
12
|
/**
|
|
5
13
|
* @summary A field for writing a turn in a conversation.
|
|
6
14
|
*
|
|
7
15
|
* @description
|
|
8
16
|
* The composer is a separate element so you can replace it. If you need
|
|
9
|
-
*
|
|
10
|
-
*
|
|
17
|
+
* slash commands or a layout of your own, replace the composer and keep the
|
|
18
|
+
* transcript.
|
|
11
19
|
*
|
|
12
20
|
* By default, Enter sends and Shift+Enter starts a new line, the convention in
|
|
13
21
|
* messaging apps. Document editors mostly use the opposite, so `submit-on`
|
|
@@ -47,33 +55,71 @@ declare const HelixHeadlessChatComposer_base: (new (...args: any[]) => import("@
|
|
|
47
55
|
* `typing-timeout` milliseconds. Pass it on to show "is typing" to the other
|
|
48
56
|
* side.
|
|
49
57
|
*
|
|
58
|
+
* **`busy` says a reply is on its way**, and turns the send button into a
|
|
59
|
+
* stop button, named by `stop-label`, that sends `helix-stop`. The field
|
|
60
|
+
* stays open so the reader can write the next turn, and Enter does not send
|
|
61
|
+
* it until `busy` is cleared. With `queue`, Enter sends while `busy` too, for
|
|
62
|
+
* a page that holds a turn until the reply ends or hands it to the reply
|
|
63
|
+
* under way; the button stays the stop button.
|
|
64
|
+
*
|
|
65
|
+
* **The `idle` slot takes the send button's place while there is nothing to
|
|
66
|
+
* send**: the field is empty, `attached` is not set, and no reply is on its
|
|
67
|
+
* way. Put the action an empty draft offers there, such as a button that
|
|
68
|
+
* starts a voice conversation. The send button comes back as soon as there
|
|
69
|
+
* is a draft. Where focus was on the one that goes, it moves to the one that
|
|
70
|
+
* comes, so a keyboard reader stays in the same place.
|
|
71
|
+
*
|
|
72
|
+
* **What the draft carries goes in `attachments`**, inside the box and above
|
|
73
|
+
* the field, such as thumbnails of attached images and cards for attached
|
|
74
|
+
* files. The region takes no room while nothing is in it. A press on its
|
|
75
|
+
* empty space keeps the reader in the field, and a press on a control in it,
|
|
76
|
+
* such as a button that removes an attachment, goes to that control. Set
|
|
77
|
+
* `attached` as well, so the send button shows for a draft with no text.
|
|
78
|
+
*
|
|
50
79
|
* @since 0.1.0
|
|
51
80
|
* @status stable
|
|
52
81
|
* @category forms
|
|
53
82
|
* @aliases Message box, Prompt input
|
|
54
83
|
* @documentation https://fusion.dev/helix/components/chat
|
|
55
84
|
*
|
|
85
|
+
* @slot attachments - What the draft carries, such as images or files, inside the box and above the field.
|
|
56
86
|
* @slot end - Content under the field, beside the send button.
|
|
87
|
+
* @slot idle - A control shown in place of the send button while the draft is empty and no reply is on its way, such as a button that starts a voice conversation.
|
|
57
88
|
* @slot start - Content under the field at the leading edge, such as an attachment button or a model picker.
|
|
58
89
|
*
|
|
59
90
|
* @event {HelixBeforeListenEvent} helix-before-listen - The dictation button was pressed. Cancelable: cancel it to keep the composer from listening.
|
|
60
91
|
* @event {HelixListenEvent} helix-listen - Dictation settled on a phrase and put it in the draft, after the draft's own `input`.
|
|
61
92
|
* @event {HelixSendEvent} helix-send - Fired when the user sends their turn, and cancelable.
|
|
93
|
+
* @event {HelixStopEvent} helix-stop - Fired when the user presses the stop button, shown in place of the send button while `busy` is set.
|
|
62
94
|
* @event {HelixTypingEvent} helix-typing - The user started or stopped writing, with `typing` saying which, as the composer's `typing` does.
|
|
63
95
|
* @event {Event} input - The user changed the text. Read it from `value`, for a typing indicator or a saved draft. Bubbles and is composed, as a native field's is.
|
|
64
96
|
*
|
|
97
|
+
* @csspart attachments - The box around the `attachments` slot, above the field. It is hidden while the slot is empty.
|
|
65
98
|
* @csspart composer - The whole composer.
|
|
66
99
|
* @csspart end - The box around the `end` slot.
|
|
67
100
|
* @csspart feedback - The `feedback` message, under the field while it is set.
|
|
68
101
|
* @csspart field - The text field.
|
|
69
|
-
* @csspart
|
|
70
|
-
* @csspart
|
|
102
|
+
* @csspart idle - The box around the `idle` slot, shown in the send button's place while the draft is empty.
|
|
103
|
+
* @csspart mark - The icon inside the send button, a return key, or a square while `busy`.
|
|
104
|
+
* @csspart reason-arrow - The arrow of the bubble that says `send-reason`, forwarded from `Tooltip`.
|
|
105
|
+
* @csspart reason-arrow-bottom - The arrow while the bubble is below the send button, on the bubble's top edge. Set together with `reason-arrow`.
|
|
106
|
+
* @csspart reason-arrow-left - The arrow while the bubble is left of the send button, on the bubble's right edge. Set together with `reason-arrow`.
|
|
107
|
+
* @csspart reason-arrow-right - The arrow while the bubble is right of the send button, on the bubble's left edge. Set together with `reason-arrow`.
|
|
108
|
+
* @csspart reason-arrow-top - The arrow while the bubble is above the send button, on the bubble's bottom edge. Set together with `reason-arrow`.
|
|
109
|
+
* @csspart reason-surface - The bubble that says `send-reason` over the send button, forwarded from `Tooltip`.
|
|
110
|
+
* @csspart reason-surface-bottom - The bubble while it is below the send button. Set together with `reason-surface`.
|
|
111
|
+
* @csspart reason-surface-left - The bubble while it is left of the send button. Set together with `reason-surface`.
|
|
112
|
+
* @csspart reason-surface-right - The bubble while it is right of the send button. Set together with `reason-surface`.
|
|
113
|
+
* @csspart reason-surface-top - The bubble while it is above the send button. Set together with `reason-surface`.
|
|
114
|
+
* @csspart send - The send button, which is the stop button while `busy`.
|
|
71
115
|
* @csspart start - The box around the `start` slot.
|
|
72
116
|
* @csspart tools - The row under the field.
|
|
73
117
|
* @csspart voice - The dictation button, under `voice`, before the send button.
|
|
74
118
|
* @csspart voice-listening - The dictation button while it listens. Set alongside `voice`.
|
|
75
119
|
* @csspart voice-status - What a screen reader hears as dictation starts and stops, drawn for no one else.
|
|
76
120
|
*
|
|
121
|
+
* @cssstate example-entering - The example in the empty field has just replaced the one before it, for an entrance. It holds for 350 milliseconds. See `placeholders`.
|
|
122
|
+
* @cssstate example-leaving - The example in the empty field is giving way to the next one, for an exit. It holds for 250 milliseconds, and the next example becomes the placeholder 100 milliseconds in, so the two can cross. See `placeholders`.
|
|
77
123
|
* @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. Or `feedback` is set.
|
|
78
124
|
* @cssstate listening - While the composer listens for dictation. The element sets `--dictation-level` on itself meanwhile, from 0 to 1, for a meter.
|
|
79
125
|
*/
|
|
@@ -84,6 +130,22 @@ export declare class HelixHeadlessChatComposer extends HelixHeadlessChatComposer
|
|
|
84
130
|
* `registerWords('de', { 'helix-chat-composer': { sendLabel: 'Senden' } })`.
|
|
85
131
|
*/
|
|
86
132
|
static words: string;
|
|
133
|
+
/**
|
|
134
|
+
* Whether the draft holds something besides its text, such as an attached
|
|
135
|
+
* file. The draft then counts as one though the field is empty, so the
|
|
136
|
+
* send button shows in place of the `idle` slot.
|
|
137
|
+
*/
|
|
138
|
+
attached: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Whether a reply is on its way. While set, the send button is a stop
|
|
141
|
+
* button that sends `helix-stop`, and the draft does not send.
|
|
142
|
+
*
|
|
143
|
+
* **The same button rather than one beside it**, because sending and
|
|
144
|
+
* stopping are never both possible: while a reply is on its way there is
|
|
145
|
+
* nothing to send to, and the place the reader's eye goes to act is the
|
|
146
|
+
* place that can act. The field stays open for the next turn.
|
|
147
|
+
*/
|
|
148
|
+
busy: boolean;
|
|
87
149
|
/**
|
|
88
150
|
* A message about the draft, shown under the field. While set, the composer
|
|
89
151
|
* is invalid, does not send, and does not submit, as a field's `feedback`
|
|
@@ -107,6 +169,35 @@ export declare class HelixHeadlessChatComposer extends HelixHeadlessChatComposer
|
|
|
107
169
|
* Placeholder text shown in an empty field.
|
|
108
170
|
*/
|
|
109
171
|
placeholder: string;
|
|
172
|
+
/**
|
|
173
|
+
* Examples the empty field shows in turn, in place of `placeholder`, such
|
|
174
|
+
* as tasks a reader might ask for. Each shows for 6.5 seconds, and a longer
|
|
175
|
+
* one shows longer, up to 10 seconds.
|
|
176
|
+
*
|
|
177
|
+
* **Each is the field's placeholder in its turn**, so a screen reader that
|
|
178
|
+
* reaches the field hears the one showing. The swap runs through
|
|
179
|
+
* `:state(example-leaving)`, as one example leaves, and
|
|
180
|
+
* `:state(example-entering)`, as the next arrives.
|
|
181
|
+
*
|
|
182
|
+
* **They move only while nobody is about to write, and while the reader
|
|
183
|
+
* can see them.** An empty field without focus moves on. Focus or text
|
|
184
|
+
* holds the example showing, and an example already leaving finishes its
|
|
185
|
+
* turn. A composer scrolled out of view holds its example, and the example
|
|
186
|
+
* gets its full time again when the composer comes back. Where the reader
|
|
187
|
+
* asked for less motion, or the page paused its motion, the first example
|
|
188
|
+
* stays.
|
|
189
|
+
*/
|
|
190
|
+
placeholders: readonly string[];
|
|
191
|
+
/**
|
|
192
|
+
* Whether Enter sends while `busy`, for a page that queues the turn or
|
|
193
|
+
* hands it to the reply under way.
|
|
194
|
+
*
|
|
195
|
+
* Off by default, because a page that does neither would drop the turn: it
|
|
196
|
+
* has nowhere to put a message while a reply is coming. The button stays
|
|
197
|
+
* the stop button either way, since stopping is still the one thing it
|
|
198
|
+
* should do while a reply is on its way.
|
|
199
|
+
*/
|
|
200
|
+
queue: boolean;
|
|
110
201
|
/**
|
|
111
202
|
* Whether the form needs a draft to submit. An empty draft never sends,
|
|
112
203
|
* required or not.
|
|
@@ -118,10 +209,28 @@ export declare class HelixHeadlessChatComposer extends HelixHeadlessChatComposer
|
|
|
118
209
|
* It grows from there.
|
|
119
210
|
*/
|
|
120
211
|
rows: number;
|
|
212
|
+
/**
|
|
213
|
+
* Whether the draft waits to be sent, such as while an attachment is still
|
|
214
|
+
* read. The send button is unavailable and Enter does not send, while the
|
|
215
|
+
* field stays open. The stop button shown while `busy` still stops.
|
|
216
|
+
*/
|
|
217
|
+
sendDisabled: boolean;
|
|
121
218
|
/**
|
|
122
219
|
* Label for the send button.
|
|
123
220
|
*/
|
|
124
221
|
sendLabel: string;
|
|
222
|
+
/**
|
|
223
|
+
* Why the draft waits, such as "Reading photo.jpg", shown in a tooltip on
|
|
224
|
+
* the send button while `send-disabled` is set. The button then stays in
|
|
225
|
+
* the tab order and takes the pointer, with `aria-disabled` in place of
|
|
226
|
+
* `disabled`, so the reason can be reached by keyboard and by pointer, as
|
|
227
|
+
* Button's `focusable-when-disabled` does. A press still sends nothing.
|
|
228
|
+
*/
|
|
229
|
+
sendReason: string;
|
|
230
|
+
/**
|
|
231
|
+
* Label for the stop button shown while `busy`.
|
|
232
|
+
*/
|
|
233
|
+
stopLabel: string;
|
|
125
234
|
/**
|
|
126
235
|
* What sends the turn: `enter` for the Enter key, or `button` for the send
|
|
127
236
|
* button only.
|
|
@@ -164,6 +273,29 @@ export declare class HelixHeadlessChatComposer extends HelixHeadlessChatComposer
|
|
|
164
273
|
private edited;
|
|
165
274
|
/** Whether the composer shows that its draft is wrong. */
|
|
166
275
|
private shownInvalid;
|
|
276
|
+
/** Which of `placeholders` the empty field shows. */
|
|
277
|
+
private example;
|
|
278
|
+
/**
|
|
279
|
+
* Which of `placeholders` is on its way out while the next comes in, so a
|
|
280
|
+
* tier can draw the two at once.
|
|
281
|
+
*/
|
|
282
|
+
private leavingExample;
|
|
283
|
+
/** Whether the field holds focus, which holds the examples still. */
|
|
284
|
+
private focused;
|
|
285
|
+
/** Whether any of the composer is in view, which the examples need to move. */
|
|
286
|
+
private onScreen;
|
|
287
|
+
/** Watches the composer come into view and go out of it. */
|
|
288
|
+
private sight;
|
|
289
|
+
/** The wait before the example showing leaves, while the examples move. */
|
|
290
|
+
private turning;
|
|
291
|
+
/** The wait before the next example appears, part way through the exit. */
|
|
292
|
+
private crossing;
|
|
293
|
+
/** The exit of the example leaving, while it runs. */
|
|
294
|
+
private leaving;
|
|
295
|
+
/** The entrance of the example arriving, while it runs. */
|
|
296
|
+
private entering;
|
|
297
|
+
/** Stops listening to the page's motion switch. */
|
|
298
|
+
private unmoved;
|
|
167
299
|
/** The pause after which the user has stopped writing. */
|
|
168
300
|
private readonly pause;
|
|
169
301
|
/**
|
|
@@ -204,8 +336,90 @@ export declare class HelixHeadlessChatComposer extends HelixHeadlessChatComposer
|
|
|
204
336
|
* and the send control drifts inward by one for every slot a consumer left
|
|
205
337
|
* alone. No selector can ask this: a slot's assigned nodes are not its DOM
|
|
206
338
|
* children, so `:empty` describes the fallback rather than the fill.
|
|
339
|
+
* Re-marked on `slotchange` as well as after a render, so a control a
|
|
340
|
+
* framework renders after the first render is shown.
|
|
207
341
|
*/
|
|
208
342
|
private markEmpty;
|
|
343
|
+
/**
|
|
344
|
+
* Whether the `idle` slot shows in the send button's place: a child is in
|
|
345
|
+
* it, the draft is empty, and no reply is on its way. Read off the children
|
|
346
|
+
* where they are known, and drawn again as the slot's contents change. A
|
|
347
|
+
* server cannot know them, so it serves the send button, which a hydrating
|
|
348
|
+
* render keeps until `finishHydration` reads them.
|
|
349
|
+
*/
|
|
350
|
+
private get idling();
|
|
351
|
+
/**
|
|
352
|
+
* Whether anything is slotted in `attachments`, where the children are
|
|
353
|
+
* known. A server cannot know them, so it serves the region hidden and
|
|
354
|
+
* takes no gap above the field for an empty draft, the common case.
|
|
355
|
+
* `markEmpty` keeps it right after every render and on `slotchange`.
|
|
356
|
+
*/
|
|
357
|
+
private get carrying();
|
|
358
|
+
/**
|
|
359
|
+
* A press on the attachments' empty space, such as the gap between two
|
|
360
|
+
* thumbnails, keeps the reader in the field, so the next key they press is
|
|
361
|
+
* typed. A press on a control of their own, such as a button that removes
|
|
362
|
+
* an attachment, is left to that control.
|
|
363
|
+
*/
|
|
364
|
+
private readonly handleAttachmentsDown;
|
|
365
|
+
/** Where focus was as an update began: on the send button, in the idle control, or elsewhere. */
|
|
366
|
+
private focusedOn;
|
|
367
|
+
/** Reads which control holds focus, before an update swaps them. */
|
|
368
|
+
private noteFocus;
|
|
369
|
+
/** Moves focus to the control that came, where it was on the one that went. */
|
|
370
|
+
private keepFocus;
|
|
371
|
+
connectedCallback(): void;
|
|
372
|
+
disconnectedCallback(): void;
|
|
373
|
+
/** Notes focus coming and going, which holds the examples or frees them. */
|
|
374
|
+
private readonly handleFocus;
|
|
375
|
+
/** The examples drawn over the field: the one showing, and the one leaving while they cross. */
|
|
376
|
+
private renderExamples;
|
|
377
|
+
/** The example the empty field shows, or `placeholder` where there are none. */
|
|
378
|
+
private get shownPlaceholder();
|
|
379
|
+
/**
|
|
380
|
+
* Starts the examples moving, or stops them, as the list, the field, and
|
|
381
|
+
* the page allow.
|
|
382
|
+
*
|
|
383
|
+
* **Each turn is timed by the example showing**, so a long one stays up
|
|
384
|
+
* longer. **Focus holds the examples and leaves a turn under way to
|
|
385
|
+
* finish**, so the field never shows half an exit. Text, reduced motion, a
|
|
386
|
+
* paused page, scrolling out of view, and leaving the document stop them at
|
|
387
|
+
* once.
|
|
388
|
+
*/
|
|
389
|
+
private turnExamples;
|
|
390
|
+
/**
|
|
391
|
+
* Starts the example showing on its way out. The next appears part way
|
|
392
|
+
* through the exit, and the one leaving goes once its exit ends.
|
|
393
|
+
*/
|
|
394
|
+
private leaveExample;
|
|
395
|
+
/** Shows the next example, on its way in, and times the turn after it. */
|
|
396
|
+
private arriveExample;
|
|
397
|
+
private stopExamples;
|
|
398
|
+
/**
|
|
399
|
+
* Called as the example showing has landed: its entrance ended, or the
|
|
400
|
+
* examples stopped. A tier that holds anything for the swap lets it go
|
|
401
|
+
* here. The headless tier holds nothing.
|
|
402
|
+
*/
|
|
403
|
+
protected exampleLanded(): void;
|
|
404
|
+
/**
|
|
405
|
+
* What the tier above draws over the empty field while `placeholders` holds
|
|
406
|
+
* two examples or more, behind a seam so a tier can draw the swap the
|
|
407
|
+
* field's own placeholder cannot. `example` is the field's placeholder, and
|
|
408
|
+
* `leaving` the one on its way out, each with its place in `placeholders`.
|
|
409
|
+
* For the first 100 milliseconds of an exit the two are the same example,
|
|
410
|
+
* and after that they cross. The headless tier draws nothing, and its field
|
|
411
|
+
* shows each example as its placeholder.
|
|
412
|
+
*
|
|
413
|
+
* **The field keeps its placeholder either way**, so whatever is drawn
|
|
414
|
+
* here is decoration, and the field is what a screen reader reaches.
|
|
415
|
+
*/
|
|
416
|
+
protected renderExample(_example: {
|
|
417
|
+
index: number;
|
|
418
|
+
text: string;
|
|
419
|
+
}, _leaving?: {
|
|
420
|
+
index: number;
|
|
421
|
+
text: string;
|
|
422
|
+
}): TemplateResult | typeof nothing;
|
|
209
423
|
protected updated(changed: PropertyValues): void;
|
|
210
424
|
protected firstUpdated(changed: PropertyValues): void;
|
|
211
425
|
protected willUpdate(changed: PropertyValues): void;
|
|
@@ -226,7 +440,7 @@ export declare class HelixHeadlessChatComposer extends HelixHeadlessChatComposer
|
|
|
226
440
|
*/
|
|
227
441
|
protected renderSend(): TemplateResult;
|
|
228
442
|
/**
|
|
229
|
-
*
|
|
443
|
+
* What the draft carries, the field, and a row of tools under it.
|
|
230
444
|
*
|
|
231
445
|
* **The tools take a line of their own rather than sharing the field's.** On
|
|
232
446
|
* one line every tool a consumer adds takes width away from the thing being
|
|
@@ -254,6 +468,7 @@ export declare class HelixHeadlessChatComposer extends HelixHeadlessChatComposer
|
|
|
254
468
|
*/
|
|
255
469
|
private readonly handleInput;
|
|
256
470
|
private readonly handleSend;
|
|
471
|
+
private readonly handleStop;
|
|
257
472
|
/**
|
|
258
473
|
* Enter sends where that is what `submit-on` says, and never mid-composition.
|
|
259
474
|
*
|