@fusion.dev/helix-headless 0.162.1 → 0.164.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 +1041 -47
- package/dist/callout/root/callout-root.d.ts +43 -4
- package/dist/callout/root/callout-root.js +58 -2
- package/dist/canvas/index.d.ts +1 -1
- package/dist/canvas/root/canvas-document.d.ts +33 -0
- package/dist/canvas/root/canvas-root.d.ts +50 -2
- package/dist/canvas/root/canvas-root.js +98 -3
- package/dist/chat/composer/chat-composer.js +1 -1
- package/dist/chat/root/chat-root.d.ts +15 -4
- package/dist/chat/root/chat-root.js +19 -8
- package/dist/code-block/patch.d.ts +42 -0
- package/dist/code-block/patch.js +118 -0
- package/dist/code-block/root/code-block-root.d.ts +37 -1
- package/dist/code-block/root/code-block-root.js +57 -9
- package/dist/color-picker/root/color-picker-root.d.ts +7 -3
- package/dist/color-picker/root/color-picker-root.js +7 -4
- package/dist/combobox/root/combobox-root.d.ts +1 -0
- package/dist/combobox/root/combobox-root.js +10 -1
- package/dist/data-table/root/data-table-root.d.ts +1 -0
- package/dist/data-table/root/data-table-root.js +1 -0
- package/dist/element-events.generated.js +7 -1
- package/dist/map/root/map-root.d.ts +8 -1
- package/dist/map/root/map-root.js +12 -0
- package/dist/menu/item/menu-item.d.ts +38 -7
- package/dist/menu/item/menu-item.js +20 -9
- package/dist/menu/root/menu-root.d.ts +10 -1
- package/dist/menu/root/menu-root.js +8 -3
- package/dist/object-inspector/root/object-inspector-root.d.ts +23 -0
- package/dist/object-inspector/root/object-inspector-root.js +19 -1
- package/dist/steps/root/steps-root.d.ts +7 -1
- package/dist/steps/root/steps-root.js +6 -1
- package/dist/table/grid-controller.d.ts +7 -0
- package/dist/table/grid-controller.js +15 -1
- package/dist/table/root/table-root.d.ts +10 -0
- package/dist/table/root/table-root.js +18 -1
- package/dist/video/root/video-root.js +2 -1
- package/dist/video-playlist/item/video-playlist-item.d.ts +8 -0
- package/dist/video-playlist/item/video-playlist-item.js +7 -0
- package/dist/video-playlist/root/video-playlist-root.d.ts +60 -0
- package/dist/video-playlist/root/video-playlist-root.js +55 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +502 -40
- package/types/vue.d.ts +126 -14
- package/types/words.d.ts +58 -0
|
@@ -15,8 +15,12 @@ declare const HelixHeadlessCallout_base: (new (...args: any[]) => import("@fusio
|
|
|
15
15
|
* region around it.
|
|
16
16
|
*
|
|
17
17
|
* Set `dismissible` for a note a reader may put away, as Shoelace's `closable`
|
|
18
|
-
* alert does. The button fires `helix-dismiss
|
|
19
|
-
*
|
|
18
|
+
* alert does. The button fires `helix-dismiss`, and unless a listener cancels
|
|
19
|
+
* it the callout hides itself: `open` turns false, `:state(exiting)` holds
|
|
20
|
+
* while its exit animations run, and then it is `hidden`. Remove it in your
|
|
21
|
+
* listener to take it out of the page, or set `open` to show it again, which
|
|
22
|
+
* holds `:state(entering)` while it comes back. Focus on its dismiss button
|
|
23
|
+
* moves to the next callout's, or to the tab stop beside it, once it hides.
|
|
20
24
|
*
|
|
21
25
|
* A callout sets a passage apart inside a document and is read where it sits.
|
|
22
26
|
* For page-level chrome above the masthead that is a live region and can be
|
|
@@ -33,13 +37,19 @@ declare const HelixHeadlessCallout_base: (new (...args: any[]) => import("@fusio
|
|
|
33
37
|
* @slot heading - A heading above the content.
|
|
34
38
|
* @slot icon - Replaces the icon chosen by `tone`.
|
|
35
39
|
*
|
|
36
|
-
* @event {HelixDismissEvent} helix-dismiss - The dismiss button was pressed.
|
|
40
|
+
* @event {HelixDismissEvent} helix-dismiss - The dismiss button was pressed. Unless a listener cancels it, the callout hides itself, and a listener may remove it instead. Cancelable: cancel it to keep the callout showing.
|
|
37
41
|
*
|
|
38
42
|
* @csspart content - The content region.
|
|
39
43
|
* @csspart dismiss - The button that dismisses the callout, shown when `dismissible` is set.
|
|
40
44
|
* @csspart dismiss-mark - The cross on the dismiss button, shown when the `dismiss-icon` slot is empty.
|
|
41
45
|
* @csspart icon - The icon region, an image named for the tone.
|
|
42
46
|
* @csspart icon-mark - The icon for the tone, shown when no icon is slotted.
|
|
47
|
+
*
|
|
48
|
+
* @cssstate closed - While `open` is false: the callout was dismissed, and is leaving or hidden.
|
|
49
|
+
* @cssstate entering - While a callout shown again by `open` runs its entrance animations.
|
|
50
|
+
* @cssstate exiting - While a dismissed callout runs its exit animations, before it is hidden.
|
|
51
|
+
* @cssstate has-heading - Something is slotted into `heading`.
|
|
52
|
+
* @cssstate has-icon - An icon of your own is slotted into `icon`, in place of the tone's.
|
|
43
53
|
*/
|
|
44
54
|
export declare class HelixHeadlessCallout extends HelixHeadlessCallout_base {
|
|
45
55
|
/**
|
|
@@ -47,7 +57,6 @@ export declare class HelixHeadlessCallout extends HelixHeadlessCallout_base {
|
|
|
47
57
|
* `registerWords('de', { 'helix-callout': { toneLabels: { danger: 'Fehler' } } })`.
|
|
48
58
|
*/
|
|
49
59
|
static words: string;
|
|
50
|
-
protected willUpdate(changed: PropertyValues): void;
|
|
51
60
|
/**
|
|
52
61
|
* The dismiss button's name, read by screen readers, with `{label}` for
|
|
53
62
|
* what the callout is called: its heading, or the tone's name where it has
|
|
@@ -58,6 +67,23 @@ export declare class HelixHeadlessCallout extends HelixHeadlessCallout_base {
|
|
|
58
67
|
* read as written.
|
|
59
68
|
*/
|
|
60
69
|
dismissLabel: string;
|
|
70
|
+
/** Which journey is the latest, so an entrance settling late leaves an exit alone. */
|
|
71
|
+
private turn;
|
|
72
|
+
/** Hidden from a reader once hidden, for the focus hand-off. */
|
|
73
|
+
protected get present(): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Hides the callout once a dismissal stands, unless the page removed it in
|
|
76
|
+
* its listener, which is the page's answer and leaves `open` as it was.
|
|
77
|
+
*/
|
|
78
|
+
dismiss(): boolean;
|
|
79
|
+
protected willUpdate(changed: PropertyValues): void;
|
|
80
|
+
/**
|
|
81
|
+
* Whether the callout is showing. A dismissal nobody canceled turns it
|
|
82
|
+
* false and the callout hides itself, and setting it true shows the callout
|
|
83
|
+
* again. Not reflected: open is the resting state, so an `open` attribute
|
|
84
|
+
* would appear on markup a server sent without it.
|
|
85
|
+
*/
|
|
86
|
+
open: boolean;
|
|
61
87
|
/**
|
|
62
88
|
* What kind of attention the passage needs.
|
|
63
89
|
*
|
|
@@ -116,6 +142,19 @@ export declare class HelixHeadlessCallout extends HelixHeadlessCallout_base {
|
|
|
116
142
|
*/
|
|
117
143
|
private dismissName;
|
|
118
144
|
protected firstUpdated(changed: PropertyValues): void;
|
|
145
|
+
protected updated(changed: PropertyValues): void;
|
|
146
|
+
/**
|
|
147
|
+
* The way out after a dismissal, or back in after `open` is set again.
|
|
148
|
+
*
|
|
149
|
+
* **Hidden only once the exit has run**, since a hidden element draws no
|
|
150
|
+
* animation: `:state(exiting)` holds until every animation on the callout
|
|
151
|
+
* has finished, or two frames where it has none, and then `hidden` takes it
|
|
152
|
+
* out of the page and out of the accessibility tree. Shown again at once,
|
|
153
|
+
* with `:state(entering)` while it arrives.
|
|
154
|
+
*/
|
|
155
|
+
private journey;
|
|
156
|
+
/** Says which regions hold something, one call apiece so each pairs with its `@cssstate`. */
|
|
157
|
+
private readonly readRegions;
|
|
119
158
|
protected render(): TemplateResult;
|
|
120
159
|
}
|
|
121
160
|
declare global {
|
|
@@ -4,7 +4,7 @@ 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, formatWords, HelixElement, hostAria, MixinDismissible, slottedWords, TONE_MARKS, toneMark, words, wordsConverter, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { animationsSettled, 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';
|
|
@@ -12,6 +12,8 @@ let HelixHeadlessCallout = class HelixHeadlessCallout extends MixinDismissible(H
|
|
|
12
12
|
constructor() {
|
|
13
13
|
super(...arguments);
|
|
14
14
|
this.dismissLabel = 'Dismiss {label}';
|
|
15
|
+
this.turn = 0;
|
|
16
|
+
this.open = true;
|
|
15
17
|
this.toneLabel = '';
|
|
16
18
|
this.toneLabels = {
|
|
17
19
|
danger: 'Error',
|
|
@@ -20,14 +22,34 @@ let HelixHeadlessCallout = class HelixHeadlessCallout extends MixinDismissible(H
|
|
|
20
22
|
success: 'Success',
|
|
21
23
|
warning: 'Warning',
|
|
22
24
|
};
|
|
25
|
+
this.readRegions = () => {
|
|
26
|
+
const filled = (name) => {
|
|
27
|
+
const slot = this.renderRoot?.querySelector(`slot[name="${name}"]`);
|
|
28
|
+
return (slot?.assignedNodes({ flatten: true }) ?? []).some((node) => node.parentNode !== slot &&
|
|
29
|
+
(node.nodeType === Node.ELEMENT_NODE || node.textContent?.trim() !== ''));
|
|
30
|
+
};
|
|
31
|
+
this.toggleState('has-heading', filled('heading'));
|
|
32
|
+
this.toggleState('has-icon', filled('icon'));
|
|
33
|
+
};
|
|
23
34
|
}
|
|
24
35
|
static { this.words = 'helix-callout'; }
|
|
36
|
+
get present() {
|
|
37
|
+
return this.isConnected && !this.hasAttribute('hidden');
|
|
38
|
+
}
|
|
39
|
+
dismiss() {
|
|
40
|
+
const stands = super.dismiss();
|
|
41
|
+
if (stands && this.isConnected) {
|
|
42
|
+
this.open = false;
|
|
43
|
+
}
|
|
44
|
+
return stands;
|
|
45
|
+
}
|
|
25
46
|
willUpdate(changed) {
|
|
26
47
|
super.willUpdate(changed);
|
|
27
48
|
hostAria(this.internals, { role: 'note' });
|
|
49
|
+
this.toggleState('closed', !this.open);
|
|
28
50
|
}
|
|
29
51
|
renderIcon(icon, _label) {
|
|
30
|
-
return html `<slot name="icon" aria-hidden="true">
|
|
52
|
+
return html `<slot name="icon" aria-hidden="true" @slotchange=${this.readRegions}>
|
|
31
53
|
<svg part="icon-mark" fill="currentColor" viewBox=${icon.viewBox} aria-hidden="true">
|
|
32
54
|
${unsafeSVG(icon.content)}
|
|
33
55
|
</svg>
|
|
@@ -48,6 +70,37 @@ let HelixHeadlessCallout = class HelixHeadlessCallout extends MixinDismissible(H
|
|
|
48
70
|
super.firstUpdated(changed);
|
|
49
71
|
this.finishHydration();
|
|
50
72
|
}
|
|
73
|
+
updated(changed) {
|
|
74
|
+
super.updated(changed);
|
|
75
|
+
this.readRegions();
|
|
76
|
+
if (changed.has('open') && changed.get('open') !== undefined) {
|
|
77
|
+
this.journey();
|
|
78
|
+
}
|
|
79
|
+
else if (changed.has('open') && !this.open) {
|
|
80
|
+
this.setAttribute('hidden', '');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
journey() {
|
|
84
|
+
this.turn += 1;
|
|
85
|
+
const turn = this.turn;
|
|
86
|
+
const open = this.open;
|
|
87
|
+
this.toggleState('entering', open);
|
|
88
|
+
this.toggleState('exiting', !open);
|
|
89
|
+
if (open) {
|
|
90
|
+
this.removeAttribute('hidden');
|
|
91
|
+
}
|
|
92
|
+
void animationsSettled(this).then(() => {
|
|
93
|
+
if (turn !== this.turn) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.toggleState('entering', false);
|
|
97
|
+
this.toggleState('exiting', false);
|
|
98
|
+
if (!open) {
|
|
99
|
+
this.setAttribute('hidden', '');
|
|
100
|
+
this.handFocusOn();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
51
104
|
render() {
|
|
52
105
|
const tone = this.tone === 'neutral' ? 'neutral' : toneMark(this.tone) ? (this.tone ?? 'info') : 'info';
|
|
53
106
|
const { icon } = toneMark(tone) ?? TONE_MARKS.info;
|
|
@@ -69,6 +122,9 @@ __decorate([
|
|
|
69
122
|
words,
|
|
70
123
|
property({ attribute: 'dismiss-label' })
|
|
71
124
|
], HelixHeadlessCallout.prototype, "dismissLabel", void 0);
|
|
125
|
+
__decorate([
|
|
126
|
+
property({ type: Boolean })
|
|
127
|
+
], HelixHeadlessCallout.prototype, "open", void 0);
|
|
72
128
|
__decorate([
|
|
73
129
|
property({ reflect: true })
|
|
74
130
|
], HelixHeadlessCallout.prototype, "tone", void 0);
|
package/dist/canvas/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { HelixHeadlessCanvasEdge } from './edge/canvas-edge.js';
|
|
2
2
|
export { HelixHeadlessCanvasNode } from './node/canvas-node.js';
|
|
3
|
-
export type { CanvasDocument, CanvasEdge, CanvasEdgeProps, CanvasEnd, CanvasNode, CanvasNodeProps, CanvasPath, CanvasSide, CanvasTone, } from './root/canvas-root.js';
|
|
3
|
+
export type { CanvasDocument, CanvasEdge, CanvasEdgeProps, CanvasEnd, CanvasNode, CanvasNodeProps, CanvasPath, CanvasRoute, CanvasRouteEnds, CanvasSide, CanvasTone, } from './root/canvas-root.js';
|
|
4
4
|
export { fromJsonCanvas, HelixHeadlessCanvas } from './root/canvas-root.js';
|
|
@@ -91,6 +91,39 @@ export declare const port: (node: CanvasNode, side: CanvasSide) => {
|
|
|
91
91
|
};
|
|
92
92
|
/** How a connector travels between the two cards it joins. */
|
|
93
93
|
export type CanvasPath = 'curve' | 'straight' | 'step';
|
|
94
|
+
/** What a board's `edgeRoute` is given for one connection, in the board's own units. */
|
|
95
|
+
export interface CanvasRouteEnds {
|
|
96
|
+
/** The connection. */
|
|
97
|
+
edge: CanvasEdge;
|
|
98
|
+
/** Where it arrives, on the side of the card it arrives at. */
|
|
99
|
+
end: {
|
|
100
|
+
x: number;
|
|
101
|
+
y: number;
|
|
102
|
+
};
|
|
103
|
+
/** The side of the card it leaves. */
|
|
104
|
+
fromSide: CanvasSide;
|
|
105
|
+
/** The route the board would draw: the connection's `path`, or the board's. */
|
|
106
|
+
path: CanvasPath;
|
|
107
|
+
/** Where it leaves, on the side of the card it leaves. */
|
|
108
|
+
start: {
|
|
109
|
+
x: number;
|
|
110
|
+
y: number;
|
|
111
|
+
};
|
|
112
|
+
/** The side of the card it arrives at. */
|
|
113
|
+
toSide: CanvasSide;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Draws a connection's line: its SVG path from `start` to `end`, and where its
|
|
117
|
+
* label sits, the middle of the two ends where it says nothing. Nothing back
|
|
118
|
+
* draws the board's own.
|
|
119
|
+
*/
|
|
120
|
+
export type CanvasRoute = (ends: CanvasRouteEnds) => {
|
|
121
|
+
d: string;
|
|
122
|
+
mid?: {
|
|
123
|
+
x: number;
|
|
124
|
+
y: number;
|
|
125
|
+
} | undefined;
|
|
126
|
+
} | undefined;
|
|
94
127
|
/** A point in the document's own units. */
|
|
95
128
|
type Point = {
|
|
96
129
|
x: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type CanvasView, HelixElement, type ViewportPanDetail, type ViewportWay, type ViewportZoomDetail } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
-
import { type CanvasDocument, type CanvasTone } from './canvas-document.js';
|
|
4
|
-
export type { CanvasDocument, CanvasEdge, CanvasEdgeProps, CanvasEnd, CanvasNode, CanvasNodeProps, CanvasPath, CanvasSide, CanvasTone, } from './canvas-document.js';
|
|
3
|
+
import { type CanvasDocument, type CanvasRoute, type CanvasTone } from './canvas-document.js';
|
|
4
|
+
export type { CanvasDocument, CanvasEdge, CanvasEdgeProps, CanvasEnd, CanvasNode, CanvasNodeProps, CanvasPath, CanvasRoute, CanvasRouteEnds, CanvasSide, CanvasTone, } from './canvas-document.js';
|
|
5
5
|
export { fromJsonCanvas } from './canvas-document.js';
|
|
6
6
|
declare const HelixHeadlessCanvas_base: (new (...args: any[]) => import("@fusion.dev/helix-core").ViewportElement) & typeof HelixElement;
|
|
7
7
|
/**
|
|
@@ -92,6 +92,17 @@ declare const HelixHeadlessCanvas_base: (new (...args: any[]) => import("@fusion
|
|
|
92
92
|
* connection is a new element, and React discards an element the board adds to
|
|
93
93
|
* its markup on the next render, so you write the connection.
|
|
94
94
|
*
|
|
95
|
+
* **`edgeRoute` draws the connections' lines your own way**: a function from
|
|
96
|
+
* where a line leaves and arrives, and the sides it does, to its SVG path, for
|
|
97
|
+
* a route such as one that bends around the cards between. What it returns
|
|
98
|
+
* is the line drawn, the line pressed and focused, and the one an arrowhead
|
|
99
|
+
* sits at the end of. Nothing back draws the board's own.
|
|
100
|
+
*
|
|
101
|
+
* Add `minimap` to `controls` for a picture of the whole board in a corner:
|
|
102
|
+
* each card as a box, and a frame around what is on screen. A press or a drag
|
|
103
|
+
* on it brings that place to the middle of the view. It is for a pointer, and
|
|
104
|
+
* a screen reader skips it: the arrow keys and `show()` reach every card.
|
|
105
|
+
*
|
|
95
106
|
* Cards and connections are `helix-canvas-node` and `helix-canvas-edge`
|
|
96
107
|
* elements, so they are in the served markup whichever renderer wrote them. The
|
|
97
108
|
* library fetches nothing, and an array passed to an element is missing from
|
|
@@ -148,6 +159,9 @@ declare const HelixHeadlessCanvas_base: (new (...args: any[]) => import("@fusion
|
|
|
148
159
|
* @csspart label-editor - The field for editing a connection's label or a group's name, placed over that text.
|
|
149
160
|
* @csspart live - A live region that screen readers announce while a connection is being made.
|
|
150
161
|
* @csspart marquee - The box a Shift-drag across the empty board draws, which selects the cards it touches.
|
|
162
|
+
* @csspart minimap - The picture of the whole board, under `controls="minimap"`.
|
|
163
|
+
* @csspart minimap-node - One card in the minimap.
|
|
164
|
+
* @csspart minimap-view - The frame in the minimap around what is on screen.
|
|
151
165
|
* @csspart pan-down - The button that moves the board down.
|
|
152
166
|
* @csspart pan-end - The button that moves the board toward the end of a line of text: right in English.
|
|
153
167
|
* @csspart pan-start - The button that moves the board toward the start of a line of text: left in English.
|
|
@@ -210,6 +224,15 @@ export declare class HelixHeadlessCanvas extends HelixHeadlessCanvas_base {
|
|
|
210
224
|
controlsLabel: string;
|
|
211
225
|
/** What screen readers hear after a copy, with `{count}` for the cards copied. */
|
|
212
226
|
copiedLabel: string;
|
|
227
|
+
/**
|
|
228
|
+
* Draws every connection's line: a function from where it leaves and
|
|
229
|
+
* arrives, and the sides it does, to its SVG path and where its label sits.
|
|
230
|
+
*
|
|
231
|
+
* Absent, or returning nothing for a connection, draws the board's own
|
|
232
|
+
* route. A function that throws draws the board's own too, and the error is
|
|
233
|
+
* reported to the page as an uncaught one. Set it as a property.
|
|
234
|
+
*/
|
|
235
|
+
edgeRoute?: CanvasRoute;
|
|
213
236
|
/** The board's name where `label` is empty. */
|
|
214
237
|
fallbackLabel: string;
|
|
215
238
|
/**
|
|
@@ -702,6 +725,8 @@ export declare class HelixHeadlessCanvas extends HelixHeadlessCanvas_base {
|
|
|
702
725
|
* both its cards are on the board; nothing where either is not.
|
|
703
726
|
*/
|
|
704
727
|
private geometryOf;
|
|
728
|
+
/** One connection's line, by `edgeRoute` where it draws one, and by the board's own where not. */
|
|
729
|
+
private draw;
|
|
705
730
|
/** The card an event came from, wherever inside it the event began. */
|
|
706
731
|
private cardOf;
|
|
707
732
|
/**
|
|
@@ -1244,6 +1269,29 @@ export declare class HelixHeadlessCanvas extends HelixHeadlessCanvas_base {
|
|
|
1244
1269
|
* the card a keyboard reader is on.
|
|
1245
1270
|
*/
|
|
1246
1271
|
private renderDraft;
|
|
1272
|
+
/** The part of the document on screen, in its own units. */
|
|
1273
|
+
private get onScreen();
|
|
1274
|
+
/** What the minimap shows: every card and what is on screen, with room around them. */
|
|
1275
|
+
private get minimapArea();
|
|
1276
|
+
/**
|
|
1277
|
+
* A press or a drag on the minimap, which brings the place under it to the
|
|
1278
|
+
* middle of the view.
|
|
1279
|
+
*
|
|
1280
|
+
* **The point is read through the picture's own scale**: the area is drawn
|
|
1281
|
+
* whole and centered in the box, so the box's spare width or height is split
|
|
1282
|
+
* either side of it.
|
|
1283
|
+
*/
|
|
1284
|
+
private readonly handleMinimapPointer;
|
|
1285
|
+
/**
|
|
1286
|
+
* The minimap, under `controls="minimap"`: each card as a box, and a frame
|
|
1287
|
+
* around what is on screen.
|
|
1288
|
+
*
|
|
1289
|
+
* **A picture for a pointer**, hidden from screen readers and taking no
|
|
1290
|
+
* focus, as a map's own overview is: the arrow keys and `show()` already take
|
|
1291
|
+
* a keyboard to any card, and a second way that says nothing a reader can
|
|
1292
|
+
* use would be a stop that does nothing.
|
|
1293
|
+
*/
|
|
1294
|
+
private renderMinimap;
|
|
1247
1295
|
protected render(): TemplateResult;
|
|
1248
1296
|
}
|
|
1249
1297
|
declare global {
|
|
@@ -733,6 +733,35 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
733
733
|
this.handleSlotChange = () => {
|
|
734
734
|
this.requestUpdate();
|
|
735
735
|
};
|
|
736
|
+
this.handleMinimapPointer = (event) => {
|
|
737
|
+
if (event.type === 'pointermove' && (event.buttons & 1) === 0) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const picture = event.currentTarget;
|
|
741
|
+
if (event.type === 'pointerdown') {
|
|
742
|
+
if (event.button !== 0) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
capturePointer(picture, event.pointerId);
|
|
746
|
+
}
|
|
747
|
+
event.preventDefault();
|
|
748
|
+
const area = this.minimapArea;
|
|
749
|
+
const shown = picture.getBoundingClientRect();
|
|
750
|
+
const scale = Math.min(shown.width / area.width, shown.height / area.height) || 1;
|
|
751
|
+
const point = {
|
|
752
|
+
x: area.x + (event.clientX - shown.left - (shown.width - area.width * scale) / 2) / scale,
|
|
753
|
+
y: area.y + (event.clientY - shown.top - (shown.height - area.height * scale) / 2) / scale,
|
|
754
|
+
};
|
|
755
|
+
const board = extent(this.nodes);
|
|
756
|
+
const { zoom } = this.at;
|
|
757
|
+
this.direct = event.type === 'pointermove';
|
|
758
|
+
this.touched = true;
|
|
759
|
+
this.at = {
|
|
760
|
+
...this.at,
|
|
761
|
+
x: this.box.width / 2 - (point.x - board.x + MARGIN) * zoom,
|
|
762
|
+
y: this.box.height / 2 - (point.y - board.y + MARGIN) * zoom,
|
|
763
|
+
};
|
|
764
|
+
};
|
|
736
765
|
}
|
|
737
766
|
static { this.words = 'helix-canvas'; }
|
|
738
767
|
static { this.shadowRootOptions = {
|
|
@@ -1120,9 +1149,25 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
1120
1149
|
if (!from || !to) {
|
|
1121
1150
|
return undefined;
|
|
1122
1151
|
}
|
|
1123
|
-
const drawn =
|
|
1152
|
+
const drawn = this.draw(edge, onPlane(from, origin), onPlane(to, origin), edge.path ?? this.path ?? 'curve', heads(edge));
|
|
1124
1153
|
return { ...drawn, from, to };
|
|
1125
1154
|
}
|
|
1155
|
+
draw(edge, from, to, path, drawnHeads) {
|
|
1156
|
+
if (this.edgeRoute) {
|
|
1157
|
+
const { end, fromSide, start, toSide } = ends(edge, from, to);
|
|
1158
|
+
let own;
|
|
1159
|
+
try {
|
|
1160
|
+
own = this.edgeRoute({ edge, end, fromSide, path, start, toSide });
|
|
1161
|
+
}
|
|
1162
|
+
catch (error) {
|
|
1163
|
+
globalThis.reportError?.(error);
|
|
1164
|
+
}
|
|
1165
|
+
if (own?.d) {
|
|
1166
|
+
return { d: own.d, mid: own.mid ?? { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 } };
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return connector(edge, from, to, path, drawnHeads);
|
|
1170
|
+
}
|
|
1126
1171
|
cardOf(event) {
|
|
1127
1172
|
return event
|
|
1128
1173
|
.composedPath()
|
|
@@ -2134,7 +2179,7 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
2134
2179
|
if (target) {
|
|
2135
2180
|
const reversed = linking.moving === 'from';
|
|
2136
2181
|
const [from, to] = reversed ? [target, anchor] : [anchor, target];
|
|
2137
|
-
const drawn =
|
|
2182
|
+
const drawn = this.draw({
|
|
2138
2183
|
fromNode: from.id,
|
|
2139
2184
|
fromSide: reversed ? linking.toSide : linking.side,
|
|
2140
2185
|
id: '',
|
|
@@ -2157,6 +2202,53 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
2157
2202
|
}
|
|
2158
2203
|
return nothing;
|
|
2159
2204
|
}
|
|
2205
|
+
get onScreen() {
|
|
2206
|
+
const board = extent(this.nodes);
|
|
2207
|
+
const { height, width } = this.box;
|
|
2208
|
+
const { x, y, zoom } = this.at;
|
|
2209
|
+
return {
|
|
2210
|
+
height: height / zoom,
|
|
2211
|
+
width: width / zoom,
|
|
2212
|
+
x: board.x - MARGIN - x / zoom,
|
|
2213
|
+
y: board.y - MARGIN - y / zoom,
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
get minimapArea() {
|
|
2217
|
+
const board = extent(this.nodes);
|
|
2218
|
+
const view = this.onScreen;
|
|
2219
|
+
const x = Math.min(board.x, view.x);
|
|
2220
|
+
const y = Math.min(board.y, view.y);
|
|
2221
|
+
const width = Math.max(board.x + board.width, view.x + view.width) - x;
|
|
2222
|
+
const height = Math.max(board.y + board.height, view.y + view.height) - y;
|
|
2223
|
+
const room = Math.max(width, height) * 0.05;
|
|
2224
|
+
return {
|
|
2225
|
+
height: height + room * 2,
|
|
2226
|
+
width: width + room * 2,
|
|
2227
|
+
x: x - room,
|
|
2228
|
+
y: y - room,
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
renderMinimap() {
|
|
2232
|
+
const nodes = this.nodes;
|
|
2233
|
+
if (!this.drawsControls('minimap') || nodes.length === 0) {
|
|
2234
|
+
return nothing;
|
|
2235
|
+
}
|
|
2236
|
+
const area = this.minimapArea;
|
|
2237
|
+
const view = this.onScreen;
|
|
2238
|
+
return html `<svg
|
|
2239
|
+
part="minimap"
|
|
2240
|
+
aria-hidden="true"
|
|
2241
|
+
height="120"
|
|
2242
|
+
preserveAspectRatio="xMidYMid meet"
|
|
2243
|
+
viewBox=${`${area.x} ${area.y} ${area.width} ${area.height}`}
|
|
2244
|
+
width="160"
|
|
2245
|
+
@pointerdown=${this.handleMinimapPointer}
|
|
2246
|
+
@pointermove=${this.handleMinimapPointer}
|
|
2247
|
+
>
|
|
2248
|
+
${nodes.map((node) => svg `<rect part="minimap-node" data-group=${node.type === 'group' ? '' : nothing} x=${node.x} y=${node.y} width=${node.width} height=${node.height} />`)}
|
|
2249
|
+
${svg `<rect part="minimap-view" x=${view.x} y=${view.y} width=${view.width} height=${view.height} />`}
|
|
2250
|
+
</svg>`;
|
|
2251
|
+
}
|
|
2160
2252
|
render() {
|
|
2161
2253
|
const nodes = this.nodes;
|
|
2162
2254
|
const drawn = extent(nodes);
|
|
@@ -2219,7 +2311,7 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
2219
2311
|
<slot name="empty"></slot>
|
|
2220
2312
|
${this.renderGestures()}
|
|
2221
2313
|
</div>
|
|
2222
|
-
${this.renderControls()}
|
|
2314
|
+
${this.renderControls()} ${this.renderMinimap()}
|
|
2223
2315
|
<span part="live" role="status">${this.said}</span>
|
|
2224
2316
|
<div part="footer">
|
|
2225
2317
|
<slot name="footer"></slot>
|
|
@@ -2255,6 +2347,9 @@ __decorate([
|
|
|
2255
2347
|
words,
|
|
2256
2348
|
property({ attribute: 'copied-label' })
|
|
2257
2349
|
], HelixHeadlessCanvas.prototype, "copiedLabel", void 0);
|
|
2350
|
+
__decorate([
|
|
2351
|
+
property({ attribute: false })
|
|
2352
|
+
], HelixHeadlessCanvas.prototype, "edgeRoute", void 0);
|
|
2258
2353
|
__decorate([
|
|
2259
2354
|
words,
|
|
2260
2355
|
property({ attribute: 'fallback-label' })
|
|
@@ -113,7 +113,7 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFor
|
|
|
113
113
|
}
|
|
114
114
|
const invalid = this.touched && !this.internals.validity.valid;
|
|
115
115
|
this.toggleState('invalid', invalid);
|
|
116
|
-
this.internals.ariaInvalid = invalid ? 'true' :
|
|
116
|
+
this.internals.ariaInvalid = invalid ? 'true' : 'false';
|
|
117
117
|
if (invalid !== this.shownInvalid) {
|
|
118
118
|
this.shownInvalid = invalid;
|
|
119
119
|
}
|
|
@@ -517,12 +517,23 @@ export declare class HelixHeadlessChat extends HelixHeadlessChat_base {
|
|
|
517
517
|
* Moves focus to turn `at`, drawing it first where the transcript is
|
|
518
518
|
* windowed and it is not drawn.
|
|
519
519
|
*
|
|
520
|
-
* **A turn takes focus only once a reader moves to it**, so the
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
* to.
|
|
520
|
+
* **A turn takes focus only once a reader moves to it**, so the `tabindex`
|
|
521
|
+
* it needs is written then, long after any server's markup has hydrated,
|
|
522
|
+
* and never on a turn nobody moved to.
|
|
524
523
|
*/
|
|
525
524
|
private focusTurn;
|
|
525
|
+
/** The turn that is a tab stop because a reader moved to it. */
|
|
526
|
+
private stop;
|
|
527
|
+
/**
|
|
528
|
+
* Focuses a turn, making it the transcript's one stop a reader moved to.
|
|
529
|
+
*
|
|
530
|
+
* **`0`, and never `-1`.** Chromium leaves the whole of a shadow host at a
|
|
531
|
+
* negative `tabindex` out of the Tab order, its own controls and what is
|
|
532
|
+
* slotted into it alike, so a turn at `-1` lost its links and its Try again
|
|
533
|
+
* once a reader had paged through it. The stop moves with the reader, and a
|
|
534
|
+
* `tabindex` you wrote on a turn is left as it is.
|
|
535
|
+
*/
|
|
536
|
+
private visit;
|
|
526
537
|
/** The first control in the regions `names` holds, in their order. */
|
|
527
538
|
private firstControlIn;
|
|
528
539
|
/**
|
|
@@ -524,7 +524,9 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
524
524
|
}
|
|
525
525
|
async focusTurn(at) {
|
|
526
526
|
const count = this.fed ? this.turns.length : this.messages.length;
|
|
527
|
-
|
|
527
|
+
const log = this.log;
|
|
528
|
+
const scrolls = log !== null && log !== undefined && log.scrollHeight > log.clientHeight;
|
|
529
|
+
if (at < count - 1 && this.pinned && scrolls) {
|
|
528
530
|
this.requestPinned(false);
|
|
529
531
|
}
|
|
530
532
|
if (this.fed) {
|
|
@@ -537,17 +539,27 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
537
539
|
this.requestUpdate();
|
|
538
540
|
await this.updateComplete;
|
|
539
541
|
}
|
|
540
|
-
drawn()
|
|
542
|
+
const turn = drawn();
|
|
543
|
+
if (turn) {
|
|
544
|
+
this.visit(turn);
|
|
545
|
+
}
|
|
541
546
|
return;
|
|
542
547
|
}
|
|
543
548
|
const message = this.messages[at];
|
|
544
|
-
if (
|
|
545
|
-
|
|
549
|
+
if (message) {
|
|
550
|
+
this.visit(message);
|
|
546
551
|
}
|
|
547
|
-
|
|
548
|
-
|
|
552
|
+
}
|
|
553
|
+
visit(turn) {
|
|
554
|
+
if (this.stop !== turn) {
|
|
555
|
+
this.stop?.removeAttribute('tabindex');
|
|
556
|
+
this.stop = undefined;
|
|
557
|
+
if (!turn.hasAttribute('tabindex')) {
|
|
558
|
+
turn.setAttribute('tabindex', '0');
|
|
559
|
+
this.stop = turn;
|
|
560
|
+
}
|
|
549
561
|
}
|
|
550
|
-
|
|
562
|
+
turn.focus();
|
|
551
563
|
}
|
|
552
564
|
firstControlIn(names) {
|
|
553
565
|
for (const name of names) {
|
|
@@ -582,7 +594,6 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
582
594
|
.turnWords=${this.words}
|
|
583
595
|
.turnPosition=${first + offset + 1}
|
|
584
596
|
.turnCount=${this.turns.length}
|
|
585
|
-
tabindex="-1"
|
|
586
597
|
.failedLabel=${this.failedLabel}
|
|
587
598
|
state=${turn.state ?? nothing}
|
|
588
599
|
>${turn.text}</${message}>
|
|
@@ -8,11 +8,21 @@
|
|
|
8
8
|
* depends on, out of a component library. What is left is punctuation: a header
|
|
9
9
|
* per file, a `@@` per hunk, and a first character per line.
|
|
10
10
|
*
|
|
11
|
+
* **Within a changed line it does compare.** A removed line and the added line
|
|
12
|
+
* paired with it are compared word by word, which takes two short strings and
|
|
13
|
+
* no dependency, so a reader sees which words changed without comparing two
|
|
14
|
+
* whole lines by eye.
|
|
15
|
+
*
|
|
11
16
|
* **Line numbers come from the hunk headers**, so they are the file's numbers
|
|
12
17
|
* rather than the diff's, which is the only kind a reader can look up. A line
|
|
13
18
|
* that is not in a hunk is not in the output: file headers, `index` lines and
|
|
14
19
|
* `diff --git` are metadata, and a name is taken from them and nothing else.
|
|
15
20
|
*/
|
|
21
|
+
/** A run of a changed line's text, and whether the line it is paired with lacks it. */
|
|
22
|
+
export interface DiffPiece {
|
|
23
|
+
changed: boolean;
|
|
24
|
+
text: string;
|
|
25
|
+
}
|
|
16
26
|
/** One line of a hunk, as the unified format marks it. */
|
|
17
27
|
export interface DiffLine {
|
|
18
28
|
kind: 'added' | 'context' | 'removed';
|
|
@@ -21,6 +31,11 @@ export interface DiffLine {
|
|
|
21
31
|
/** The number on the new side, absent for a line that only the old side has. */
|
|
22
32
|
new?: number;
|
|
23
33
|
text: string;
|
|
34
|
+
/**
|
|
35
|
+
* The line's text in runs, where it replaced a line or was replaced by one
|
|
36
|
+
* and the two share some words. Absent for every other line.
|
|
37
|
+
*/
|
|
38
|
+
pieces?: DiffPiece[];
|
|
24
39
|
}
|
|
25
40
|
/** A `@@` header and the lines under it. */
|
|
26
41
|
export interface DiffHunk {
|
|
@@ -51,6 +66,16 @@ export interface DiffRow {
|
|
|
51
66
|
* before it rather than a line of its own, so it is dropped.
|
|
52
67
|
*/
|
|
53
68
|
export declare const parsePatch: (patch: string) => DiffFile[];
|
|
69
|
+
/**
|
|
70
|
+
* The runs of two paired lines that the other line does not have, by the
|
|
71
|
+
* longest run of words they share in order.
|
|
72
|
+
*
|
|
73
|
+
* **Space between two changes is part of the change**, so "a b" replaced by
|
|
74
|
+
* "c d" is one run, with no gap for the eye to bridge. Two lines
|
|
75
|
+
* that share no word share nothing worth marking: the whole line is the
|
|
76
|
+
* change, which its own wash already says, and `undefined` draws it whole.
|
|
77
|
+
*/
|
|
78
|
+
export declare const changedWords: (before: string, after: string) => [DiffPiece[], DiffPiece[]] | undefined;
|
|
54
79
|
/**
|
|
55
80
|
* A hunk's lines paired for a side-by-side view.
|
|
56
81
|
*
|
|
@@ -60,3 +85,20 @@ export declare const parsePatch: (patch: string) => DiffFile[];
|
|
|
60
85
|
* across rather than hunting for it further down.
|
|
61
86
|
*/
|
|
62
87
|
export declare const pairLines: (lines: DiffLine[]) => DiffRow[];
|
|
88
|
+
/** A stretch of a hunk's lines: drawn, or unchanged lines folded behind a control. */
|
|
89
|
+
export interface DiffRun {
|
|
90
|
+
/** Whether the lines are unchanged ones folded away. */
|
|
91
|
+
folded: boolean;
|
|
92
|
+
lines: DiffLine[];
|
|
93
|
+
/** Where the run starts among the hunk's lines, which names a fold. */
|
|
94
|
+
start: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* A hunk's lines with each long run of unchanged lines folded, keeping `keep`
|
|
98
|
+
* lines on the side of each change, as a code host draws a diff with more
|
|
99
|
+
* context than a reader needs.
|
|
100
|
+
*
|
|
101
|
+
* A fold hides two lines or more: a control in place of one line is no
|
|
102
|
+
* shorter than the line.
|
|
103
|
+
*/
|
|
104
|
+
export declare const foldContext: (lines: DiffLine[], keep: number) => DiffRun[];
|