@fusion.dev/helix-headless 0.218.0 → 0.220.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.
Files changed (84) hide show
  1. package/api.json +1 -1
  2. package/custom-elements.json +1 -1
  3. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +13 -2
  4. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +19 -9
  5. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +16 -1
  6. package/dist/breadcrumbs/root/breadcrumbs-root.js +6 -0
  7. package/dist/button/root/button-root.d.ts +33 -0
  8. package/dist/button/root/button-root.js +13 -3
  9. package/dist/canvas/root/canvas-root.d.ts +11 -0
  10. package/dist/canvas/root/canvas-root.js +1 -0
  11. package/dist/chat/composer/chat-composer.d.ts +221 -6
  12. package/dist/chat/composer/chat-composer.js +249 -19
  13. package/dist/chat/message/chat-message.d.ts +7 -0
  14. package/dist/chat/message/chat-message.js +2 -1
  15. package/dist/chat/root/chat-root.d.ts +1 -0
  16. package/dist/chat/root/chat-root.js +1 -1
  17. package/dist/code-editor/root/code-editor-root.d.ts +48 -51
  18. package/dist/code-editor/root/code-editor-root.js +67 -249
  19. package/dist/copy-button/root/copy-button-root.js +3 -3
  20. package/dist/device-screen/root/device-screen-root.d.ts +11 -0
  21. package/dist/device-screen/root/device-screen-root.js +22 -10
  22. package/dist/dialog/root/dialog-root.js +6 -1
  23. package/dist/drilldown/item/drilldown-item.js +1 -1
  24. package/dist/element-events.generated.js +4 -1
  25. package/dist/filter-input/root/filter-input-root.d.ts +4 -3
  26. package/dist/filter-input/root/filter-input-root.js +1 -20
  27. package/dist/find/index.d.ts +1 -0
  28. package/dist/find/index.js +1 -0
  29. package/dist/find/root/find-root.d.ts +314 -0
  30. package/dist/find/root/find-root.js +577 -0
  31. package/dist/graph/root/graph-root.d.ts +11 -0
  32. package/dist/graph/root/graph-root.js +4 -1
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +1 -0
  35. package/dist/input/root/input-root.d.ts +6 -0
  36. package/dist/input/root/input-root.js +7 -0
  37. package/dist/lightbox/root/lightbox-root.d.ts +4 -1
  38. package/dist/lightbox/root/lightbox-root.js +10 -8
  39. package/dist/listbox/root/listbox-root.d.ts +21 -0
  40. package/dist/listbox/root/listbox-root.js +37 -2
  41. package/dist/log/group/log-group.d.ts +18 -3
  42. package/dist/log/group/log-group.js +31 -4
  43. package/dist/log/root/log-root.d.ts +13 -3
  44. package/dist/log/root/log-root.js +5 -2
  45. package/dist/map/root/map-root.d.ts +11 -0
  46. package/dist/map/root/map-root.js +28 -12
  47. package/dist/menu/root/menu-root.d.ts +8 -0
  48. package/dist/menu/root/menu-root.js +22 -1
  49. package/dist/menu/section/menu-section.d.ts +10 -0
  50. package/dist/menu/section/menu-section.js +10 -0
  51. package/dist/meter/index.d.ts +1 -0
  52. package/dist/meter/index.js +1 -0
  53. package/dist/meter/root/meter-root.d.ts +16 -1
  54. package/dist/meter/root/meter-root.js +27 -11
  55. package/dist/meter/segment/meter-segment.d.ts +74 -0
  56. package/dist/meter/segment/meter-segment.js +47 -0
  57. package/dist/scroll-area/root/scroll-area-root.d.ts +20 -3
  58. package/dist/scroll-area/root/scroll-area-root.js +23 -4
  59. package/dist/spatial-bar-chart/bars.d.ts +125 -5
  60. package/dist/spatial-bar-chart/bars.js +194 -17
  61. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.d.ts +148 -6
  62. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.js +285 -16
  63. package/dist/splitter/root/splitter-root.js +1 -0
  64. package/dist/tabs/root/tabs-root.d.ts +4 -0
  65. package/dist/tabs/tab/tab.d.ts +1 -0
  66. package/dist/tabs/tab/tab.js +2 -2
  67. package/dist/tag/root/tag-root.d.ts +1 -1
  68. package/dist/terminal/root/terminal-root.d.ts +60 -32
  69. package/dist/terminal/root/terminal-root.js +82 -178
  70. package/dist/toolbar/root/toolbar-root.d.ts +4 -1
  71. package/dist/tooltip/root/tooltip-root.d.ts +23 -1
  72. package/dist/tooltip/root/tooltip-root.js +28 -5
  73. package/dist/tour/root/tour-root.d.ts +1 -0
  74. package/dist/tour/root/tour-root.js +4 -1
  75. package/dist/tour/stop/tour-stop.js +1 -0
  76. package/dist/tree/item/tree-item.d.ts +11 -0
  77. package/dist/tree/item/tree-item.js +19 -1
  78. package/examples.json +1 -1
  79. package/lint.json +1 -1
  80. package/package.json +5 -5
  81. package/types/jsx.d.ts +2222 -118
  82. package/types/svelte.d.ts +2 -0
  83. package/types/vue.d.ts +468 -19
  84. package/types/words.d.ts +96 -1
