@fusion.dev/helix-headless 0.215.2 → 0.216.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 (113) hide show
  1. package/api.json +1 -1
  2. package/custom-elements.json +1 -1
  3. package/dist/area-chart/root/area-chart-root.d.ts +1 -0
  4. package/dist/area-chart/root/area-chart-root.js +1 -1
  5. package/dist/bar-chart/root/bar-chart-root.d.ts +1 -0
  6. package/dist/bar-chart/root/bar-chart-root.js +1 -1
  7. package/dist/button/root/button-root.d.ts +3 -1
  8. package/dist/button/root/button-root.js +2 -1
  9. package/dist/chat/composer/chat-composer.d.ts +18 -1
  10. package/dist/chat/composer/chat-composer.js +8 -3
  11. package/dist/checkbox/root/checkbox-root.d.ts +3 -1
  12. package/dist/checkbox/root/checkbox-root.js +2 -1
  13. package/dist/code-block/highlight.d.ts +9 -0
  14. package/dist/code-block/highlight.js +39 -0
  15. package/dist/code-block/highlighter.d.ts +44 -0
  16. package/dist/code-block/highlighter.js +44 -0
  17. package/dist/code-block/index.d.ts +1 -0
  18. package/dist/code-block/index.js +1 -0
  19. package/dist/code-block/keyed-lines.d.ts +23 -0
  20. package/dist/code-block/keyed-lines.js +75 -0
  21. package/dist/code-block/root/code-block-root.d.ts +6 -23
  22. package/dist/code-block/root/code-block-root.js +8 -25
  23. package/dist/code-editor/index.d.ts +1 -0
  24. package/dist/code-editor/index.js +1 -0
  25. package/dist/code-editor/root/code-editor-root.d.ts +367 -0
  26. package/dist/code-editor/root/code-editor-root.js +746 -0
  27. package/dist/code-input/index.d.ts +1 -0
  28. package/dist/code-input/index.js +1 -0
  29. package/dist/code-input/root/code-input-root.d.ts +119 -0
  30. package/dist/code-input/root/code-input-root.js +142 -0
  31. package/dist/combobox/root/combobox-root.d.ts +6 -1
  32. package/dist/combobox/root/combobox-root.js +4 -2
  33. package/dist/command/root/command-root.d.ts +61 -11
  34. package/dist/command/root/command-root.js +88 -3
  35. package/dist/donut-chart/root/donut-chart-root.d.ts +1 -0
  36. package/dist/donut-chart/root/donut-chart-root.js +1 -1
  37. package/dist/element-events.generated.js +36 -3
  38. package/dist/filter-input/root/filter-input-root.d.ts +9 -1
  39. package/dist/filter-input/root/filter-input-root.js +7 -3
  40. package/dist/heatmap/root/heatmap-root.d.ts +1 -0
  41. package/dist/heatmap/root/heatmap-root.js +1 -1
  42. package/dist/index.d.ts +4 -0
  43. package/dist/index.js +4 -0
  44. package/dist/input/root/input-root.d.ts +14 -2
  45. package/dist/input/root/input-root.js +11 -3
  46. package/dist/kbd/root/kbd-root.js +9 -2
  47. package/dist/line-chart/root/line-chart-root.d.ts +1 -0
  48. package/dist/line-chart/root/line-chart-root.js +1 -1
  49. package/dist/listbox/root/listbox-root.d.ts +8 -1
  50. package/dist/listbox/root/listbox-root.js +7 -4
  51. package/dist/markdown/blocks.d.ts +66 -0
  52. package/dist/markdown/blocks.js +326 -0
  53. package/dist/markdown/index.d.ts +6 -0
  54. package/dist/markdown/index.js +5 -0
  55. package/dist/markdown/inline.d.ts +57 -0
  56. package/dist/markdown/inline.js +483 -0
  57. package/dist/markdown/root/markdown-root.d.ts +99 -0
  58. package/dist/markdown/root/markdown-root.js +93 -0
  59. package/dist/markdown/stream.d.ts +47 -0
  60. package/dist/markdown/stream.js +62 -0
  61. package/dist/markdown/tree.d.ts +57 -0
  62. package/dist/markdown/tree.js +144 -0
  63. package/dist/markdown/writer.d.ts +33 -0
  64. package/dist/markdown/writer.js +139 -0
  65. package/dist/menu/root/menu-root.d.ts +53 -0
  66. package/dist/menu/root/menu-root.js +92 -15
  67. package/dist/pagination/size/pagination-size.d.ts +3 -0
  68. package/dist/pagination/size/pagination-size.js +2 -1
  69. package/dist/scatter-chart/root/scatter-chart-root.d.ts +1 -0
  70. package/dist/scatter-chart/root/scatter-chart-root.js +1 -1
  71. package/dist/select/root/select-root.d.ts +6 -1
  72. package/dist/select/root/select-root.js +4 -2
  73. package/dist/shortcut-overlay/index.d.ts +1 -0
  74. package/dist/shortcut-overlay/index.js +1 -0
  75. package/dist/shortcut-overlay/root/shortcut-overlay-root.d.ts +128 -0
  76. package/dist/shortcut-overlay/root/shortcut-overlay-root.js +275 -0
  77. package/dist/slider/root/slider-root.d.ts +3 -1
  78. package/dist/slider/root/slider-root.js +2 -1
  79. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.d.ts +1 -0
  80. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.js +1 -1
  81. package/dist/spatial-heatmap/root/spatial-heatmap-root.d.ts +1 -0
  82. package/dist/spatial-heatmap/root/spatial-heatmap-root.js +1 -1
  83. package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.d.ts +1 -0
  84. package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.js +1 -1
  85. package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.d.ts +1 -0
  86. package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.js +1 -1
  87. package/dist/spatial-surface-chart/root/spatial-surface-chart-root.d.ts +1 -0
  88. package/dist/spatial-surface-chart/root/spatial-surface-chart-root.js +1 -1
  89. package/dist/spatial-treemap/root/spatial-treemap-root.d.ts +1 -0
  90. package/dist/spatial-treemap/root/spatial-treemap-root.js +1 -1
  91. package/dist/switch/root/switch-root.d.ts +3 -1
  92. package/dist/switch/root/switch-root.js +2 -1
  93. package/dist/tabs/tab/tab.d.ts +3 -1
  94. package/dist/tabs/tab/tab.js +2 -1
  95. package/dist/textarea/root/textarea-root.d.ts +20 -4
  96. package/dist/textarea/root/textarea-root.js +31 -23
  97. package/dist/time-input/root/time-input-root.d.ts +6 -0
  98. package/dist/time-input/root/time-input-root.js +4 -2
  99. package/dist/time-picker/root/time-picker-root.d.ts +6 -0
  100. package/dist/time-picker/root/time-picker-root.js +4 -2
  101. package/dist/toast/root/toast-root.d.ts +64 -0
  102. package/dist/toast/root/toast-root.js +128 -3
  103. package/dist/toggle/root/toggle-root.d.ts +3 -1
  104. package/dist/toggle/root/toggle-root.js +2 -1
  105. package/dist/virtual-list/root/virtual-list-root.d.ts +18 -2
  106. package/dist/virtual-list/root/virtual-list-root.js +103 -5
  107. package/examples.json +1 -1
  108. package/lint.json +1 -1
  109. package/package.json +8 -8
  110. package/types/jsx.d.ts +5315 -596
  111. package/types/svelte.d.ts +4 -0
  112. package/types/vue.d.ts +1085 -4
  113. package/types/words.d.ts +491 -0
