@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
package/dist/code-block/patch.js
CHANGED
|
@@ -63,8 +63,79 @@ export const parsePatch = (patch) => {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
+
for (const one of files) {
|
|
67
|
+
for (const { lines } of one.hunks) {
|
|
68
|
+
markWords(lines);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
66
71
|
return files;
|
|
67
72
|
};
|
|
73
|
+
const TOKEN = /[\p{L}\p{N}_]+|\s+|[^\p{L}\p{N}_\s]/gu;
|
|
74
|
+
const MOST_TOKENS = 200;
|
|
75
|
+
const joined = (pieces) => {
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const piece of pieces) {
|
|
78
|
+
const last = out.at(-1);
|
|
79
|
+
if (last && last.changed === piece.changed) {
|
|
80
|
+
last.text += piece.text;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
out.push({ ...piece });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
};
|
|
88
|
+
export const changedWords = (before, after) => {
|
|
89
|
+
const a = before.match(TOKEN) ?? [];
|
|
90
|
+
const b = after.match(TOKEN) ?? [];
|
|
91
|
+
if (a.length === 0 || b.length === 0 || a.length > MOST_TOKENS || b.length > MOST_TOKENS) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const width = b.length + 1;
|
|
95
|
+
const table = new Uint16Array((a.length + 1) * width);
|
|
96
|
+
for (let i = a.length - 1; i >= 0; i -= 1) {
|
|
97
|
+
for (let j = b.length - 1; j >= 0; j -= 1) {
|
|
98
|
+
table[i * width + j] =
|
|
99
|
+
a[i] === b[j]
|
|
100
|
+
? table[(i + 1) * width + j + 1] + 1
|
|
101
|
+
: Math.max(table[(i + 1) * width + j], table[i * width + j + 1]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const left = [];
|
|
105
|
+
const right = [];
|
|
106
|
+
let shared = false;
|
|
107
|
+
let i = 0;
|
|
108
|
+
let j = 0;
|
|
109
|
+
while (i < a.length || j < b.length) {
|
|
110
|
+
if (i < a.length && j < b.length && a[i] === b[j]) {
|
|
111
|
+
shared ||= a[i].trim() !== '';
|
|
112
|
+
left.push({ changed: false, text: a[i] });
|
|
113
|
+
right.push({ changed: false, text: b[j] });
|
|
114
|
+
i += 1;
|
|
115
|
+
j += 1;
|
|
116
|
+
}
|
|
117
|
+
else if (j >= b.length ||
|
|
118
|
+
(i < a.length &&
|
|
119
|
+
table[(i + 1) * width + j] >= table[i * width + j + 1])) {
|
|
120
|
+
left.push({ changed: true, text: a[i] });
|
|
121
|
+
i += 1;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
right.push({ changed: true, text: b[j] });
|
|
125
|
+
j += 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!shared) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
const bridged = (pieces) => joined(pieces.map((piece, at) => !piece.changed &&
|
|
132
|
+
piece.text.trim() === '' &&
|
|
133
|
+
pieces[at - 1]?.changed &&
|
|
134
|
+
pieces[at + 1]?.changed
|
|
135
|
+
? { ...piece, changed: true }
|
|
136
|
+
: piece));
|
|
137
|
+
return [bridged(left), bridged(right)];
|
|
138
|
+
};
|
|
68
139
|
export const pairLines = (lines) => {
|
|
69
140
|
const rows = [];
|
|
70
141
|
let at = 0;
|
|
@@ -91,3 +162,50 @@ export const pairLines = (lines) => {
|
|
|
91
162
|
}
|
|
92
163
|
return rows;
|
|
93
164
|
};
|
|
165
|
+
const markWords = (lines) => {
|
|
166
|
+
for (const { left, right } of pairLines(lines)) {
|
|
167
|
+
if (left?.kind !== 'removed' || right?.kind !== 'added') {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const pieces = changedWords(left.text, right.text);
|
|
171
|
+
if (pieces) {
|
|
172
|
+
;
|
|
173
|
+
[left.pieces, right.pieces] = pieces;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
export const foldContext = (lines, keep) => {
|
|
178
|
+
const runs = [];
|
|
179
|
+
const add = (start, end, folded) => {
|
|
180
|
+
if (end <= start) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const last = runs.at(-1);
|
|
184
|
+
if (last && !last.folded && !folded) {
|
|
185
|
+
last.lines.push(...lines.slice(start, end));
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
runs.push({ folded, lines: lines.slice(start, end), start });
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
let at = 0;
|
|
192
|
+
while (at < lines.length) {
|
|
193
|
+
const context = lines[at]?.kind === 'context';
|
|
194
|
+
let end = at;
|
|
195
|
+
while (end < lines.length && (lines[end]?.kind === 'context') === context) {
|
|
196
|
+
end += 1;
|
|
197
|
+
}
|
|
198
|
+
const head = context && at > 0 ? keep : 0;
|
|
199
|
+
const tail = context && end < lines.length ? keep : 0;
|
|
200
|
+
if (context && end - at - head - tail >= 2) {
|
|
201
|
+
add(at, at + head, false);
|
|
202
|
+
add(at + head, end - tail, true);
|
|
203
|
+
add(end - tail, end, false);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
add(at, end, false);
|
|
207
|
+
}
|
|
208
|
+
at = end;
|
|
209
|
+
}
|
|
210
|
+
return runs;
|
|
211
|
+
};
|
|
@@ -45,9 +45,14 @@ import '../../copy-button/index.js';
|
|
|
45
45
|
* removed. The block never computes a diff itself. Line numbers come from the
|
|
46
46
|
* hunk headers and are hidden from the accessibility tree. Changed lines are
|
|
47
47
|
* `<ins>` and `<del>` with the `+` and `-` kept as text, so the change does not
|
|
48
|
-
* depend on color.
|
|
48
|
+
* depend on color. Where a removed line is paired with the added line that
|
|
49
|
+
* replaced it, the words each does not share with the other are the `changed`
|
|
50
|
+
* part, so the eye goes to what changed within the line. Each file in the patch is its own block under its own name,
|
|
49
51
|
* and its copy button copies the new version of the file. `variant="split"`
|
|
50
52
|
* shows the old version beside the new, each removal opposite its replacement.
|
|
53
|
+
* `context-lines` folds each long run of unchanged lines, keeping that many
|
|
54
|
+
* beside each change, behind a button that shows them and hides them again,
|
|
55
|
+
* as a code host draws a diff written with more context than a reader needs.
|
|
51
56
|
*
|
|
52
57
|
* **A block that scrolls is a tab stop.** While its lines run past its width or
|
|
53
58
|
* its cap, the scroller is a region named for the block, such as "Code,
|
|
@@ -83,6 +88,7 @@ import '../../copy-button/index.js';
|
|
|
83
88
|
* @csspart actions - The box in the header that holds the `actions` slot, drawn while something is slotted there.
|
|
84
89
|
* @csspart added - In a diff, a line only the new version has, an `<ins>`.
|
|
85
90
|
* @csspart block - The scrolling `<pre>`.
|
|
91
|
+
* @csspart changed - In a diff, the words of a changed line that the line opposite it does not have, inside its `text`.
|
|
86
92
|
* @csspart code - The `<code>` inside it.
|
|
87
93
|
* @csspart context - In a diff, a line both versions share.
|
|
88
94
|
* @csspart copy - The copy button. It sits in the header when there is one, and beside the code otherwise.
|
|
@@ -104,6 +110,7 @@ import '../../copy-button/index.js';
|
|
|
104
110
|
* @csspart expand - The button under a capped block that shows every line, and fewer again. Drawn when `expandable` is set and the cap hides something.
|
|
105
111
|
* @csspart file - In a diff, one file's header and block.
|
|
106
112
|
* @csspart filler - In a split diff, the empty row opposite a line the other side does not have.
|
|
113
|
+
* @csspart fold - In a diff with `context-lines`, the button that shows a folded run of unchanged lines, and hides it again, saying which with `aria-expanded`.
|
|
107
114
|
* @csspart frame - The code, and the copy button beside it where there is no header.
|
|
108
115
|
* @csspart gutter - The line numbers beside the code, shown when `line-numbers` is set.
|
|
109
116
|
* @csspart header - The bar that holds the name and the copy button, shown when there is a name.
|
|
@@ -148,6 +155,12 @@ export declare class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
148
155
|
* The expand button's words while the block shows every line.
|
|
149
156
|
*/
|
|
150
157
|
collapseLabel: string;
|
|
158
|
+
/**
|
|
159
|
+
* In a diff, how many unchanged lines to keep beside each change. Each
|
|
160
|
+
* longer run of unchanged lines is folded behind a button that shows it.
|
|
161
|
+
* Unset shows every line the diff holds.
|
|
162
|
+
*/
|
|
163
|
+
contextLines?: number;
|
|
151
164
|
/**
|
|
152
165
|
* The copy button's name and tooltip text after a copy.
|
|
153
166
|
*/
|
|
@@ -185,6 +198,12 @@ export declare class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
185
198
|
* The expand button's words while the cap hides lines.
|
|
186
199
|
*/
|
|
187
200
|
expandLabel: string;
|
|
201
|
+
/**
|
|
202
|
+
* The words of a button that hides a run of unchanged lines it showed, in a
|
|
203
|
+
* diff with `context-lines`. `{count}` is how many, and each language
|
|
204
|
+
* writes the forms it has.
|
|
205
|
+
*/
|
|
206
|
+
hideUnchangedLabel: string;
|
|
188
207
|
/**
|
|
189
208
|
* Turns the code into highlighted markup, which the block draws in place of
|
|
190
209
|
* the plain text.
|
|
@@ -263,6 +282,11 @@ export declare class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
263
282
|
* `false`.
|
|
264
283
|
*/
|
|
265
284
|
noCopy: boolean;
|
|
285
|
+
/**
|
|
286
|
+
* The words of a button that shows a folded run of unchanged lines, in a
|
|
287
|
+
* diff with `context-lines`. `{count}` is how many.
|
|
288
|
+
*/
|
|
289
|
+
showUnchangedLabel: string;
|
|
266
290
|
/**
|
|
267
291
|
* How a diff's two versions are arranged. It has no effect without `diff`.
|
|
268
292
|
*
|
|
@@ -295,6 +319,18 @@ export declare class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
295
319
|
private parsed;
|
|
296
320
|
/** The files in the content, when it is a diff and it can be read yet. */
|
|
297
321
|
private get files();
|
|
322
|
+
/** The folds a reader opened, by file, hunk and the line each starts at. */
|
|
323
|
+
private readonly unfolded;
|
|
324
|
+
/** A hunk's lines in runs, folded where `context-lines` asks. */
|
|
325
|
+
private runsOf;
|
|
326
|
+
/**
|
|
327
|
+
* The button over a folded run, and the run once it is open.
|
|
328
|
+
*
|
|
329
|
+
* A disclosure, so focus stays on the button as the lines appear under it.
|
|
330
|
+
* The new side of a split diff draws the same words hidden from the
|
|
331
|
+
* accessibility tree, there to keep the rows level and to answer a press.
|
|
332
|
+
*/
|
|
333
|
+
private renderFold;
|
|
298
334
|
/**
|
|
299
335
|
* The code as text, read from what the default slot is given rather than
|
|
300
336
|
* stored.
|
|
@@ -12,7 +12,7 @@ import { html } from 'lit/static-html.js';
|
|
|
12
12
|
import '../../copy-button/index.js';
|
|
13
13
|
import { highlightedLines } from '../highlight.js';
|
|
14
14
|
import { gutterNumbers, lineCount, lineRuns, lineSpans } from '../lines.js';
|
|
15
|
-
import { pairLines, parsePatch } from '../patch.js';
|
|
15
|
+
import { foldContext, pairLines, parsePatch, } from '../patch.js';
|
|
16
16
|
let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
17
17
|
constructor() {
|
|
18
18
|
super(...arguments);
|
|
@@ -25,15 +25,18 @@ let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
25
25
|
this.diff = false;
|
|
26
26
|
this.expandable = false;
|
|
27
27
|
this.expandLabel = 'Show all lines';
|
|
28
|
+
this.hideUnchangedLabel = '{count, plural, one {Hide # unchanged line} other {Hide # unchanged lines}}';
|
|
28
29
|
this.label = '';
|
|
29
30
|
this.language = '';
|
|
30
31
|
this.lineNumbers = false;
|
|
31
32
|
this.markLines = '';
|
|
32
33
|
this.noCopy = false;
|
|
34
|
+
this.showUnchangedLabel = '{count, plural, one {Show # unchanged line} other {Show # unchanged lines}}';
|
|
33
35
|
this.wrap = false;
|
|
34
36
|
this.wrapLabel = 'Wrap lines';
|
|
35
37
|
this.wrappable = false;
|
|
36
38
|
this.parsed = { files: [], text: '' };
|
|
39
|
+
this.unfolded = new Set();
|
|
37
40
|
this.capped = false;
|
|
38
41
|
this.expanded = false;
|
|
39
42
|
this.measure = () => {
|
|
@@ -69,9 +72,32 @@ let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
69
72
|
const text = this.text;
|
|
70
73
|
if (text !== this.parsed.text) {
|
|
71
74
|
this.parsed = { files: parsePatch(text), text };
|
|
75
|
+
this.unfolded.clear();
|
|
72
76
|
}
|
|
73
77
|
return this.parsed.files;
|
|
74
78
|
}
|
|
79
|
+
runsOf(hunk) {
|
|
80
|
+
const keep = this.contextLines;
|
|
81
|
+
return keep === undefined || keep < 0
|
|
82
|
+
? [{ folded: false, lines: hunk.lines, start: 0 }]
|
|
83
|
+
: foldContext(hunk.lines, Math.floor(keep));
|
|
84
|
+
}
|
|
85
|
+
renderFold(run, key, line, hidden = false) {
|
|
86
|
+
const open = this.unfolded.has(key);
|
|
87
|
+
const words = formatWords(this, open ? 'hideUnchangedLabel' : 'showUnchangedLabel', {
|
|
88
|
+
count: run.lines.length,
|
|
89
|
+
});
|
|
90
|
+
const toggle = () => {
|
|
91
|
+
if (!this.unfolded.delete(key)) {
|
|
92
|
+
this.unfolded.add(key);
|
|
93
|
+
}
|
|
94
|
+
this.requestUpdate();
|
|
95
|
+
};
|
|
96
|
+
const control = hidden
|
|
97
|
+
? html `<span part="fold" aria-hidden="true" @click=${toggle}>${words}</span>`
|
|
98
|
+
: html `<button part="fold" type="button" aria-expanded=${open ? 'true' : 'false'} @click=${toggle}>${words}</button>`;
|
|
99
|
+
return html `${control}\n${open ? run.lines.map(line) : nothing}`;
|
|
100
|
+
}
|
|
75
101
|
get text() {
|
|
76
102
|
if (this.hydrating) {
|
|
77
103
|
return '';
|
|
@@ -319,7 +345,10 @@ let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
319
345
|
const numbers = side
|
|
320
346
|
? this.renderNumber(side === 'old' ? line.old : line.new, width)
|
|
321
347
|
: html `${this.renderNumber(line.old, width)}${this.renderNumber(line.new, width)}`;
|
|
322
|
-
const
|
|
348
|
+
const words = line.pieces
|
|
349
|
+
? line.pieces.map((piece) => piece.changed ? html `<span part="changed">${piece.text}</span>` : piece.text)
|
|
350
|
+
: line.text;
|
|
351
|
+
const text = html `<span part="text">${words}\n</span>`;
|
|
323
352
|
if (line.kind === 'added') {
|
|
324
353
|
return html `<ins part="added">${numbers}<span part="marker">+</span>${text}</ins>`;
|
|
325
354
|
}
|
|
@@ -331,16 +360,22 @@ let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
331
360
|
renderHunk(hunk, hidden = false) {
|
|
332
361
|
return html `<span part="hunk" aria-hidden=${hidden ? 'true' : nothing}>${hunk.header}\n</span>`;
|
|
333
362
|
}
|
|
334
|
-
renderSplit(file, width) {
|
|
335
|
-
const hunks = file.hunks.map((hunk) => ({ hunk,
|
|
363
|
+
renderSplit(file, width, at) {
|
|
364
|
+
const hunks = file.hunks.map((hunk) => ({ hunk, runs: this.runsOf(hunk) }));
|
|
336
365
|
const filler = html `<span part="filler" aria-hidden="true">\n</span>`;
|
|
366
|
+
const side = (old) => hunks.map(({ hunk, runs }, h) => html `${this.renderHunk(hunk, !old)}${runs.map((run) => run.folded
|
|
367
|
+
? this.renderFold(run, `${at}:${h}:${run.start}`, (line) => this.renderLine(line, width, old ? 'old' : 'new'), !old)
|
|
368
|
+
: pairLines(run.lines).map(({ left, right }) => {
|
|
369
|
+
const line = old ? left : right;
|
|
370
|
+
return line ? this.renderLine(line, width, old ? 'old' : 'new') : filler;
|
|
371
|
+
}))}`);
|
|
337
372
|
return html `<div part="split">
|
|
338
|
-
<pre part="old" translate="no"><code part="code">${
|
|
339
|
-
<pre part="new" translate="no"><code part="code">${
|
|
373
|
+
<pre part="old" translate="no"><code part="code">${side(true)}</code></pre>
|
|
374
|
+
<pre part="new" translate="no"><code part="code">${side(false)}</code></pre>
|
|
340
375
|
</div>`;
|
|
341
376
|
}
|
|
342
377
|
renderDiff(files) {
|
|
343
|
-
return html `${files.map((file) => {
|
|
378
|
+
return html `${files.map((file, at) => {
|
|
344
379
|
const name = file.name || this.label;
|
|
345
380
|
const widest = Math.max(0, ...file.hunks.flatMap((hunk) => hunk.lines.map((line) => Math.max(line.old ?? 0, line.new ?? 0))));
|
|
346
381
|
const width = String(widest).length;
|
|
@@ -352,8 +387,10 @@ let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
352
387
|
${this.renderHeader(name, now)}
|
|
353
388
|
<div part="frame">
|
|
354
389
|
${this.variant === 'split'
|
|
355
|
-
? this.renderSplit(file, width)
|
|
356
|
-
: html `<pre part="block" translate="no"><code part="code">${file.hunks.map((hunk) => html `${this.renderHunk(hunk)}${hunk.
|
|
390
|
+
? this.renderSplit(file, width, at)
|
|
391
|
+
: html `<pre part="block" translate="no"><code part="code">${file.hunks.map((hunk, h) => html `${this.renderHunk(hunk)}${this.runsOf(hunk).map((run) => run.folded
|
|
392
|
+
? this.renderFold(run, `${at}:${h}:${run.start}`, (line) => this.renderLine(line, width))
|
|
393
|
+
: run.lines.map((line) => this.renderLine(line, width)))}`)}</code></pre>`}
|
|
357
394
|
${this.headed(name) ? nothing : this.renderCopy(now)}
|
|
358
395
|
</div>
|
|
359
396
|
</div>`;
|
|
@@ -372,6 +409,9 @@ __decorate([
|
|
|
372
409
|
words,
|
|
373
410
|
property({ attribute: 'collapse-label' })
|
|
374
411
|
], HelixHeadlessCodeBlock.prototype, "collapseLabel", void 0);
|
|
412
|
+
__decorate([
|
|
413
|
+
property({ attribute: 'context-lines', type: Number })
|
|
414
|
+
], HelixHeadlessCodeBlock.prototype, "contextLines", void 0);
|
|
375
415
|
__decorate([
|
|
376
416
|
words,
|
|
377
417
|
property({ attribute: 'copied-label' })
|
|
@@ -394,6 +434,10 @@ __decorate([
|
|
|
394
434
|
words,
|
|
395
435
|
property({ attribute: 'expand-label' })
|
|
396
436
|
], HelixHeadlessCodeBlock.prototype, "expandLabel", void 0);
|
|
437
|
+
__decorate([
|
|
438
|
+
words,
|
|
439
|
+
property({ attribute: 'hide-unchanged-label' })
|
|
440
|
+
], HelixHeadlessCodeBlock.prototype, "hideUnchangedLabel", void 0);
|
|
397
441
|
__decorate([
|
|
398
442
|
property({ attribute: false })
|
|
399
443
|
], HelixHeadlessCodeBlock.prototype, "highlight", void 0);
|
|
@@ -412,6 +456,10 @@ __decorate([
|
|
|
412
456
|
__decorate([
|
|
413
457
|
property({ attribute: 'no-copy', reflect: true, type: Boolean })
|
|
414
458
|
], HelixHeadlessCodeBlock.prototype, "noCopy", void 0);
|
|
459
|
+
__decorate([
|
|
460
|
+
words,
|
|
461
|
+
property({ attribute: 'show-unchanged-label' })
|
|
462
|
+
], HelixHeadlessCodeBlock.prototype, "showUnchangedLabel", void 0);
|
|
415
463
|
__decorate([
|
|
416
464
|
property({ reflect: true })
|
|
417
465
|
], HelixHeadlessCodeBlock.prototype, "variant", void 0);
|
|
@@ -22,8 +22,10 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
|
|
|
22
22
|
* gray after you drag to the bottom of the square and back.
|
|
23
23
|
*
|
|
24
24
|
* The square is one tab stop. In it, the up and down arrows move brightness
|
|
25
|
-
* and the left and right arrows move saturation,
|
|
26
|
-
* brightness by ten
|
|
25
|
+
* and the left and right arrows move saturation, by ten with Shift. PageUp and
|
|
26
|
+
* PageDown move brightness by ten, and Home and End move saturation by ten,
|
|
27
|
+
* turned on a right-to-left page as the arrows are, as React Aria's ColorArea
|
|
28
|
+
* does. The picker sends `input` as the color moves and `change`
|
|
27
29
|
* when a drag, a key, a typed value or a preset settles it, as
|
|
28
30
|
* `<input type="color">` does.
|
|
29
31
|
*
|
|
@@ -546,7 +548,9 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
|
|
|
546
548
|
* its own one axis: on the saturation input, ArrowUp moved the handle
|
|
547
549
|
* sideways. Here the vertical keys move brightness and the horizontal keys
|
|
548
550
|
* move saturation, turned on a right-to-left page as the pointer is, which is
|
|
549
|
-
* what React Aria's ColorArea does.
|
|
551
|
+
* what React Aria's ColorArea does. Shift makes an arrow a step of ten, and
|
|
552
|
+
* Home and End are saturation's steps of ten, as PageUp and PageDown are
|
|
553
|
+
* brightness's.
|
|
550
554
|
*/
|
|
551
555
|
private readonly handleAreaKeydown;
|
|
552
556
|
/**
|
|
@@ -211,11 +211,14 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
211
211
|
const color = this.color;
|
|
212
212
|
const along = isRtl(this) ? -1 : 1;
|
|
213
213
|
const held = (at) => clamp(at, 0, 100);
|
|
214
|
+
const step = event.shiftKey ? 10 : 1;
|
|
214
215
|
const moves = {
|
|
215
|
-
ArrowDown: { brightness: held(color.brightness -
|
|
216
|
-
ArrowLeft: { saturation: held(color.saturation - along) },
|
|
217
|
-
ArrowRight: { saturation: held(color.saturation + along) },
|
|
218
|
-
ArrowUp: { brightness: held(color.brightness +
|
|
216
|
+
ArrowDown: { brightness: held(color.brightness - step) },
|
|
217
|
+
ArrowLeft: { saturation: held(color.saturation - along * step) },
|
|
218
|
+
ArrowRight: { saturation: held(color.saturation + along * step) },
|
|
219
|
+
ArrowUp: { brightness: held(color.brightness + step) },
|
|
220
|
+
End: { saturation: held(color.saturation + along * 10) },
|
|
221
|
+
Home: { saturation: held(color.saturation - along * 10) },
|
|
219
222
|
PageDown: { brightness: held(color.brightness - 10) },
|
|
220
223
|
PageUp: { brightness: held(color.brightness + 10) },
|
|
221
224
|
};
|
|
@@ -26,6 +26,7 @@ declare const HelixHeadlessCombobox_base: (new (...args: any[]) => import("@fusi
|
|
|
26
26
|
* | Key | What it does |
|
|
27
27
|
* |---|---|
|
|
28
28
|
* | Down, Up | On a closed field, opens the list on the chosen option, or the first or the last. In the open list, moves the highlight, round from the last option to the first. |
|
|
29
|
+
* | Alt with Down, Alt with Up | Opens the list without moving the highlight, and closes it, choosing nothing. |
|
|
29
30
|
* | Page Down, Page Up | In the open list, moves the highlight by as many options as the list shows at once. |
|
|
30
31
|
* | Enter | Chooses the highlighted option. With nothing left in the list, creates what was typed with `creatable`, or takes it with `allows-custom-value`. On a closed field, submits the form. |
|
|
31
32
|
* | Escape | Closes the list and puts the chosen words back. On a closed field, a second Escape clears a single choice. |
|
|
@@ -154,7 +154,11 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
154
154
|
return;
|
|
155
155
|
}
|
|
156
156
|
if (!this.open) {
|
|
157
|
-
if (event.key === 'ArrowDown'
|
|
157
|
+
if (event.key === 'ArrowDown' && event.altKey) {
|
|
158
|
+
event.preventDefault();
|
|
159
|
+
this.requestOpen(true);
|
|
160
|
+
}
|
|
161
|
+
else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
158
162
|
event.preventDefault();
|
|
159
163
|
const fallback = event.key === 'ArrowDown' ? 'first' : 'last';
|
|
160
164
|
this.opening = this.values.length > 0 ? 'selected' : fallback;
|
|
@@ -162,6 +166,11 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
162
166
|
}
|
|
163
167
|
return;
|
|
164
168
|
}
|
|
169
|
+
if (event.key === 'ArrowUp' && event.altKey) {
|
|
170
|
+
event.preventDefault();
|
|
171
|
+
this.close();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
165
174
|
if (this.moveAroundCreate(event)) {
|
|
166
175
|
void this.updateComplete.then(() => this.linkActive());
|
|
167
176
|
return;
|
|
@@ -215,6 +215,7 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
|
|
|
215
215
|
*
|
|
216
216
|
* @cssstate empty - While there are no rows to show and none are loading.
|
|
217
217
|
* @cssstate error - While `error-text` is set.
|
|
218
|
+
* @cssstate loading - While `loading` is set, as the rows are on their way.
|
|
218
219
|
*/
|
|
219
220
|
export declare class HelixHeadlessDataTable extends HelixHeadlessDataTable_base {
|
|
220
221
|
/**
|
|
@@ -984,6 +984,7 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
|
|
|
984
984
|
this.markEmpty();
|
|
985
985
|
this.toggleState('error', this.errorText !== '');
|
|
986
986
|
this.toggleState('empty', !this.loading && !this.hasRows);
|
|
987
|
+
this.toggleState('loading', this.loading);
|
|
987
988
|
this.assign();
|
|
988
989
|
this.announceSort();
|
|
989
990
|
this.quietControls();
|
|
@@ -335,7 +335,13 @@ registerElementEvents({
|
|
|
335
335
|
'volumechange',
|
|
336
336
|
'waiting',
|
|
337
337
|
],
|
|
338
|
-
'helix-headless-video-playlist': [
|
|
338
|
+
'helix-headless-video-playlist': [
|
|
339
|
+
'helix-activate',
|
|
340
|
+
'helix-before-activate',
|
|
341
|
+
'helix-before-reorder',
|
|
342
|
+
'helix-end',
|
|
343
|
+
'helix-reorder',
|
|
344
|
+
],
|
|
339
345
|
'helix-headless-video-playlist-item': ['helix-select'],
|
|
340
346
|
'helix-headless-video-playlist-menu': ['helix-select'],
|
|
341
347
|
'helix-headless-virtual-list': [
|
|
@@ -46,7 +46,8 @@ declare const HelixHeadlessMap_base: (new (...args: any[]) => import("@fusion.de
|
|
|
46
46
|
* same `group`, such as a route and its dotted legs, share one tile. A tile
|
|
47
47
|
* sends `helix-before-toggle` from the layer first, so you can store the
|
|
48
48
|
* choice or refuse it, and `helix-toggle` from it once `hidden` holds the
|
|
49
|
-
* choice.
|
|
49
|
+
* choice. Escape closes the menu and returns focus to its button, and the menu
|
|
50
|
+
* closes when focus moves on from it or a press lands outside the map.
|
|
50
51
|
*
|
|
51
52
|
* Imagery, a `helix-map-raster` with `ground`, is a map type and not a tile.
|
|
52
53
|
* The menu opens with a card for each type, Standard, Hybrid, and Satellite,
|
|
@@ -315,6 +316,12 @@ export declare class HelixHeadlessMap extends HelixHeadlessMap_base {
|
|
|
315
316
|
private hiding?;
|
|
316
317
|
/** Closes the layers menu on a press anywhere outside the frame. */
|
|
317
318
|
private readonly handleOutside;
|
|
319
|
+
/**
|
|
320
|
+
* Closes the layers menu when focus moves on from its button and the menu,
|
|
321
|
+
* as a Tab past the last tile does. Focus leaving the page, with nothing
|
|
322
|
+
* focused in its place, leaves the menu as it is.
|
|
323
|
+
*/
|
|
324
|
+
private readonly handleMenuFocusOut;
|
|
318
325
|
/**
|
|
319
326
|
* Keys pressed in the layers menu. Escape closes it and returns to its
|
|
320
327
|
* button, and no key reaches the region, where the arrows would pan the map.
|
|
@@ -82,6 +82,16 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
|
|
|
82
82
|
this.layersOpen = false;
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
|
+
this.handleMenuFocusOut = (event) => {
|
|
86
|
+
const to = event.relatedTarget;
|
|
87
|
+
if (!(this.layersOpen && to)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const owners = this.renderRoot.querySelectorAll('[part="layers-toggle"], [part="layers-menu"]');
|
|
91
|
+
if (![...owners].some((owner) => owner.contains(to))) {
|
|
92
|
+
this.layersOpen = false;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
85
95
|
this.handleMenuKeydown = (event) => {
|
|
86
96
|
event.stopPropagation();
|
|
87
97
|
if (event.key === 'Escape' && this.layersOpen) {
|
|
@@ -402,6 +412,7 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
|
|
|
402
412
|
this.dispatchEvent(new HelixMapPartChangeEvent({ kind: 'menu' }));
|
|
403
413
|
}
|
|
404
414
|
}}
|
|
415
|
+
@focusout=${this.handleMenuFocusOut}
|
|
405
416
|
@keydown=${this.handleMenuKeydown}
|
|
406
417
|
>
|
|
407
418
|
<slot name="layers-icon">
|
|
@@ -416,6 +427,7 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
|
|
|
416
427
|
role="group"
|
|
417
428
|
aria-label=${this.layersLabel}
|
|
418
429
|
?hidden=${!this.layersOpen}
|
|
430
|
+
@focusout=${this.handleMenuFocusOut}
|
|
419
431
|
@keydown=${this.handleMenuKeydown}
|
|
420
432
|
>
|
|
421
433
|
${this.imagery.length > 0 ? this.renderMapTypes() : nothing}
|
|
@@ -10,16 +10,22 @@ import '../../kbd/index.js';
|
|
|
10
10
|
* - `checked` makes the row a choice.
|
|
11
11
|
* - With neither, the row is a command.
|
|
12
12
|
*
|
|
13
|
-
* The role follows the kind. A row with `
|
|
14
|
-
* `aria-checked`, and `exclusive`
|
|
15
|
-
* only one can be on. A row that opens a page
|
|
16
|
-
* `aria-haspopup`.
|
|
13
|
+
* The role follows the kind. A row with `checkable` or `checked` is
|
|
14
|
+
* `menuitemcheckbox` with `aria-checked`, and an `exclusive` row is
|
|
15
|
+
* `menuitemradio`, for a set where only one can be on. A row that opens a page
|
|
16
|
+
* keeps `menuitem` and sets `aria-haspopup`.
|
|
17
17
|
*
|
|
18
18
|
* Choosing a choice row checks or unchecks it before `helix-select` is sent,
|
|
19
19
|
* so a listener reads the new `checked`. An `exclusive` row turns on and turns
|
|
20
20
|
* off the `exclusive` rows next to it. Cancel `helix-before-toggle` to keep a
|
|
21
21
|
* row as it is and set `checked` yourself.
|
|
22
22
|
*
|
|
23
|
+
* Space on a choice row changes it and leaves the menu open, so a reader can
|
|
24
|
+
* set several in a row, as the APG's menu does. Enter and a press change it and
|
|
25
|
+
* close the menu. `close-on-select="false"` on the row keeps the menu open after
|
|
26
|
+
* any choice of it, and `close-on-select` on the row closes it after a Space as
|
|
27
|
+
* well.
|
|
28
|
+
*
|
|
23
29
|
* The menu sets whether the row's page is showing as a property, never as an
|
|
24
30
|
* attribute. An attribute written onto your element after it upgrades causes a
|
|
25
31
|
* hydration mismatch.
|
|
@@ -82,7 +88,8 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
|
82
88
|
* as a checkbox tells a reader its siblings are commands, which is the one
|
|
83
89
|
* thing they are not.
|
|
84
90
|
*
|
|
85
|
-
* `checked`
|
|
91
|
+
* `checked` and `exclusive` imply it, so a row that is on, or one of a set,
|
|
92
|
+
* never has to say both.
|
|
86
93
|
*
|
|
87
94
|
* Choosing the row checks or unchecks it, after a `helix-before-toggle` that
|
|
88
95
|
* you can cancel.
|
|
@@ -95,6 +102,14 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
|
95
102
|
* state you start the row in, and `:state(checked)` carries the current one.
|
|
96
103
|
*/
|
|
97
104
|
checked: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Whether choosing the row closes the menu.
|
|
107
|
+
*
|
|
108
|
+
* Unset takes the menu's `close-on-select`, which is on, and keeps the menu
|
|
109
|
+
* open after a Space on a choice row. Write `close-on-select="false"` for a
|
|
110
|
+
* row that changes something the reader is watching, such as a zoom step.
|
|
111
|
+
*/
|
|
112
|
+
closeOnSelect?: boolean;
|
|
98
113
|
/**
|
|
99
114
|
* Whether the row is disabled, so it cannot be chosen.
|
|
100
115
|
*/
|
|
@@ -116,8 +131,7 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
|
116
131
|
* it checked. `menuitemradio` tells a reader which kind of set they are in,
|
|
117
132
|
* where `menuitemcheckbox` says every row is independent.
|
|
118
133
|
*
|
|
119
|
-
*
|
|
120
|
-
* one of a set of anything.
|
|
134
|
+
* It makes the row a choice, so it needs no `checkable` beside it.
|
|
121
135
|
*/
|
|
122
136
|
exclusive: boolean;
|
|
123
137
|
/**
|
|
@@ -192,6 +206,17 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
|
192
206
|
* protected. Not a consumer's to read or set.
|
|
193
207
|
*/
|
|
194
208
|
readonly isMenuItem = true;
|
|
209
|
+
/**
|
|
210
|
+
* @internal Whether the last choice of this row asked the menu to stay open:
|
|
211
|
+
* a Space on a choice row, or `close-on-select="false"`. Read by the menu
|
|
212
|
+
* while it hears the row's `helix-select`.
|
|
213
|
+
*/
|
|
214
|
+
keepsOpen: boolean;
|
|
215
|
+
/**
|
|
216
|
+
* @internal Whether the row is a choice: it can be ticked, or is one of a
|
|
217
|
+
* set. Read by the menu to reserve the trailing column.
|
|
218
|
+
*/
|
|
219
|
+
get choice(): boolean;
|
|
195
220
|
/**
|
|
196
221
|
* Whether anything was slotted into `submenu`.
|
|
197
222
|
*
|
|
@@ -342,8 +367,14 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
|
342
367
|
private tick;
|
|
343
368
|
/** The other `exclusive` rows in this row's run of siblings. */
|
|
344
369
|
private exclusivePeers;
|
|
370
|
+
/**
|
|
371
|
+
* Chooses the row. `space` is whether a Space chose it, which keeps the menu
|
|
372
|
+
* open on a choice row unless the row says otherwise.
|
|
373
|
+
*/
|
|
345
374
|
private choose;
|
|
346
375
|
private readonly handleClick;
|
|
376
|
+
/** Whether the click on its way is the one a Space on a linked row sent. */
|
|
377
|
+
private spacePressed;
|
|
347
378
|
/**
|
|
348
379
|
* Enter and Space choose the row, which a `<div>` would not do on its own.
|
|
349
380
|
*
|