@@ -1,11 +1,11 @@
1
1
  import { __decorate } from "tslib";
2
- import { AriaForwardController, customElement, drawIcon, formatWords, HelixFormElement, ListedController, MIRROR_RULE, MixinControllerValue, MixinField, numberConverter, onApple, restoreValue, stopPress, words, } from '@fusion.dev/helix-core';
3
- import { asterisk, caretDown, caretRight, caretUp, info, magnifyingGlass, textAa, textAUnderline, warning, x, xCircleFill, } from '@fusion.dev/helix-icons';
2
+ import { AriaForwardController, customElement, drawIcon, findPattern, findRanges, formatWords, HelixFormElement, ListedController, MixinControllerValue, MixinField, numberConverter, onApple, restoreValue, stopPress, words, } from '@fusion.dev/helix-core';
3
+ import { info, magnifyingGlass, warning, xCircleFill } from '@fusion.dev/helix-icons';
4
4
  import { nothing } from 'lit';
5
5
  import { property, state } from 'lit/decorators.js';
6
- import { live } from 'lit/directives/live.js';
7
6
  import { html } from 'lit/static-html.js';
8
7
  import '../../button/index.js';
8
+ import '../../find/index.js';
9
9
  import '../../icon-button/index.js';
10
10
  import '../../toolbar/index.js';
11
11
  export const diagnosticLines = (value, diagnostics) => {
@@ -53,24 +53,7 @@ export const positionOf = (value, at) => {
53
53
  line: before.split('\n').length,
54
54
  };
55
55
  };
56
- const patternOf = (query) => {
57
- const source = query.regex ? query.search : query.search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
58
- try {
59
- return new RegExp(query.wholeWord ? `\\b(?:${source})\\b` : source, query.caseSensitive ? 'gu' : 'giu');
60
- }
61
- catch {
62
- return undefined;
63
- }
64
- };
65
- export const findRanges = (text, query) => {
66
- const pattern = query.search === '' ? undefined : patternOf(query);
67
- if (!pattern) {
68
- return [];
69
- }
70
- return [...text.matchAll(pattern)]
71
- .filter((match) => match[0] !== '')
72
- .map((match) => [match.index, match.index + match[0].length]);
73
- };
56
+ export { findRanges };
74
57
  const LANGUAGE_NAMES = {
75
58
  bash: 'Shell',
76
59
  css: 'CSS',
@@ -129,15 +112,6 @@ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControl
129
112
  this.wrap = false;
130
113
  this.caret = 0;
131
114
  this.findOpen = false;
132
- this.found = undefined;
133
- this.replaceOpen = false;
134
- this.query = {
135
- caseSensitive: false,
136
- regex: false,
137
- replace: '',
138
- search: '',
139
- wholeWord: false,
140
- };
141
115
  this.escaped = false;
142
116
  this.engineEdited = false;
143
117
  this.handleViewFocusOut = (event) => {
@@ -147,6 +121,14 @@ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControl
147
121
  this.announceChange();
148
122
  }
149
123
  };
124
+ this.finder = {
125
+ end: () => this.view?.findEnd?.(),
126
+ find: (query, direction) => this.findIn(query, direction),
127
+ replace: (query, all) => this.replaceIn(query, all),
128
+ };
129
+ this.handleFindToggle = (event) => {
130
+ this.findOpen = event.newState === 'open';
131
+ };
150
132
  this.handleKeys = (event) => {
151
133
  const mod = onApple() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
152
134
  if (mod && !event.altKey && !event.shiftKey && event.key.toLowerCase() === 'f') {
@@ -155,28 +137,6 @@ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControl
155
137
  void this.openFind();
156
138
  }
157
139
  };
158
- this.handleFindInput = (event) => {
159
- this.query = { ...this.query, search: event.target.value };
160
- this.search('first');
161
- };
162
- this.handleReplaceInput = (event) => {
163
- this.query = { ...this.query, replace: event.target.value };
164
- };
165
- this.handleFindKeys = (event) => {
166
- if (event.key === 'Escape') {
167
- event.preventDefault();
168
- this.closeFind();
169
- }
170
- else if (event.key === 'Enter') {
171
- event.preventDefault();
172
- if (event.target.getAttribute('part') === 'replace-field') {
173
- this.replace(event.metaKey || event.ctrlKey);
174
- }
175
- else {
176
- this.search(event.shiftKey ? 'previous' : 'next');
177
- }
178
- }
179
- };
180
140
  this.handleInput = (event) => {
181
141
  event.stopPropagation();
182
142
  const control = event.target;
@@ -203,7 +163,6 @@ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControl
203
163
  this.escaped = false;
204
164
  };
205
165
  }
206
- static { this.markRules = [MIRROR_RULE]; }
207
166
  static { this.words = 'helix-code-editor'; }