@@ -0,0 +1,746 @@
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';
4
+ import { nothing } from 'lit';
5
+ import { property, state } from 'lit/decorators.js';
6
+ import { live } from 'lit/directives/live.js';
7
+ import { html } from 'lit/static-html.js';
8
+ import '../../button/index.js';
9
+ import '../../icon-button/index.js';
10
+ import '../../toolbar/index.js';
11
+ export const diagnosticLines = (value, diagnostics) => {
12
+ const starts = [0];
13
+ for (let at = value.indexOf('\n'); at !== -1; at = value.indexOf('\n', at + 1)) {
14
+ starts.push(at + 1);
15
+ }
16
+ return diagnostics.map((one) => {
17
+ const from = Math.max(0, one.from);
18
+ let low = 0;
19
+ let high = starts.length - 1;
20
+ while (low < high) {
21
+ const middle = (low + high + 1) >> 1;
22
+ if ((starts[middle] ?? 0) <= from) {
23
+ low = middle;
24
+ }
25
+ else {
26
+ high = middle - 1;
27
+ }
28
+ }
29
+ return low + 1;
30
+ });
31
+ };
32
+ const ENGINE_KEYS = [
33
+ 'diagnostics',
34
+ 'label',
35
+ 'language',
36
+ 'lineNumbers',
37
+ 'placeholder',
38
+ 'readOnly',
39
+ 'value',
40
+ 'wrap',
41
+ ];
42
+ const typeText = (control, text) => {
43
+ const typed = typeof document.execCommand === 'function' && document.execCommand('insertText', false, text);
44
+ if (!typed) {
45
+ control.setRangeText(text, control.selectionStart, control.selectionEnd, 'end');
46
+ control.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
47
+ }
48
+ };
49
+ export const positionOf = (value, at) => {
50
+ const before = value.slice(0, Math.max(0, at));
51
+ return {
52
+ column: before.length - before.lastIndexOf('\n'),
53
+ line: before.split('\n').length,
54
+ };
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
+ };
74
+ const LANGUAGE_NAMES = {
75
+ bash: 'Shell',
76
+ css: 'CSS',
77
+ htm: 'HTML',
78
+ html: 'HTML',
79
+ javascript: 'JavaScript',
80
+ js: 'JavaScript',
81
+ json: 'JSON',
82
+ jsx: 'JSX',
83
+ markdown: 'Markdown',
84
+ md: 'Markdown',
85
+ py: 'Python',
86
+ python: 'Python',
87
+ rs: 'Rust',
88
+ rust: 'Rust',
89
+ sh: 'Shell',
90
+ shell: 'Shell',
91
+ ts: 'TypeScript',
92
+ tsx: 'TSX',
93
+ typescript: 'TypeScript',
94
+ };
95
+ let HelixHeadlessCodeEditor = class HelixHeadlessCodeEditor extends MixinControllerValue(MixinField(HelixFormElement)) {
96
+ constructor() {
97
+ super(...arguments);
98
+ this.listed = new ListedController(this, 'focus', () => this.internals?.ariaKeyShortcuts);
99
+ this.aria = new AriaForwardController(this, {
100
+ target: () => this.partNamed('control'),
101
+ });
102
+ this.announcesInput = true;
103
+ this.controlsLabel = 'Editor controls';
104
+ this.diagnosticLabel = 'Line {line}: {message}';
105
+ this.diagnostics = [];
106
+ this.diagnosticsLabel = 'Problems';
107
+ this.engine = undefined;
108
+ this.findCloseLabel = 'Close find';
109
+ this.findControlsLabel = 'Find controls';
110
+ this.findInvalidLabel = 'Not a valid regular expression';
111
+ this.findLabel = 'Find';
112
+ this.findMatchCaseLabel = 'Match case';
113
+ this.findNextLabel = 'Next match';
114
+ this.findPreviousLabel = 'Previous match';
115
+ this.findRegexLabel = 'Use regular expression';
116
+ this.findReplaceLabel = 'Find and replace';
117
+ this.findWholeWordLabel = 'Match whole word';
118
+ this.language = '';
119
+ this.lineNumbers = false;
120
+ this.matchesLabel = '{count, plural, =0 {No matches} one {{index} of # match} other {{index} of # matches}}';
121
+ this.placeholder = '';
122
+ this.positionLabel = 'Line {line}, column {column}';
123
+ this.problemsLabel = '{count, plural, =0 {No problems} one {# problem} other {# problems}}';
124
+ this.readOnly = false;
125
+ this.replaceAllLabel = 'Replace all';
126
+ this.replaceLabel = 'Replace';
127
+ this.replaceWithLabel = 'Replace with';
128
+ this.rows = 8;
129
+ this.wrap = false;
130
+ this.caret = 0;
131
+ 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
+ this.escaped = false;
142
+ this.engineEdited = false;
143
+ this.handleViewFocusOut = (event) => {
144
+ const view = event.currentTarget;
145
+ if (this.engineEdited && !view.contains(event.relatedTarget)) {
146
+ this.engineEdited = false;
147
+ this.announceChange();
148
+ }
149
+ };
150
+ this.handleKeys = (event) => {
151
+ const mod = onApple() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
152
+ if (mod && !event.altKey && !event.shiftKey && event.key.toLowerCase() === 'f') {
153
+ event.preventDefault();
154
+ event.stopPropagation();
155
+ void this.openFind();
156
+ }
157
+ };
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
+ this.handleInput = (event) => {
181
+ event.stopPropagation();
182
+ const control = event.target;
183
+ this.requestValue(control.value);
184
+ restoreValue(control, this.value);
185
+ this.caret = control.selectionStart;
186
+ };
187
+ this.handleChange = (event) => {
188
+ event.stopPropagation();
189
+ this.announceChange();
190
+ };
191
+ this.handleCaret = (event) => {
192
+ this.caret = event.target.selectionStart;
193
+ };
194
+ this.handleKeydown = (event) => {
195
+ if (event.key === 'Escape') {
196
+ this.escaped = true;
197
+ return;
198
+ }
199
+ if (event.key === 'Tab' && !event.shiftKey && !this.escaped && !this.readOnly) {
200
+ event.preventDefault();
201
+ typeText(event.target, ' ');
202
+ }
203
+ this.escaped = false;
204
+ };
205
+ }
206
+ static { this.markRules = [MIRROR_RULE]; }
207
+ static { this.words = 'helix-code-editor'; }
208
+ get control() {
209
+ return this.partNamed('control');
210
+ }
211
+ get engineState() {
212
+ return {
213
+ diagnostics: this.diagnostics,
214
+ label: this.label,
215
+ language: this.language,
216
+ lineNumbers: this.lineNumbers,
217
+ placeholder: this.placeholder,
218
+ readOnly: this.readOnly,
219
+ value: this.value,
220
+ wrap: this.wrap,
221
+ };
222
+ }
223
+ get readable() {
224
+ return this.query.search === '' || patternOf(this.query) !== undefined;
225
+ }
226
+ focus(options) {
227
+ if (this.view?.focus) {
228
+ this.view.focus();
229
+ return;
230
+ }
231
+ this.control?.focus(options);
232
+ }
233
+ select(from, to = from) {
234
+ if (this.view?.select) {
235
+ this.view.select(from, to);
236
+ }
237
+ else {
238
+ const control = this.control;
239
+ control?.focus();
240
+ control?.setSelectionRange(from, to);
241
+ }
242
+ this.caret = from;
243
+ }
244
+ connectedCallback() {
245
+ super.connectedCallback();
246
+ this.addEventListener('keydown', this.handleKeys);
247
+ if (this.hasUpdated && this.engine && !this.view) {
248
+ this.requestUpdate();
249
+ }
250
+ }
251
+ disconnectedCallback() {
252
+ super.disconnectedCallback();
253
+ this.removeEventListener('keydown', this.handleKeys);
254
+ this.view?.destroy();
255
+ this.view = undefined;
256
+ }
257
+ firstUpdated(changed) {
258
+ super.firstUpdated(changed);
259
+ this.finishHydration();
260
+ }
261
+ updated(changed) {
262
+ super.updated(changed);
263
+ this.internals.setFormValue(this.value);
264
+ const control = this.control;
265
+ if (control) {
266
+ this.copyValidity(control);
267
+ const anchor = this.view
268
+ ? this.partNamed('view')?.querySelector('[contenteditable]')
269
+ : undefined;
270
+ if (anchor && !control.validity.valid) {
271
+ this.internals.setValidity(control.validity, this.internals.validationMessage, anchor);
272
+ }
273
+ this.refreshFeedback();
274
+ }
275
+ this.toggleState('find-open', this.findOpen);
276
+ this.toggleState('replace-open', this.findOpen && this.replaceOpen && !this.readOnly);
277
+ const mount = this.partNamed('view');
278
+ if (changed.has('engine') || (this.engine && !this.view && mount)) {
279
+ this.view?.destroy();
280
+ this.view = undefined;
281
+ if (this.engine && mount && this.isConnected) {
282
+ this.view = this.engine.mount(mount, this.engineState, {
283
+ caret: (from) => {
284
+ this.caret = from;
285
+ },
286
+ change: (next) => this.engineChanged(next),
287
+ });
288
+ }
289
+ this.engineValue = this.view ? this.value : undefined;
290
+ this.toggleState('connected', this.view !== undefined);
291
+ return;
292
+ }
293
+ const drawn = ENGINE_KEYS.filter((key) => changed.has(key));
294
+ if (this.view &&
295
+ drawn.length > 0 &&
296
+ !(drawn.length === 1 && drawn[0] === 'value' && this.value === this.engineValue)) {
297
+ this.engineValue = this.value;
298
+ this.view.update(this.engineState);
299
+ }
300
+ }
301
+ engineChanged(next) {
302
+ if (!this.requestValue(next)) {
303
+ return false;
304
+ }
305
+ this.engineValue = next;
306
+ this.engineEdited = true;
307
+ this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
308
+ return true;
309
+ }
310
+ async openFind() {
311
+ const control = this.control;
312
+ if (!this.view && control && control.selectionEnd > control.selectionStart) {
313
+ const selected = control.value.slice(control.selectionStart, control.selectionEnd);
314
+ if (!selected.includes('\n')) {
315
+ this.query = { ...this.query, search: selected };
316
+ }
317
+ }
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();
332
+ }
333
+ search(direction) {
334
+ if (this.query.search === '' || !this.readable) {
335
+ this.found = undefined;
336
+ this.view?.findEnd?.();
337
+ return;
338
+ }
339
+ if (this.view?.find) {
340
+ this.found = this.view.find(this.query, direction);
341
+ return;
342
+ }
343
+ const control = this.control;
344
+ const ranges = findRanges(this.value, this.query);
345
+ if (!control || ranges.length === 0) {
346
+ this.found = { count: ranges.length, index: -1 };
347
+ return;
348
+ }
349
+ const at = direction === 'first' ? control.selectionStart : control.selectionEnd;
350
+ let index = direction === 'previous'
351
+ ? ranges.findLastIndex(([from]) => from < control.selectionStart)
352
+ : ranges.findIndex(([from]) => from >= at);
353
+ if (index === -1) {
354
+ index = direction === 'previous' ? ranges.length - 1 : 0;
355
+ }
356
+ const [from, to] = ranges[index] ?? [0, 0];
357
+ control.setSelectionRange(from, to);
358
+ this.caret = from;
359
+ this.found = { count: ranges.length, index };
360
+ }
361
+ replace(all) {
362
+ if (this.readOnly || this.query.search === '' || !this.readable) {
363
+ return;
364
+ }
365
+ if (this.view?.replace) {
366
+ this.found = this.view.replace(this.query, all);
367
+ return;
368
+ }
369
+ const control = this.control;
370
+ const pattern = patternOf(this.query);
371
+ if (!control || !pattern) {
372
+ return;
373
+ }
374
+ if (all) {
375
+ this.requestValue(this.value.replace(pattern, this.query.replace));
376
+ }
377
+ else {
378
+ const selected = findRanges(this.value, this.query).find(([from, to]) => from === control.selectionStart && to === control.selectionEnd);
379
+ if (!selected) {
380
+ this.search('next');
381
+ return;
382
+ }
383
+ const [from, to] = selected;
384
+ const one = new RegExp(pattern.source, pattern.flags.replace('g', ''));
385
+ const replaced = this.value.slice(from, to).replace(one, this.query.replace);
386
+ this.requestValue(this.value.slice(0, from) + replaced + this.value.slice(to));
387
+ restoreValue(control, this.value);
388
+ control.setSelectionRange(from + replaced.length, from + replaced.length);
389
+ }
390
+ 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
+ });
407
+ }
408
+ renderHeader() {
409
+ const toolbar = this.tag('toolbar');
410
+ const button = this.tag('icon-button');
411
+ const language = LANGUAGE_NAMES[this.language.trim().toLowerCase()] ?? this.language;
412
+ return html `<div part="header">
413
+ ${this.renderLabel()}
414
+ ${language ? html `<span part="language">${language}</span>` : nothing}
415
+ <${toolbar} part="controls" exportparts="toolbar: controls-toolbar" exportparts="toolbar: controls-toolbar" label=${this.controlsLabel}>
416
+ <slot name="actions"></slot>
417
+ <${button}
418
+ part="find"
419
+ exportparts="button: find-button, live: find-live, spinner: find-spinner, text: find-text"
420
+ .expanded=${this.findOpen}
421
+ label=${this.findLabel}
422
+ @click=${() => (this.findOpen ? this.closeFind() : void this.openFind())}
423
+ @helix-press=${stopPress}
424
+ >
425
+ ${drawIcon(magnifyingGlass)}
426
+ </${button}>
427
+ </${toolbar}>
428
+ </div>`;
429
+ }
430
+ 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>`;
557
+ }
558
+ renderDiagnostics() {
559
+ if (this.diagnostics.length === 0) {
560
+ return nothing;
561
+ }
562
+ const lines = diagnosticLines(this.value, this.diagnostics);
563
+ return html `<ul part="diagnostics" aria-label=${this.diagnosticsLabel}>
564
+ ${this.diagnostics.map((one, index) => {
565
+ const words = formatWords(this, 'diagnosticLabel', {
566
+ line: lines[index] ?? 1,
567
+ message: one.message,
568
+ });
569
+ const go = () => this.select(one.from, one.to);
570
+ if (one.tone === 'warning') {
571
+ return html `<li><button part="diagnostic diagnostic-warning" type="button" @click=${go}>
572
+ <span part="diagnostic-icon" aria-hidden="true">${drawIcon(warning)}</span>${words}
573
+ </button></li>`;
574
+ }
575
+ if (one.tone === 'info') {
576
+ return html `<li><button part="diagnostic diagnostic-info" type="button" @click=${go}>
577
+ <span part="diagnostic-icon" aria-hidden="true">${drawIcon(info)}</span>${words}
578
+ </button></li>`;
579
+ }
580
+ return html `<li><button part="diagnostic diagnostic-danger" type="button" @click=${go}>
581
+ <span part="diagnostic-icon" aria-hidden="true">${drawIcon(xCircleFill)}</span>${words}
582
+ </button></li>`;
583
+ })}
584
+ </ul>`;
585
+ }
586
+ renderStatus() {
587
+ const { column, line } = positionOf(this.value, this.caret);
588
+ return html `<div part="status">
589
+ <span part="position">${formatWords(this, 'positionLabel', { column, line })}</span>
590
+ <span part="problem-count">${formatWords(this, 'problemsLabel', { count: this.diagnostics.length })}</span>
591
+ </div>`;
592
+ }
593
+ render() {
594
+ return html `<div part="field">
595
+ ${this.renderHeader()}${this.renderFind()}
596
+ <div part="body">
597
+ <textarea
598
+ part="control"
599
+ id=${this.controlId}
600
+ .value=${this.value}
601
+ name=${this.name || nothing}
602
+ placeholder=${this.placeholder || nothing}
603
+ rows=${this.rows}
604
+ spellcheck="false"
605
+ autocapitalize="off"
606
+ translate="no"
607
+ wrap=${this.wrap ? 'soft' : 'off'}
608
+ aria-describedby=${this.describedBy ?? nothing}
609
+ aria-invalid=${this.invalidity ?? nothing}
610
+ ?disabled=${this.isDisabled}
611
+ ?hidden=${this.engine !== undefined}
612
+ ?readonly=${this.readOnly}
613
+ ?required=${this.required}
614
+ @input=${this.handleInput}
615
+ @change=${this.handleChange}
616
+ @keydown=${this.handleKeydown}
617
+ @keyup=${this.handleCaret}
618
+ @pointerup=${this.handleCaret}
619
+ @select=${this.handleCaret}
620
+ ></textarea>
621
+ <div part="view" ?hidden=${!this.engine} @focusout=${this.handleViewFocusOut}></div>
622
+ </div>
623
+ ${this.renderDiagnostics()}${this.renderStatus()}
624
+ </div>
625
+ ${this.renderHint()}${this.renderFeedback()}`;
626
+ }
627
+ };
628
+ __decorate([
629
+ words,
630
+ property({ attribute: 'controls-label' })
631
+ ], HelixHeadlessCodeEditor.prototype, "controlsLabel", void 0);
632
+ __decorate([
633
+ words,
634
+ property({ attribute: 'diagnostic-label' })
635
+ ], HelixHeadlessCodeEditor.prototype, "diagnosticLabel", void 0);
636
+ __decorate([
637
+ property({ attribute: false })
638
+ ], HelixHeadlessCodeEditor.prototype, "diagnostics", void 0);
639
+ __decorate([
640
+ words,
641
+ property({ attribute: 'diagnostics-label' })
642
+ ], HelixHeadlessCodeEditor.prototype, "diagnosticsLabel", void 0);
643
+ __decorate([
644
+ property({ attribute: false })
645
+ ], HelixHeadlessCodeEditor.prototype, "engine", void 0);
646
+ __decorate([
647
+ words,
648
+ property({ attribute: 'find-close-label' })
649
+ ], HelixHeadlessCodeEditor.prototype, "findCloseLabel", void 0);
650
+ __decorate([
651
+ words,
652
+ property({ attribute: 'find-controls-label' })
653
+ ], HelixHeadlessCodeEditor.prototype, "findControlsLabel", void 0);
654
+ __decorate([
655
+ words,
656
+ property({ attribute: 'find-invalid-label' })
657
+ ], HelixHeadlessCodeEditor.prototype, "findInvalidLabel", void 0);
658
+ __decorate([
659
+ words,
660
+ property({ attribute: 'find-label' })
661
+ ], HelixHeadlessCodeEditor.prototype, "findLabel", void 0);
662
+ __decorate([
663
+ words,
664
+ property({ attribute: 'find-match-case-label' })
665
+ ], HelixHeadlessCodeEditor.prototype, "findMatchCaseLabel", void 0);
666
+ __decorate([
667
+ words,
668
+ property({ attribute: 'find-next-label' })
669
+ ], HelixHeadlessCodeEditor.prototype, "findNextLabel", void 0);
670
+ __decorate([
671
+ words,
672
+ property({ attribute: 'find-previous-label' })
673
+ ], HelixHeadlessCodeEditor.prototype, "findPreviousLabel", void 0);
674
+ __decorate([
675
+ words,
676
+ property({ attribute: 'find-regex-label' })
677
+ ], HelixHeadlessCodeEditor.prototype, "findRegexLabel", void 0);
678
+ __decorate([
679
+ words,
680
+ property({ attribute: 'find-replace-label' })
681
+ ], HelixHeadlessCodeEditor.prototype, "findReplaceLabel", void 0);
682
+ __decorate([
683
+ words,
684
+ property({ attribute: 'find-whole-word-label' })
685
+ ], HelixHeadlessCodeEditor.prototype, "findWholeWordLabel", void 0);
686
+ __decorate([
687
+ property()
688
+ ], HelixHeadlessCodeEditor.prototype, "language", void 0);
689
+ __decorate([
690
+ property({ attribute: 'line-numbers', type: Boolean })
691
+ ], HelixHeadlessCodeEditor.prototype, "lineNumbers", void 0);
692
+ __decorate([
693
+ words,
694
+ property({ attribute: 'matches-label' })
695
+ ], HelixHeadlessCodeEditor.prototype, "matchesLabel", void 0);
696
+ __decorate([
697
+ property()
698
+ ], HelixHeadlessCodeEditor.prototype, "placeholder", void 0);
699
+ __decorate([
700
+ words,
701
+ property({ attribute: 'position-label' })
702
+ ], HelixHeadlessCodeEditor.prototype, "positionLabel", void 0);
703
+ __decorate([
704
+ words,
705
+ property({ attribute: 'problems-label' })
706
+ ], HelixHeadlessCodeEditor.prototype, "problemsLabel", void 0);
707
+ __decorate([
708
+ property({ attribute: 'readonly', reflect: true, type: Boolean })
709
+ ], HelixHeadlessCodeEditor.prototype, "readOnly", void 0);
710
+ __decorate([
711
+ words,
712
+ property({ attribute: 'replace-all-label' })
713
+ ], HelixHeadlessCodeEditor.prototype, "replaceAllLabel", void 0);
714
+ __decorate([
715
+ words,
716
+ property({ attribute: 'replace-label' })
717
+ ], HelixHeadlessCodeEditor.prototype, "replaceLabel", void 0);
718
+ __decorate([
719
+ words,
720
+ property({ attribute: 'replace-with-label' })
721
+ ], HelixHeadlessCodeEditor.prototype, "replaceWithLabel", void 0);
722
+ __decorate([
723
+ property({ converter: numberConverter('rows', 8) })
724
+ ], HelixHeadlessCodeEditor.prototype, "rows", void 0);
725
+ __decorate([
726
+ property({ reflect: true, type: Boolean })
727
+ ], HelixHeadlessCodeEditor.prototype, "wrap", void 0);
728
+ __decorate([
729
+ state()
730
+ ], HelixHeadlessCodeEditor.prototype, "caret", void 0);
731
+ __decorate([
732
+ state()
733
+ ], 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
+ HelixHeadlessCodeEditor = __decorate([
744
+ customElement('helix-headless-code-editor')
745
+ ], HelixHeadlessCodeEditor);
746
+ export { HelixHeadlessCodeEditor };