@fusion.dev/helix-headless 0.219.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.
- package/api.json +1 -1
- package/custom-elements.json +1 -1
- package/dist/code-editor/root/code-editor-root.d.ts +48 -51
- package/dist/code-editor/root/code-editor-root.js +67 -249
- package/dist/element-events.generated.js +3 -1
- package/dist/find/index.d.ts +1 -0
- package/dist/find/index.js +1 -0
- package/dist/find/root/find-root.d.ts +314 -0
- package/dist/find/root/find-root.js +577 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/log/group/log-group.d.ts +11 -2
- package/dist/log/group/log-group.js +17 -3
- package/dist/terminal/root/terminal-root.d.ts +49 -32
- package/dist/terminal/root/terminal-root.js +72 -181
- package/dist/tree/item/tree-item.d.ts +11 -0
- package/dist/tree/item/tree-item.js +19 -1
- package/examples.json +1 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +849 -66
- package/types/svelte.d.ts +1 -0
- package/types/vue.d.ts +156 -2
- package/types/words.d.ts +87 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { AriaForwardController, HelixFormElement, ListedController } from '@fusion.dev/helix-core';
|
|
1
|
+
import { AriaForwardController, findRanges, HelixFormElement, ListedController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import '../../button/index.js';
|
|
4
|
+
import '../../find/index.js';
|
|
4
5
|
import '../../icon-button/index.js';
|
|
5
6
|
import '../../toolbar/index.js';
|
|
6
7
|
/** A problem in the text, from a linter or a language server, by where it is. */
|
|
@@ -81,8 +82,8 @@ export declare const positionOf: (value: string, at: number) => {
|
|
|
81
82
|
column: number;
|
|
82
83
|
line: number;
|
|
83
84
|
};
|
|
84
|
-
/** Where a query matches in a text, as `[from, to]` offsets, with no empty match. */
|
|
85
|
-
export
|
|
85
|
+
/** Where a query matches in a text, as `[from, to]` offsets, with no empty match. Find's own, from core. */
|
|
86
|
+
export { findRanges };
|
|
86
87
|
declare const HelixHeadlessCodeEditor_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
|
|
87
88
|
/**
|
|
88
89
|
* @summary An editor for a file of code, drawn by a code editor you connect.
|
|
@@ -128,6 +129,7 @@ declare const HelixHeadlessCodeEditor_base: (new (...args: any[]) => import("@fu
|
|
|
128
129
|
*
|
|
129
130
|
* @event {Event} change - The reader finished an edit and focus left the code, in the engine or the plain field.
|
|
130
131
|
* @event {HelixBeforeChangeEvent} helix-before-change - The text is about to change. Cancelable: cancel it to keep the current text.
|
|
132
|
+
* @event {HelixFoundEvent} helix-found - The find bar searched or moved among the matches, with `count` and the selected match's `index`, counting from 0, or -1 for none.
|
|
131
133
|
* @event {Event} input - Fired after the text changes.
|
|
132
134
|
*
|
|
133
135
|
* @csspart body - The box the code is drawn in, between the header and the problems.
|
|
@@ -142,9 +144,9 @@ declare const HelixHeadlessCodeEditor_base: (new (...args: any[]) => import("@fu
|
|
|
142
144
|
* @csspart diagnostics - The list of problems under the code, while there are any.
|
|
143
145
|
* @csspart feedback - The message that says what is wrong.
|
|
144
146
|
* @csspart field - The frame around the header, the code, the problems and the status line.
|
|
145
|
-
* @csspart find - The
|
|
146
|
-
* @csspart find-
|
|
147
|
-
* @csspart find-
|
|
147
|
+
* @csspart find - The find bar under the header, where the reader searches the code: a Find of its own.
|
|
148
|
+
* @csspart find-actions - The find bar's `actions` slot, which the editor fills with nothing.
|
|
149
|
+
* @csspart find-bar - The find bar's strip: the replace toggle, the field, the count, the toolbar, and the replace row while it shows.
|
|
148
150
|
* @csspart find-close - The button that closes the find bar.
|
|
149
151
|
* @csspart find-close-button - The close button's inner button.
|
|
150
152
|
* @csspart find-close-live - The close button's live region, which speaks while the button is pending.
|
|
@@ -154,10 +156,11 @@ declare const HelixHeadlessCodeEditor_base: (new (...args: any[]) => import("@fu
|
|
|
154
156
|
* @csspart find-controls-toolbar - The box inside the find bar's toolbar that lays its buttons out.
|
|
155
157
|
* @csspart find-count - Which match is selected, of how many.
|
|
156
158
|
* @csspart find-field - The field the search is typed in.
|
|
157
|
-
* @csspart find-live - The
|
|
159
|
+
* @csspart find-live - The find bar's live region, which says the count as it changes.
|
|
158
160
|
* @csspart find-match-case - The option that finds only the same case. Pressed while it applies.
|
|
159
161
|
* @csspart find-match-case-button - The match case button's inner button.
|
|
160
162
|
* @csspart find-match-case-live - The match case button's live region, which speaks while the button is pending.
|
|
163
|
+
* @csspart find-match-case-pressed - The match case option while it applies, beside `find-match-case`.
|
|
161
164
|
* @csspart find-match-case-spinner - The ring the match case button turns while it is pending.
|
|
162
165
|
* @csspart find-match-case-text - The box the match case button's icon sits in.
|
|
163
166
|
* @csspart find-next - The button that selects the next match.
|
|
@@ -173,18 +176,34 @@ declare const HelixHeadlessCodeEditor_base: (new (...args: any[]) => import("@fu
|
|
|
173
176
|
* @csspart find-regex - The option that reads the search as a regular expression. Pressed while it applies.
|
|
174
177
|
* @csspart find-regex-button - The regular expression button's inner button.
|
|
175
178
|
* @csspart find-regex-live - The regular expression button's live region, which speaks while the button is pending.
|
|
179
|
+
* @csspart find-regex-pressed - The regular expression option while it applies, beside `find-regex`.
|
|
176
180
|
* @csspart find-regex-spinner - The ring the regular expression button turns while it is pending.
|
|
177
181
|
* @csspart find-regex-text - The box the regular expression button's icon sits in.
|
|
178
|
-
* @csspart find-replace - The button that
|
|
179
|
-
* @csspart find-replace-
|
|
180
|
-
* @csspart find-replace-
|
|
181
|
-
* @csspart find-replace-
|
|
182
|
-
* @csspart find-replace-
|
|
183
|
-
* @csspart find-
|
|
184
|
-
* @csspart find-
|
|
182
|
+
* @csspart find-replace - The button that replaces the selected match.
|
|
183
|
+
* @csspart find-replace-all - The button that replaces every match.
|
|
184
|
+
* @csspart find-replace-all-button - The Replace all button's inner button.
|
|
185
|
+
* @csspart find-replace-all-live - The Replace all button's live region, which speaks while the button is pending.
|
|
186
|
+
* @csspart find-replace-all-spinner - The ring the Replace all button turns while it is pending.
|
|
187
|
+
* @csspart find-replace-all-text - The box the Replace all button's words sits in.
|
|
188
|
+
* @csspart find-replace-button - The Replace button's inner button.
|
|
189
|
+
* @csspart find-replace-field - The field the replacement is typed in.
|
|
190
|
+
* @csspart find-replace-live - The Replace button's live region, which speaks while the button is pending.
|
|
191
|
+
* @csspart find-replace-spinner - The ring the Replace button turns while it is pending.
|
|
192
|
+
* @csspart find-replace-text - The box the Replace button's words sits in.
|
|
193
|
+
* @csspart find-replace-toggle - The button that shows the replace field under the find field. Expanded while it shows. Not drawn while `readonly`.
|
|
194
|
+
* @csspart find-replace-toggle-button - The find and replace button's inner button.
|
|
195
|
+
* @csspart find-replace-toggle-live - The find and replace button's live region, which speaks while the button is pending.
|
|
196
|
+
* @csspart find-replace-toggle-spinner - The ring the find and replace button turns while it is pending.
|
|
197
|
+
* @csspart find-replace-toggle-text - The box the find and replace button's icon sits in.
|
|
198
|
+
* @csspart find-toggle - The button in the header that opens and closes the find bar. Expanded while it is open.
|
|
199
|
+
* @csspart find-toggle-button - The Find button's inner button.
|
|
200
|
+
* @csspart find-toggle-live - The Find button's live region, which speaks while the button is pending.
|
|
201
|
+
* @csspart find-toggle-spinner - The ring the Find button turns while it is pending.
|
|
202
|
+
* @csspart find-toggle-text - The box the Find button's icon sits in.
|
|
185
203
|
* @csspart find-whole-word - The option that finds only whole words. Pressed while it applies.
|
|
186
204
|
* @csspart find-whole-word-button - The whole word button's inner button.
|
|
187
205
|
* @csspart find-whole-word-live - The whole word button's live region, which speaks while the button is pending.
|
|
206
|
+
* @csspart find-whole-word-pressed - The whole word option while it applies, beside `find-whole-word`.
|
|
188
207
|
* @csspart find-whole-word-spinner - The ring the whole word button turns while it is pending.
|
|
189
208
|
* @csspart find-whole-word-text - The box the whole word button's icon sits in.
|
|
190
209
|
* @csspart header - The strip above the code: the name, the language, and the controls.
|
|
@@ -193,27 +212,14 @@ declare const HelixHeadlessCodeEditor_base: (new (...args: any[]) => import("@fu
|
|
|
193
212
|
* @csspart language - The language's name in the header.
|
|
194
213
|
* @csspart position - The caret's line and column, in the status line.
|
|
195
214
|
* @csspart problem-count - How many problems there are, in the status line.
|
|
196
|
-
* @csspart replace - The button that replaces the selected match.
|
|
197
|
-
* @csspart replace-all - The button that replaces every match.
|
|
198
|
-
* @csspart replace-all-button - The Replace all button's inner button.
|
|
199
|
-
* @csspart replace-all-live - The Replace all button's live region, which speaks while the button is pending.
|
|
200
|
-
* @csspart replace-all-spinner - The ring the Replace all button turns while it is pending.
|
|
201
|
-
* @csspart replace-all-text - The box the Replace all button's words sit in.
|
|
202
|
-
* @csspart replace-button - The Replace button's inner button.
|
|
203
|
-
* @csspart replace-field - The field the replacement is typed in.
|
|
204
|
-
* @csspart replace-live - The Replace button's live region, which speaks while the button is pending.
|
|
205
|
-
* @csspart replace-spinner - The ring the Replace button turns while it is pending.
|
|
206
|
-
* @csspart replace-text - The box the Replace button's words sit in.
|
|
207
215
|
* @csspart status - The strip under the code.
|
|
208
216
|
* @csspart view - Where an engine draws, while one is connected.
|
|
209
217
|
*
|
|
210
218
|
* @cssstate connected - An engine draws the editor.
|
|
211
219
|
* @cssstate find-open - The find bar is open.
|
|
212
220
|
* @cssstate invalid - The editor has an error to show, from `required` or your `feedback`.
|
|
213
|
-
* @cssstate replace-open - The replace field shows under the find field.
|
|
214
221
|
*/
|
|
215
222
|
export declare class HelixHeadlessCodeEditor extends HelixHeadlessCodeEditor_base {
|
|
216
|
-
protected static markRules: string[];
|
|
217
223
|
/**
|
|
218
224
|
* The key this family's words are registered under, its styled tag:
|
|
219
225
|
* `registerWords('de', { 'helix-code-editor': { findLabel: 'Suchen' } })`.
|
|
@@ -286,13 +292,10 @@ export declare class HelixHeadlessCodeEditor extends HelixHeadlessCodeEditor_bas
|
|
|
286
292
|
wrap: boolean;
|
|
287
293
|
/** Where the caret is, as an offset into the value. */
|
|
288
294
|
private caret;
|
|
289
|
-
/** Whether the find bar is open. */
|
|
295
|
+
/** Whether the find bar is open, as it last said. */
|
|
290
296
|
private findOpen;
|
|
291
|
-
/**
|
|
292
|
-
private
|
|
293
|
-
/** Whether the replace field shows under the find field. */
|
|
294
|
-
private replaceOpen;
|
|
295
|
-
private query;
|
|
297
|
+
/** The find bar drawn now, and how a search is reported to it. */
|
|
298
|
+
private find;
|
|
296
299
|
private view;
|
|
297
300
|
/** Whether Escape came just before, which lets Tab leave the plain field. */
|
|
298
301
|
private escaped;
|
|
@@ -303,8 +306,6 @@ export declare class HelixHeadlessCodeEditor extends HelixHeadlessCodeEditor_bas
|
|
|
303
306
|
/** The textarea, which the form's validity is read from either way. */
|
|
304
307
|
private get control();
|
|
305
308
|
private get engineState();
|
|
306
|
-
/** Whether the search can be read, which a regular expression may not be. */
|
|
307
|
-
private get readable();
|
|
308
309
|
/** Puts the caret in the editor, as a label pointing at it would. */
|
|
309
310
|
focus(options?: FocusOptions): void;
|
|
310
311
|
/** Selects a range of the code and puts the caret there, in the engine or the plain field. */
|
|
@@ -319,19 +320,18 @@ export declare class HelixHeadlessCodeEditor extends HelixHeadlessCodeEditor_bas
|
|
|
319
320
|
private readonly handleViewFocusOut;
|
|
320
321
|
/** Opens the find bar, with the selection as the search where one is selected, and puts focus in its field. */
|
|
321
322
|
private openFind;
|
|
322
|
-
/**
|
|
323
|
-
private
|
|
323
|
+
/** Hands the find bar drawn now the editor to search through, once per bar. */
|
|
324
|
+
private attachFind;
|
|
325
|
+
/** The editor, as the find bar searches through it: the engine, or the plain field. */
|
|
326
|
+
private readonly finder;
|
|
324
327
|
/** Finds the search and selects a match, in the engine or the plain field. */
|
|
325
|
-
private
|
|
326
|
-
/** Replaces the selected match, or every match, through `requestValue
|
|
327
|
-
private
|
|
328
|
-
/**
|
|
329
|
-
private
|
|
328
|
+
private findIn;
|
|
329
|
+
/** Replaces the selected match, or every match, through `requestValue`, and finds the next. */
|
|
330
|
+
private replaceIn;
|
|
331
|
+
/** The find bar opened or closed, which the Find button shows. */
|
|
332
|
+
private readonly handleFindToggle;
|
|
330
333
|
/** The find key opens the find bar from anywhere in the editor, ahead of the browser's own. */
|
|
331
334
|
private readonly handleKeys;
|
|
332
|
-
private readonly handleFindInput;
|
|
333
|
-
private readonly handleReplaceInput;
|
|
334
|
-
private readonly handleFindKeys;
|
|
335
335
|
/** An edit in the textarea, which has taken the character already: put back where it is refused. */
|
|
336
336
|
private readonly handleInput;
|
|
337
337
|
/** Relayed, because `change` does not cross a shadow boundary on its own. */
|
|
@@ -343,14 +343,12 @@ export declare class HelixHeadlessCodeEditor extends HelixHeadlessCodeEditor_bas
|
|
|
343
343
|
* Escape came just before it, which lets the reader leave.
|
|
344
344
|
*/
|
|
345
345
|
private readonly handleKeydown;
|
|
346
|
-
/** The words the find bar's count says, or its regular expression's fault. */
|
|
347
|
-
private get countText();
|
|
348
346
|
/** The header: the name, the language, and the toolbar of the page's actions and Find. */
|
|
349
347
|
protected renderHeader(): TemplateResult;
|
|
350
348
|
/**
|
|
351
|
-
* The find bar,
|
|
352
|
-
*
|
|
353
|
-
*
|
|
349
|
+
* The find bar, a Find of the editor's own that searches the engine or the
|
|
350
|
+
* plain field, kept in the tree while closed so its fields keep what was
|
|
351
|
+
* typed. The editor takes the find key itself, so the bar takes none.
|
|
354
352
|
*/
|
|
355
353
|
protected renderFind(): TemplateResult;
|
|
356
354
|
/** The problems, each a button that moves the caret to it, in its tone. */
|
|
@@ -364,4 +362,3 @@ declare global {
|
|
|
364
362
|
'helix-headless-code-editor': HelixHeadlessCodeEditor;
|
|
365
363
|
}
|
|
366
364
|
}
|
|
367
|
-
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { AriaForwardController, customElement, drawIcon, formatWords, HelixFormElement, ListedController,
|
|
3
|
-
import {
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
275
|
+
find.query = selected;
|
|
316
276
|
}
|
|
317
277
|
}
|
|
318
|
-
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
341
|
-
return;
|
|
290
|
+
return this.view.find(query, direction);
|
|
342
291
|
}
|
|
343
292
|
const control = this.control;
|
|
344
|
-
const ranges = findRanges(this.value,
|
|
293
|
+
const ranges = findRanges(this.value, query);
|
|
345
294
|
if (!control || ranges.length === 0) {
|
|
346
|
-
|
|
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
|
-
|
|
307
|
+
return { count: ranges.length, index };
|
|
360
308
|
}
|
|
361
|
-
|
|
362
|
-
if (this.readOnly
|
|
363
|
-
return;
|
|
309
|
+
replaceIn(query, all) {
|
|
310
|
+
if (this.readOnly) {
|
|
311
|
+
return undefined;
|
|
364
312
|
}
|
|
365
313
|
if (this.view?.replace) {
|
|
366
|
-
|
|
367
|
-
return;
|
|
314
|
+
return this.view.replace(query, all);
|
|
368
315
|
}
|
|
369
316
|
const control = this.control;
|
|
370
|
-
const pattern =
|
|
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,
|
|
322
|
+
this.requestValue(this.value.replace(pattern, query.replace));
|
|
376
323
|
}
|
|
377
324
|
else {
|
|
378
|
-
const selected = findRanges(this.value,
|
|
325
|
+
const selected = findRanges(this.value, query).find(([from, to]) => from === control.selectionStart && to === control.selectionEnd);
|
|
379
326
|
if (!selected) {
|
|
380
|
-
this.
|
|
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,
|
|
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.
|
|
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"
|
|
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.
|
|
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
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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);
|