208
167
  get control() {
209
168
  return this.partNamed('control');
@@ -220,9 +179,6 @@ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControl
220
179
  wrap: this.wrap,
221
180
  };
222
181
  }
223
- get readable() {
224
- return this.query.search === '' || patternOf(this.query) !== undefined;
225
- }
226
182
  focus(options) {
227
183
  if (this.view?.focus) {
228
184
  this.view.focus();
@@ -273,7 +229,7 @@ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControl
273
229
  this.refreshFeedback();
274
230
  }
275
231
  this.toggleState('find-open', this.findOpen);
276
- this.toggleState('replace-open', this.findOpen && this.replaceOpen && !this.readOnly);
232
+ this.attachFind();
277
233
  const mount = this.partNamed('view');
278
234
  if (changed.has('engine') || (this.engine && !this.view && mount)) {
279
235
  this.view?.destroy();
@@ -308,43 +264,35 @@ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControl
308
264
  return true;
309
265
  }
310
266
  async openFind() {
267
+ const find = this.find?.element;
268
+ if (!find) {
269
+ return;
270
+ }
311
271
  const control = this.control;
312
272
  if (!this.view && control && control.selectionEnd > control.selectionStart) {
313
273
  const selected = control.value.slice(control.selectionStart, control.selectionEnd);
314
274
  if (!selected.includes('\n')) {
315
- this.query = { ...this.query, search: selected };
275
+ find.query = selected;
316
276
  }
317
277
  }
318
- this.findOpen = true;
319
- await this.updateComplete;
320
- const field = this.partNamed('find-field');
321
- field?.focus();
322
- field?.select();
323
- if (this.query.search !== '') {
324
- this.search('first');
325
- }
326
- }
327
- closeFind() {
328
- this.findOpen = false;
329
- this.found = undefined;
330
- this.view?.findEnd?.();
331
- this.focus();
278
+ await find.show();
332
279
  }
333
- search(direction) {
334
- if (this.query.search === '' || !this.readable) {
335
- this.found = undefined;
336
- this.view?.findEnd?.();
280
+ attachFind() {
281
+ const element = this.partNamed('find') ?? undefined;
282
+ if (element === this.find?.element) {
337
283
  return;
338
284
  }
285
+ this.find?.sink.detach();
286
+ this.find = element ? { element, sink: element.attach(this.finder) } : undefined;
287
+ }
288
+ findIn(query, direction) {
339
289
  if (this.view?.find) {
340
- this.found = this.view.find(this.query, direction);
341
- return;
290
+ return this.view.find(query, direction);
342
291
  }
343
292
  const control = this.control;
344
- const ranges = findRanges(this.value, this.query);
293
+ const ranges = findRanges(this.value, query);
345
294
  if (!control || ranges.length === 0) {
346
- this.found = { count: ranges.length, index: -1 };
347
- return;
295
+ return { count: ranges.length, index: -1 };
348
296
  }
349
297
  const at = direction === 'first' ? control.selectionStart : control.selectionEnd;
350
298
  let index = direction === 'previous'
@@ -356,54 +304,37 @@ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControl
356
304
  const [from, to] = ranges[index] ?? [0, 0];
357
305
  control.setSelectionRange(from, to);
358
306
  this.caret = from;
359
- this.found = { count: ranges.length, index };
307
+ return { count: ranges.length, index };
360
308
  }
361
- replace(all) {
362
- if (this.readOnly || this.query.search === '' || !this.readable) {
363
- return;
309
+ replaceIn(query, all) {
310
+ if (this.readOnly) {
311
+ return undefined;
364
312
  }
365
313
  if (this.view?.replace) {
366
- this.found = this.view.replace(this.query, all);
367
- return;
314
+ return this.view.replace(query, all);
368
315
  }
369
316
  const control = this.control;
370
- const pattern = patternOf(this.query);
317
+ const pattern = findPattern(query);
371
318
  if (!control || !pattern) {
372
- return;
319
+ return undefined;
373
320
  }
374
321
  if (all) {
375
- this.requestValue(this.value.replace(pattern, this.query.replace));
322
+ this.requestValue(this.value.replace(pattern, query.replace));
376
323
  }
377
324
  else {
378
- const selected = findRanges(this.value, this.query).find(([from, to]) => from === control.selectionStart && to === control.selectionEnd);
325
+ const selected = findRanges(this.value, query).find(([from, to]) => from === control.selectionStart && to === control.selectionEnd);
379
326
  if (!selected) {
380
- this.search('next');
381
- return;
327
+ return this.findIn(query, 'next');
382
328
  }
383
329
  const [from, to] = selected;
384
330
  const one = new RegExp(pattern.source, pattern.flags.replace('g', ''));
385
- const replaced = this.value.slice(from, to).replace(one, this.query.replace);
331
+ const replaced = this.value.slice(from, to).replace(one, query.replace);
386
332
  this.requestValue(this.value.slice(0, from) + replaced + this.value.slice(to));
387
333
  restoreValue(control, this.value);
388
334
  control.setSelectionRange(from + replaced.length, from + replaced.length);
389
335
  }
390
336
  restoreValue(control, this.value);
391
- this.search('next');
392
- }
393
- toggleOption(option) {
394
- this.query = { ...this.query, [option]: !this.query[option] };
395
- this.search('first');
396
- }
397
- get countText() {
398
- if (!this.readable) {
399
- return this.findInvalidLabel;
400
- }
401
- return this.found === undefined
402
- ? ''
403
- : formatWords(this, 'matchesLabel', {
404
- count: this.found.count,
405
- index: Math.max(this.found.index, 0) + 1,
406
- });
337
+ return this.findIn(query, 'next');
407
338
  }
408
339
  renderHeader() {
409
340
  const toolbar = this.tag('toolbar');
@@ -412,14 +343,14 @@ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControl
412
343
  return html `<div part="header">
413
344
  ${this.renderLabel()}
414
345
  ${language ? html `<span part="language">${language}</span>` : nothing}
415
- <${toolbar} part="controls" exportparts="toolbar: controls-toolbar" exportparts="toolbar: controls-toolbar" label=${this.controlsLabel}>
346
+ <${toolbar} part="controls" exportparts="toolbar: controls-toolbar" label=${this.controlsLabel}>
416
347
  <slot name="actions"></slot>
417
348
  <${button}
418
- part="find"
419
- exportparts="button: find-button, live: find-live, spinner: find-spinner, text: find-text"
349
+ part="find-toggle"
350
+ exportparts="button: find-toggle-button, live: find-toggle-live, spinner: find-toggle-spinner, text: find-toggle-text"
420
351
  .expanded=${this.findOpen}
421
352
  label=${this.findLabel}
422
- @click=${() => (this.findOpen ? this.closeFind() : void this.openFind())}
353
+ @click=${() => (this.findOpen ? this.find?.element.hide() : void this.openFind())}
423
354
  @helix-press=${stopPress}
424
355
  >
425
356
  ${drawIcon(magnifyingGlass)}
@@ -428,132 +359,28 @@ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControl
428
359
  </div>`;
429
360
  }
430
361
  renderFind() {
431
- const toolbar = this.tag('toolbar');
432
- const button = this.tag('icon-button');
433
- const textButton = this.tag('button');
434
- const searchable = this.readable && (this.found?.count ?? 0) > 0;
435
- const replacing = this.replaceOpen && !this.readOnly;
436
- return html `<div part="find-bar" role="search" aria-label=${this.findLabel} ?hidden=${!this.findOpen}>
437
- ${this.readOnly
438
- ? nothing
439
- : html `<${button}
440
- part="find-replace"
441
- exportparts="button: find-replace-button, live: find-replace-live, spinner: find-replace-spinner, text: find-replace-text"
442
- .expanded=${replacing}
443
- label=${this.findReplaceLabel}
444
- @click=${() => {
445
- this.replaceOpen = !this.replaceOpen;
446
- }}
447
- @helix-press=${stopPress}
448
- >
449
- ${replacing ? drawIcon(caretDown) : html `<span data-mirror>${drawIcon(caretRight)}</span>`}
450
- </${button}>`}
451
- <input
452
- part="find-field"
453
- aria-label=${this.findLabel}
454
- aria-invalid=${this.readable ? nothing : 'true'}
455
- autocomplete="off"
456
- placeholder=${this.findLabel}
457
- spellcheck="false"
458
- type="search"
459
- .value=${live(this.query.search)}
460
- @input=${this.handleFindInput}
461
- @keydown=${this.handleFindKeys}
462
- />
463
- <span part="find-count" role="status">${this.countText}</span>
464
- <${toolbar} part="find-controls" exportparts="toolbar: find-controls-toolbar" exportparts="toolbar: find-controls-toolbar" label=${this.findControlsLabel}>
465
- <${button}
466
- part="find-match-case"
467
- exportparts="button: find-match-case-button, live: find-match-case-live, spinner: find-match-case-spinner, text: find-match-case-text"
468
- aria-pressed=${this.query.caseSensitive ? 'true' : 'false'}
469
- label=${this.findMatchCaseLabel}
470
- @click=${() => this.toggleOption('caseSensitive')}
471
- @helix-press=${stopPress}
472
- >
473
- ${drawIcon(textAa)}
474
- </${button}>
475
- <${button}
476
- part="find-whole-word"
477
- exportparts="button: find-whole-word-button, live: find-whole-word-live, spinner: find-whole-word-spinner, text: find-whole-word-text"
478
- aria-pressed=${this.query.wholeWord ? 'true' : 'false'}
479
- label=${this.findWholeWordLabel}
480
- @click=${() => this.toggleOption('wholeWord')}
481
- @helix-press=${stopPress}
482
- >
483
- ${drawIcon(textAUnderline)}
484
- </${button}>
485
- <${button}
486
- part="find-regex"
487
- exportparts="button: find-regex-button, live: find-regex-live, spinner: find-regex-spinner, text: find-regex-text"
488
- aria-pressed=${this.query.regex ? 'true' : 'false'}
489
- label=${this.findRegexLabel}
490
- @click=${() => this.toggleOption('regex')}
491
- @helix-press=${stopPress}
492
- >
493
- ${drawIcon(asterisk)}
494
- </${button}>
495
- <${button}
496
- part="find-previous"
497
- exportparts="button: find-previous-button, live: find-previous-live, spinner: find-previous-spinner, text: find-previous-text"
498
- ?disabled=${!searchable}
499
- label=${this.findPreviousLabel}
500
- @click=${() => this.search('previous')}
501
- @helix-press=${stopPress}
502
- >
503
- ${drawIcon(caretUp)}
504
- </${button}>
505
- <${button}
506
- part="find-next"
507
- exportparts="button: find-next-button, live: find-next-live, spinner: find-next-spinner, text: find-next-text"
508
- ?disabled=${!searchable}
509
- label=${this.findNextLabel}
510
- @click=${() => this.search('next')}
511
- @helix-press=${stopPress}
512
- >
513
- ${drawIcon(caretDown)}
514
- </${button}>
515
- <${button}
516
- part="find-close"
517
- exportparts="button: find-close-button, live: find-close-live, spinner: find-close-spinner, text: find-close-text"
518
- label=${this.findCloseLabel}
519
- @click=${() => this.closeFind()}
520
- @helix-press=${stopPress}
521
- >
522
- ${drawIcon(x)}
523
- </${button}>
524
- </${toolbar}>
525
- ${!replacing
526
- ? nothing
527
- : html `<input
528
- part="replace-field"
529
- aria-label=${this.replaceWithLabel}
530
- autocomplete="off"
531
- placeholder=${this.replaceWithLabel}
532
- spellcheck="false"
533
- type="text"
534
- .value=${live(this.query.replace)}
535
- @input=${this.handleReplaceInput}
536
- @keydown=${this.handleFindKeys}
537
- />
538
- <${textButton}
539
- part="replace"
540
- exportparts="button: replace-button, live: replace-live, spinner: replace-spinner, text: replace-text"
541
- ?disabled=${!searchable}
542
- @click=${() => this.replace(false)}
543
- @helix-press=${stopPress}
544
- >
545
- ${this.replaceLabel}
546
- </${textButton}>
547
- <${textButton}
548
- part="replace-all"
549
- exportparts="button: replace-all-button, live: replace-all-live, spinner: replace-all-spinner, text: replace-all-text"
550
- ?disabled=${!searchable}
551
- @click=${() => this.replace(true)}
552
- @helix-press=${stopPress}
553
- >
554
- ${this.replaceAllLabel}
555
- </${textButton}>`}
556
- </div>`;
362
+ const find = this.tag('find');
363
+ return html `<${find}
364
+ part="find"
365
+ exportparts="actions: find-actions, bar: find-bar, close: find-close, close-button: find-close-button, close-live: find-close-live, close-spinner: find-close-spinner, close-text: find-close-text, controls: find-controls, controls-toolbar: find-controls-toolbar, count: find-count, field: find-field, live: find-live, match-case: find-match-case, match-case-button: find-match-case-button, match-case-live: find-match-case-live, match-case-pressed: find-match-case-pressed, match-case-spinner: find-match-case-spinner, match-case-text: find-match-case-text, next: find-next, next-button: find-next-button, next-live: find-next-live, next-spinner: find-next-spinner, next-text: find-next-text, previous: find-previous, previous-button: find-previous-button, previous-live: find-previous-live, previous-spinner: find-previous-spinner, previous-text: find-previous-text, regex: find-regex, regex-button: find-regex-button, regex-live: find-regex-live, regex-pressed: find-regex-pressed, regex-spinner: find-regex-spinner, regex-text: find-regex-text, replace: find-replace, replace-all: find-replace-all, replace-all-button: find-replace-all-button, replace-all-live: find-replace-all-live, replace-all-spinner: find-replace-all-spinner, replace-all-text: find-replace-all-text, replace-button: find-replace-button, replace-field: find-replace-field, replace-live: find-replace-live, replace-spinner: find-replace-spinner, replace-text: find-replace-text, replace-toggle: find-replace-toggle, replace-toggle-button: find-replace-toggle-button, replace-toggle-live: find-replace-toggle-live, replace-toggle-spinner: find-replace-toggle-spinner, replace-toggle-text: find-replace-toggle-text, whole-word: find-whole-word, whole-word-button: find-whole-word-button, whole-word-live: find-whole-word-live, whole-word-pressed: find-whole-word-pressed, whole-word-spinner: find-whole-word-spinner, whole-word-text: find-whole-word-text"
366
+ close-label=${this.findCloseLabel}
367
+ controls-label=${this.findControlsLabel}
368
+ invalid-label=${this.findInvalidLabel}
369
+ label=${this.findLabel}
370
+ match-case-label=${this.findMatchCaseLabel}
371
+ matches-label=${this.matchesLabel}
372
+ next-label=${this.findNextLabel}
373
+ previous-label=${this.findPreviousLabel}
374
+ ?readonly=${this.readOnly}
375
+ regex-label=${this.findRegexLabel}
376
+ replace-all-label=${this.replaceAllLabel}
377
+ replace-field-label=${this.replaceWithLabel}
378
+ replace-label=${this.replaceLabel}
379
+ replace-toggle-label=${this.findReplaceLabel}
380
+ shortcut=""
381
+ whole-word-label=${this.findWholeWordLabel}
382
+ @toggle=${this.handleFindToggle}
383
+ ></${find}>`;
557
384
  }
558
385
  renderDiagnostics() {
559
386
  if (this.diagnostics.length === 0) {
@@ -731,15 +558,6 @@ __decorate([
731
558
  __decorate([
732
559
  state()
733
560
  ], HelixHeadlessCodeEditor.prototype, "findOpen", void 0);
734
- __decorate([
735
- state()
736
- ], HelixHeadlessCodeEditor.prototype, "found", void 0);
737
- __decorate([
738
- state()
739
- ], HelixHeadlessCodeEditor.prototype, "replaceOpen", void 0);
740
- __decorate([
741
- state()
742
- ], HelixHeadlessCodeEditor.prototype, "query", void 0);
743
561
  HelixHeadlessCodeEditor = __decorate([
744
562
  customElement('helix-headless-code-editor')
745
563
  ], HelixHeadlessCodeEditor);
@@ -24,7 +24,7 @@ let HelixHeadlessCopyButton = class HelixHeadlessCopyButton extends HelixHeadles
24
24
  this.showedResult = false;
25
25
  this.clipboard = new CopyController(this, { after: () => this.feedbackDuration });
26
26
  this.handleCopy = async (event) => {
27
- if (this.pending) {
27
+ if (this.refusesPress) {
28
28
  event.preventDefault();
29
29
  event.stopImmediatePropagation();
30
30
  return;
@@ -100,12 +100,12 @@ let HelixHeadlessCopyButton = class HelixHeadlessCopyButton extends HelixHeadles
100
100
  part="button"
101
101
  type="button"
102
102
  aria-busy=${this.pending ? 'true' : nothing}
103
- aria-disabled=${this.pending ? 'true' : nothing}
103
+ aria-disabled=${this.refusesPress ? 'true' : nothing}
104
104
  aria-label=${this.says}
105
105
  data-copied=${this.copied ? '' : nothing}
106
106
  data-failed=${this.failed ? '' : nothing}
107
107
  tabindex=${this.focusable ? nothing : '-1'}
108
- ?disabled=${this.isDisabled}
108
+ ?disabled=${this.isDisabled && !this.heldDisabled}
109
109
  @click=${this.handleCopy}
110
110
  @keydown=${this.notePress}
111
111
  @pointerdown=${this.notePress}
@@ -1,6 +1,7 @@
1
1
  import { ChildrenController, type DeviceButton, HelixElement, ToolbarController } from '@fusion.dev/helix-core';
2
2
  import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../icon-button/index.js';
4
+ import '../../tooltip/index.js';
4
5
  /**
5
6
  * @summary A device's screen, drawn from a stream you supply, that sends taps, swipes, keys, and button presses back to the device.
6
7
  *
@@ -143,6 +144,16 @@ import '../../icon-button/index.js';
143
144
  * @csspart screenshot-live - The screenshot button's live region, which speaks while the button is pending.
144
145
  * @csspart screenshot-spinner - The ring the screenshot button turns while it is pending.
145
146
  * @csspart screenshot-text - The box the screenshot button's icon sits in.
147
+ * @csspart tooltip-arrow - The arrow of the tooltip that names each button, forwarded from `Tooltip`.
148
+ * @csspart tooltip-arrow-bottom - The arrow while the bubble is below its button, on the bubble's top edge. Set together with `tooltip-arrow`.
149
+ * @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`.
150
+ * @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`.
151
+ * @csspart tooltip-arrow-top - The arrow while the bubble is above its button, on the bubble's bottom edge. Set together with `tooltip-arrow`.
152
+ * @csspart tooltip-surface - The bubble of the tooltip that names each button, forwarded from `Tooltip`.
153
+ * @csspart tooltip-surface-bottom - The bubble while it is below its button. Set together with `tooltip-surface`.
154
+ * @csspart tooltip-surface-left - The bubble while it is left of its button. Set together with `tooltip-surface`.
155
+ * @csspart tooltip-surface-right - The bubble while it is right of its button. Set together with `tooltip-surface`.
156
+ * @csspart tooltip-surface-top - The bubble while it is above its button. Set together with `tooltip-surface`.
146
157
  * @csspart touch - A ring where a touch is or was.
147
158
  * @csspart touch-held - A ring under a finger that is down, beside `touch`, with `show-touches`.
148
159
  * @csspart volume-down - The Volume down button.
@@ -6,6 +6,7 @@ import { property, state } from 'lit/decorators.js';
6
6
  import { repeat } from 'lit/directives/repeat.js';
7
7
  import { html } from 'lit/static-html.js';
8
8
  import '../../icon-button/index.js';
9
+ import '../../tooltip/index.js';
9
10
  import { gestureOf } from '../gesture.js';
10
11
  import { quarterTurns, turn, unturn, unturnDistance } from '../turn.js';
11
12
  const MARK_SHOWS = 700;
@@ -546,6 +547,7 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
546
547
  return nothing;
547
548
  }
548
549
  const button = this.tag('icon-button');
550
+ const tip = this.tag('tooltip');
549
551
  const off = this.readonly;
550
552
  const raised = this.controlsRaised;
551
553
  return html `<div
@@ -557,6 +559,7 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
557
559
  ${this.draws('back')
558
560
  ? html `<${button}
559
561
  part="back"
562
+ id="back"
560
563
  exportparts="button: back-button, live: back-live, spinner: back-spinner, text: back-text"
561
564
  ?disabled=${off}
562
565
  label=${this.backLabel}
@@ -566,11 +569,12 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
566
569
  @pointerdown=${this.handleHoldStart}
567
570
  >
568
571
  <slot name="back-icon">${drawIcon(caretLeft)}</slot>
569
- </${button}>`
572
+ </${button}><${tip} for="back" content=${this.backLabel} exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom, arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right, arrow-top: tooltip-arrow-top, surface: tooltip-surface, surface-bottom: tooltip-surface-bottom, surface-left: tooltip-surface-left, surface-right: tooltip-surface-right, surface-top: tooltip-surface-top"></${tip}>`
570
573
  : nothing}
571
574
  ${this.draws('home')
572
575
  ? html `<${button}
573
576
  part="home"
577
+ id="home"
574
578
  exportparts="button: home-button, live: home-live, spinner: home-spinner, text: home-text"
575
579
  ?disabled=${off}
576
580
  label=${this.homeLabel}
@@ -580,11 +584,12 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
580
584
  @pointerdown=${this.handleHoldStart}
581
585
  >
582
586
  <slot name="home-icon">${drawIcon(house)}</slot>
583
- </${button}>`
587
+ </${button}><${tip} for="home" content=${this.homeLabel} exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom, arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right, arrow-top: tooltip-arrow-top, surface: tooltip-surface, surface-bottom: tooltip-surface-bottom, surface-left: tooltip-surface-left, surface-right: tooltip-surface-right, surface-top: tooltip-surface-top"></${tip}>`
584
588
  : nothing}
585
589
  ${this.draws('recents')
586
590
  ? html `<${button}
587
591
  part="recents"
592
+ id="recents"
588
593
  exportparts="button: recents-button, live: recents-live, spinner: recents-spinner, text: recents-text"
589
594
  ?disabled=${off}
590
595
  label=${this.recentsLabel}
@@ -594,11 +599,12 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
594
599
  @pointerdown=${this.handleHoldStart}
595
600
  >
596
601
  <slot name="recents-icon">${drawIcon(square)}</slot>
597
- </${button}>`
602
+ </${button}><${tip} for="recents" content=${this.recentsLabel} exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom, arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right, arrow-top: tooltip-arrow-top, surface: tooltip-surface, surface-bottom: tooltip-surface-bottom, surface-left: tooltip-surface-left, surface-right: tooltip-surface-right, surface-top: tooltip-surface-top"></${tip}>`
598
603
  : nothing}
599
604
  ${this.draws('power')
600
605
  ? html `<${button}
601
606
  part="power"
607
+ id="power"
602
608
  exportparts="button: power-button, live: power-live, spinner: power-spinner, text: power-text"
603
609
  ?disabled=${off}
604
610
  label=${this.powerLabel}
@@ -608,11 +614,12 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
608
614
  @pointerdown=${this.handleHoldStart}
609
615
  >
610
616
  <slot name="power-icon">${drawIcon(power)}</slot>
611
- </${button}>`
617
+ </${button}><${tip} for="power" content=${this.powerLabel} exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom, arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right, arrow-top: tooltip-arrow-top, surface: tooltip-surface, surface-bottom: tooltip-surface-bottom, surface-left: tooltip-surface-left, surface-right: tooltip-surface-right, surface-top: tooltip-surface-top"></${tip}>`
612
618
  : nothing}
613
619
  ${this.draws('volume')
614
620
  ? html `<${button}
615
621
  part="volume-up"
622
+ id="volume-up"
616
623
  exportparts="button: volume-up-button, live: volume-up-live, spinner: volume-up-spinner, text: volume-up-text"
617
624
  ?disabled=${off}
618
625
  label=${this.volumeUpLabel}
@@ -622,9 +629,10 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
622
629
  @pointerdown=${this.handleHoldStart}
623
630
  >
624
631
  <slot name="volume-up-icon">${drawIcon(speakerHigh)}</slot>
625
- </${button}>
632
+ </${button}><${tip} for="volume-up" content=${this.volumeUpLabel} exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom, arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right, arrow-top: tooltip-arrow-top, surface: tooltip-surface, surface-bottom: tooltip-surface-bottom, surface-left: tooltip-surface-left, surface-right: tooltip-surface-right, surface-top: tooltip-surface-top"></${tip}>
626
633
  <${button}
627
634
  part="volume-down"
635
+ id="volume-down"
628
636
  exportparts="button: volume-down-button, live: volume-down-live, spinner: volume-down-spinner, text: volume-down-text"
629
637
  ?disabled=${off}
630
638
  label=${this.volumeDownLabel}
@@ -634,11 +642,12 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
634
642
  @pointerdown=${this.handleHoldStart}
635
643
  >
636
644
  <slot name="volume-down-icon">${drawIcon(speakerLow)}</slot>
637
- </${button}>`
645
+ </${button}><${tip} for="volume-down" content=${this.volumeDownLabel} exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom, arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right, arrow-top: tooltip-arrow-top, surface: tooltip-surface, surface-bottom: tooltip-surface-bottom, surface-left: tooltip-surface-left, surface-right: tooltip-surface-right, surface-top: tooltip-surface-top"></${tip}>`
638
646
  : nothing}
639
647
  ${this.draws('rotate')
640
648
  ? html `<${button}
641
649
  part="rotate-left"
650
+ id="rotate-left"
642
651
  exportparts="button: rotate-left-button, live: rotate-left-live, spinner: rotate-left-spinner, text: rotate-left-text"
643
652
  ?disabled=${off}
644
653
  label=${this.rotateLeftLabel}
@@ -648,9 +657,10 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
648
657
  @pointerdown=${this.handleHoldStart}
649
658
  >
650
659
  <slot name="rotate-left-icon">${drawIcon(arrowCounterClockwise)}</slot>
651
- </${button}>
660
+ </${button}><${tip} for="rotate-left" content=${this.rotateLeftLabel} exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom, arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right, arrow-top: tooltip-arrow-top, surface: tooltip-surface, surface-bottom: tooltip-surface-bottom, surface-left: tooltip-surface-left, surface-right: tooltip-surface-right, surface-top: tooltip-surface-top"></${tip}>
652
661
  <${button}
653
662
  part="rotate-right"
663
+ id="rotate-right"
654
664
  exportparts="button: rotate-right-button, live: rotate-right-live, spinner: rotate-right-spinner, text: rotate-right-text"
655
665
  ?disabled=${off}
656
666
  label=${this.rotateRightLabel}
@@ -660,11 +670,12 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
660
670
  @pointerdown=${this.handleHoldStart}
661
671
  >
662
672
  <slot name="rotate-right-icon">${drawIcon(arrowClockwise)}</slot>
663
- </${button}>`
673
+ </${button}><${tip} for="rotate-right" content=${this.rotateRightLabel} exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom, arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right, arrow-top: tooltip-arrow-top, surface: tooltip-surface, surface-bottom: tooltip-surface-bottom, surface-left: tooltip-surface-left, surface-right: tooltip-surface-right, surface-top: tooltip-surface-top"></${tip}>`
664
674
  : nothing}
665
675
  ${this.draws('screenshot')
666
676
  ? html `<${button}
667
677
  part="screenshot"
678
+ id="screenshot"
668
679
  exportparts="button: screenshot-button, live: screenshot-live, spinner: screenshot-spinner, text: screenshot-text"
669
680
  label=${this.screenshotLabel}
670
681
  ?raised=${raised}
@@ -672,12 +683,13 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
672
683
  @helix-press=${stopPress}
673
684
  >
674
685
  <slot name="screenshot-icon">${drawIcon(camera)}</slot>
675
- </${button}>`
686
+ </${button}><${tip} for="screenshot" content=${this.screenshotLabel} exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom, arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right, arrow-top: tooltip-arrow-top, surface: tooltip-surface, surface-bottom: tooltip-surface-bottom, surface-left: tooltip-surface-left, surface-right: tooltip-surface-right, surface-top: tooltip-surface-top"></${tip}>`
676
687
  : nothing}
677
688
  <slot name="controls" @slotchange=${this.handleSlotChange}></slot>
678
689
  ${this.draws('fullscreen')
679
690
  ? html `<${button}
680
691
  part="fullscreen"
692
+ id="fullscreen"
681
693
  exportparts="button: fullscreen-button, live: fullscreen-live, spinner: fullscreen-spinner, text: fullscreen-text"
682
694
  aria-pressed=${this.filled ? 'true' : 'false'}
683
695
  label=${this.fullscreenLabel}
@@ -686,7 +698,7 @@ let HelixHeadlessDeviceScreen = class HelixHeadlessDeviceScreen extends HelixEle
686
698
  @helix-press=${stopPress}
687
699
  >
688
700
  <slot name="fullscreen-icon">${drawIcon(cornersOut)}</slot>
689
- </${button}>`
701
+ </${button}><${tip} for="fullscreen" content=${this.fullscreenLabel} exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom, arrow-left: tooltip-arrow-left, arrow-right: tooltip-arrow-right, arrow-top: tooltip-arrow-top, surface: tooltip-surface, surface-bottom: tooltip-surface-bottom, surface-left: tooltip-surface-left, surface-right: tooltip-surface-right, surface-top: tooltip-surface-top"></${tip}>`
690
702
  : nothing}
691
703
  </div>`;
692
704
  }