@api-client/ui 0.5.10 → 0.5.12
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/build/src/elements/code-editor/internals/CodeEditor.d.ts +197 -0
- package/build/src/elements/code-editor/internals/CodeEditor.d.ts.map +1 -0
- package/build/src/elements/code-editor/internals/CodeEditor.js +612 -0
- package/build/src/elements/code-editor/internals/CodeEditor.js.map +1 -0
- package/build/src/elements/code-editor/internals/CodeEditor.styles.d.ts +3 -0
- package/build/src/elements/code-editor/internals/CodeEditor.styles.d.ts.map +1 -0
- package/build/src/elements/code-editor/internals/CodeEditor.styles.js +150 -0
- package/build/src/elements/code-editor/internals/CodeEditor.styles.js.map +1 -0
- package/build/src/elements/code-editor/internals/Linter.d.ts +5 -0
- package/build/src/elements/code-editor/internals/Linter.d.ts.map +1 -0
- package/build/src/elements/code-editor/internals/Linter.js +74 -0
- package/build/src/elements/code-editor/internals/Linter.js.map +1 -0
- package/build/src/elements/code-editor/ui-code-editor.d.ts +13 -0
- package/build/src/elements/code-editor/ui-code-editor.d.ts.map +1 -0
- package/build/src/elements/code-editor/ui-code-editor.js +28 -0
- package/build/src/elements/code-editor/ui-code-editor.js.map +1 -0
- package/build/src/index.d.ts +2 -0
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +2 -0
- package/build/src/index.js.map +1 -1
- package/build/src/md/chip/internals/Chip.styles.d.ts.map +1 -1
- package/build/src/md/chip/internals/Chip.styles.js +1 -0
- package/build/src/md/chip/internals/Chip.styles.js.map +1 -1
- package/demo/elements/code-editor/CodeEditorDemo.ts +212 -0
- package/demo/elements/code-editor/index.html +19 -0
- package/demo/elements/index.html +3 -0
- package/package.json +11 -3
- package/src/elements/code-editor/README.md +204 -0
- package/src/elements/code-editor/internals/CodeEditor.styles.ts +150 -0
- package/src/elements/code-editor/internals/CodeEditor.ts +595 -0
- package/src/elements/code-editor/internals/Linter.ts +87 -0
- package/src/elements/code-editor/ui-code-editor.ts +24 -0
- package/src/index.ts +10 -0
- package/src/md/chip/internals/Chip.styles.ts +1 -0
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
import { __esDecorate, __runInitializers } from "tslib";
|
|
2
|
+
import { html, LitElement, nothing } from 'lit';
|
|
3
|
+
import { property, query, state } from 'lit/decorators.js';
|
|
4
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
5
|
+
import { EditorState } from '@codemirror/state';
|
|
6
|
+
import { autocompletion } from '@codemirror/autocomplete';
|
|
7
|
+
import { javascript } from '@codemirror/lang-javascript';
|
|
8
|
+
import { syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language';
|
|
9
|
+
import { oneDark } from '@codemirror/theme-one-dark';
|
|
10
|
+
import { keymap } from '@codemirror/view';
|
|
11
|
+
import { defaultKeymap } from '@codemirror/commands';
|
|
12
|
+
import { EditorView, MatchDecorator, Decoration, ViewPlugin, WidgetType, } from '@codemirror/view';
|
|
13
|
+
class PlaceholderWidget extends WidgetType {
|
|
14
|
+
placeholderText;
|
|
15
|
+
constructor(placeholderText) {
|
|
16
|
+
super();
|
|
17
|
+
this.placeholderText = placeholderText;
|
|
18
|
+
}
|
|
19
|
+
eq(other) {
|
|
20
|
+
return this.placeholderText == other.placeholderText;
|
|
21
|
+
}
|
|
22
|
+
toDOM() {
|
|
23
|
+
const placeholder = document.createElement('span');
|
|
24
|
+
placeholder.className = 'cm-pill';
|
|
25
|
+
placeholder.textContent = this.placeholderText;
|
|
26
|
+
return placeholder;
|
|
27
|
+
}
|
|
28
|
+
ignoreEvent() {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// We do variables Salesforce style, with double curly braces.
|
|
33
|
+
// This decorator replaces {{variable}} with a placeholder widget.
|
|
34
|
+
const placeholderMatcher = new MatchDecorator({
|
|
35
|
+
regexp: /\{\{(\w+)\}\}/g,
|
|
36
|
+
decoration: (match) => Decoration.replace({
|
|
37
|
+
widget: new PlaceholderWidget(match[1]),
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* @see https://codemirror.net/examples/decoration/
|
|
42
|
+
*/
|
|
43
|
+
class AtomicDecorationRange {
|
|
44
|
+
placeholders;
|
|
45
|
+
constructor(view) {
|
|
46
|
+
this.placeholders = placeholderMatcher.createDeco(view);
|
|
47
|
+
}
|
|
48
|
+
update(update) {
|
|
49
|
+
this.placeholders = placeholderMatcher.updateDeco(update, this.placeholders);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const placeholders = ViewPlugin.fromClass(AtomicDecorationRange, {
|
|
53
|
+
decorations: (instance) => instance.placeholders,
|
|
54
|
+
provide: (plugin) => EditorView.atomicRanges.of((view) => {
|
|
55
|
+
return view.plugin(plugin)?.placeholders || Decoration.none;
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
let CodeEditor = (() => {
|
|
59
|
+
let _classSuper = LitElement;
|
|
60
|
+
let _label_decorators;
|
|
61
|
+
let _label_initializers = [];
|
|
62
|
+
let _label_extraInitializers = [];
|
|
63
|
+
let _supportingText_decorators;
|
|
64
|
+
let _supportingText_initializers = [];
|
|
65
|
+
let _supportingText_extraInitializers = [];
|
|
66
|
+
let _disabled_decorators;
|
|
67
|
+
let _disabled_initializers = [];
|
|
68
|
+
let _disabled_extraInitializers = [];
|
|
69
|
+
let _invalid_decorators;
|
|
70
|
+
let _invalid_initializers = [];
|
|
71
|
+
let _invalid_extraInitializers = [];
|
|
72
|
+
let _name_decorators;
|
|
73
|
+
let _name_initializers = [];
|
|
74
|
+
let _name_extraInitializers = [];
|
|
75
|
+
let _required_decorators;
|
|
76
|
+
let _required_initializers = [];
|
|
77
|
+
let _required_extraInitializers = [];
|
|
78
|
+
let _placeholder_decorators;
|
|
79
|
+
let _placeholder_initializers = [];
|
|
80
|
+
let _placeholder_extraInitializers = [];
|
|
81
|
+
let _value_decorators;
|
|
82
|
+
let _value_initializers = [];
|
|
83
|
+
let _value_extraInitializers = [];
|
|
84
|
+
let _functionSchemas_decorators;
|
|
85
|
+
let _functionSchemas_initializers = [];
|
|
86
|
+
let _functionSchemas_extraInitializers = [];
|
|
87
|
+
let _suggestions_decorators;
|
|
88
|
+
let _suggestions_initializers = [];
|
|
89
|
+
let _suggestions_extraInitializers = [];
|
|
90
|
+
let _darkTheme_decorators;
|
|
91
|
+
let _darkTheme_initializers = [];
|
|
92
|
+
let _darkTheme_extraInitializers = [];
|
|
93
|
+
let _language_decorators;
|
|
94
|
+
let _language_initializers = [];
|
|
95
|
+
let _language_extraInitializers = [];
|
|
96
|
+
let _editorContainer_decorators;
|
|
97
|
+
let _editorContainer_initializers = [];
|
|
98
|
+
let _editorContainer_extraInitializers = [];
|
|
99
|
+
let _hasContent_decorators;
|
|
100
|
+
let _hasContent_initializers = [];
|
|
101
|
+
let _hasContent_extraInitializers = [];
|
|
102
|
+
let _isEditorFocus_decorators;
|
|
103
|
+
let _isEditorFocus_initializers = [];
|
|
104
|
+
let _isEditorFocus_extraInitializers = [];
|
|
105
|
+
return class CodeEditor extends _classSuper {
|
|
106
|
+
static {
|
|
107
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
108
|
+
_label_decorators = [property({ type: String })];
|
|
109
|
+
_supportingText_decorators = [property({ type: String, attribute: 'supporting-text' })];
|
|
110
|
+
_disabled_decorators = [property({ type: Boolean, reflect: true })];
|
|
111
|
+
_invalid_decorators = [property({ type: Boolean, reflect: true })];
|
|
112
|
+
_name_decorators = [property({ type: String })];
|
|
113
|
+
_required_decorators = [property({ type: Boolean })];
|
|
114
|
+
_placeholder_decorators = [property({ type: String })];
|
|
115
|
+
_value_decorators = [property({ type: String })];
|
|
116
|
+
_functionSchemas_decorators = [property({ type: Array, attribute: false })];
|
|
117
|
+
_suggestions_decorators = [property({ type: Array, attribute: false })];
|
|
118
|
+
_darkTheme_decorators = [property({ type: Boolean, attribute: 'dark-theme' })];
|
|
119
|
+
_language_decorators = [property({ type: String })];
|
|
120
|
+
_editorContainer_decorators = [query('.editor-container')];
|
|
121
|
+
_hasContent_decorators = [state()];
|
|
122
|
+
_isEditorFocus_decorators = [state()];
|
|
123
|
+
__esDecorate(this, null, _label_decorators, { kind: "accessor", name: "label", static: false, private: false, access: { has: obj => "label" in obj, get: obj => obj.label, set: (obj, value) => { obj.label = value; } }, metadata: _metadata }, _label_initializers, _label_extraInitializers);
|
|
124
|
+
__esDecorate(this, null, _supportingText_decorators, { kind: "accessor", name: "supportingText", static: false, private: false, access: { has: obj => "supportingText" in obj, get: obj => obj.supportingText, set: (obj, value) => { obj.supportingText = value; } }, metadata: _metadata }, _supportingText_initializers, _supportingText_extraInitializers);
|
|
125
|
+
__esDecorate(this, null, _disabled_decorators, { kind: "accessor", name: "disabled", static: false, private: false, access: { has: obj => "disabled" in obj, get: obj => obj.disabled, set: (obj, value) => { obj.disabled = value; } }, metadata: _metadata }, _disabled_initializers, _disabled_extraInitializers);
|
|
126
|
+
__esDecorate(this, null, _invalid_decorators, { kind: "accessor", name: "invalid", static: false, private: false, access: { has: obj => "invalid" in obj, get: obj => obj.invalid, set: (obj, value) => { obj.invalid = value; } }, metadata: _metadata }, _invalid_initializers, _invalid_extraInitializers);
|
|
127
|
+
__esDecorate(this, null, _name_decorators, { kind: "accessor", name: "name", static: false, private: false, access: { has: obj => "name" in obj, get: obj => obj.name, set: (obj, value) => { obj.name = value; } }, metadata: _metadata }, _name_initializers, _name_extraInitializers);
|
|
128
|
+
__esDecorate(this, null, _required_decorators, { kind: "accessor", name: "required", static: false, private: false, access: { has: obj => "required" in obj, get: obj => obj.required, set: (obj, value) => { obj.required = value; } }, metadata: _metadata }, _required_initializers, _required_extraInitializers);
|
|
129
|
+
__esDecorate(this, null, _placeholder_decorators, { kind: "accessor", name: "placeholder", static: false, private: false, access: { has: obj => "placeholder" in obj, get: obj => obj.placeholder, set: (obj, value) => { obj.placeholder = value; } }, metadata: _metadata }, _placeholder_initializers, _placeholder_extraInitializers);
|
|
130
|
+
__esDecorate(this, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
|
|
131
|
+
__esDecorate(this, null, _functionSchemas_decorators, { kind: "accessor", name: "functionSchemas", static: false, private: false, access: { has: obj => "functionSchemas" in obj, get: obj => obj.functionSchemas, set: (obj, value) => { obj.functionSchemas = value; } }, metadata: _metadata }, _functionSchemas_initializers, _functionSchemas_extraInitializers);
|
|
132
|
+
__esDecorate(this, null, _suggestions_decorators, { kind: "accessor", name: "suggestions", static: false, private: false, access: { has: obj => "suggestions" in obj, get: obj => obj.suggestions, set: (obj, value) => { obj.suggestions = value; } }, metadata: _metadata }, _suggestions_initializers, _suggestions_extraInitializers);
|
|
133
|
+
__esDecorate(this, null, _darkTheme_decorators, { kind: "accessor", name: "darkTheme", static: false, private: false, access: { has: obj => "darkTheme" in obj, get: obj => obj.darkTheme, set: (obj, value) => { obj.darkTheme = value; } }, metadata: _metadata }, _darkTheme_initializers, _darkTheme_extraInitializers);
|
|
134
|
+
__esDecorate(this, null, _language_decorators, { kind: "accessor", name: "language", static: false, private: false, access: { has: obj => "language" in obj, get: obj => obj.language, set: (obj, value) => { obj.language = value; } }, metadata: _metadata }, _language_initializers, _language_extraInitializers);
|
|
135
|
+
__esDecorate(this, null, _editorContainer_decorators, { kind: "accessor", name: "editorContainer", static: false, private: false, access: { has: obj => "editorContainer" in obj, get: obj => obj.editorContainer, set: (obj, value) => { obj.editorContainer = value; } }, metadata: _metadata }, _editorContainer_initializers, _editorContainer_extraInitializers);
|
|
136
|
+
__esDecorate(this, null, _hasContent_decorators, { kind: "accessor", name: "hasContent", static: false, private: false, access: { has: obj => "hasContent" in obj, get: obj => obj.hasContent, set: (obj, value) => { obj.hasContent = value; } }, metadata: _metadata }, _hasContent_initializers, _hasContent_extraInitializers);
|
|
137
|
+
__esDecorate(this, null, _isEditorFocus_decorators, { kind: "accessor", name: "isEditorFocus", static: false, private: false, access: { has: obj => "isEditorFocus" in obj, get: obj => obj.isEditorFocus, set: (obj, value) => { obj.isEditorFocus = value; } }, metadata: _metadata }, _isEditorFocus_initializers, _isEditorFocus_extraInitializers);
|
|
138
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Shadow root configuration for the component.
|
|
142
|
+
* Uses 'open' mode for accessibility and delegates focus to enable proper focus management.
|
|
143
|
+
*/
|
|
144
|
+
static shadowRootOptions = {
|
|
145
|
+
mode: 'open',
|
|
146
|
+
delegatesFocus: true,
|
|
147
|
+
};
|
|
148
|
+
#label_accessor_storage = __runInitializers(this, _label_initializers, ''
|
|
149
|
+
/**
|
|
150
|
+
* Supporting text displayed below the editor
|
|
151
|
+
*/
|
|
152
|
+
);
|
|
153
|
+
/**
|
|
154
|
+
* The label text displayed as placeholder/floating label
|
|
155
|
+
*/
|
|
156
|
+
get label() { return this.#label_accessor_storage; }
|
|
157
|
+
set label(value) { this.#label_accessor_storage = value; }
|
|
158
|
+
#supportingText_accessor_storage = (__runInitializers(this, _label_extraInitializers), __runInitializers(this, _supportingText_initializers, ''
|
|
159
|
+
/**
|
|
160
|
+
* Whether the component is disabled
|
|
161
|
+
*/
|
|
162
|
+
));
|
|
163
|
+
/**
|
|
164
|
+
* Supporting text displayed below the editor
|
|
165
|
+
*/
|
|
166
|
+
get supportingText() { return this.#supportingText_accessor_storage; }
|
|
167
|
+
set supportingText(value) { this.#supportingText_accessor_storage = value; }
|
|
168
|
+
#disabled_accessor_storage = (__runInitializers(this, _supportingText_extraInitializers), __runInitializers(this, _disabled_initializers, false
|
|
169
|
+
/**
|
|
170
|
+
* Whether the component is in an invalid state
|
|
171
|
+
*/
|
|
172
|
+
));
|
|
173
|
+
/**
|
|
174
|
+
* Whether the component is disabled
|
|
175
|
+
*/
|
|
176
|
+
get disabled() { return this.#disabled_accessor_storage; }
|
|
177
|
+
set disabled(value) { this.#disabled_accessor_storage = value; }
|
|
178
|
+
#invalid_accessor_storage = (__runInitializers(this, _disabled_extraInitializers), __runInitializers(this, _invalid_initializers, false
|
|
179
|
+
/**
|
|
180
|
+
* The name attribute for form integration
|
|
181
|
+
*/
|
|
182
|
+
));
|
|
183
|
+
/**
|
|
184
|
+
* Whether the component is in an invalid state
|
|
185
|
+
*/
|
|
186
|
+
get invalid() { return this.#invalid_accessor_storage; }
|
|
187
|
+
set invalid(value) { this.#invalid_accessor_storage = value; }
|
|
188
|
+
#name_accessor_storage = (__runInitializers(this, _invalid_extraInitializers), __runInitializers(this, _name_initializers, ''
|
|
189
|
+
/**
|
|
190
|
+
* Whether the input is required
|
|
191
|
+
*/
|
|
192
|
+
));
|
|
193
|
+
/**
|
|
194
|
+
* The name attribute for form integration
|
|
195
|
+
*/
|
|
196
|
+
get name() { return this.#name_accessor_storage; }
|
|
197
|
+
set name(value) { this.#name_accessor_storage = value; }
|
|
198
|
+
#required_accessor_storage = (__runInitializers(this, _name_extraInitializers), __runInitializers(this, _required_initializers, false
|
|
199
|
+
/**
|
|
200
|
+
* Placeholder text shown when editor is empty
|
|
201
|
+
*/
|
|
202
|
+
));
|
|
203
|
+
/**
|
|
204
|
+
* Whether the input is required
|
|
205
|
+
*/
|
|
206
|
+
get required() { return this.#required_accessor_storage; }
|
|
207
|
+
set required(value) { this.#required_accessor_storage = value; }
|
|
208
|
+
#placeholder_accessor_storage = (__runInitializers(this, _required_extraInitializers), __runInitializers(this, _placeholder_initializers, ''
|
|
209
|
+
/**
|
|
210
|
+
* The editor content value
|
|
211
|
+
*/
|
|
212
|
+
));
|
|
213
|
+
/**
|
|
214
|
+
* Placeholder text shown when editor is empty
|
|
215
|
+
*/
|
|
216
|
+
get placeholder() { return this.#placeholder_accessor_storage; }
|
|
217
|
+
set placeholder(value) { this.#placeholder_accessor_storage = value; }
|
|
218
|
+
#value_accessor_storage = (__runInitializers(this, _placeholder_extraInitializers), __runInitializers(this, _value_initializers, ''
|
|
219
|
+
/**
|
|
220
|
+
* Available function schemas for autocomplete
|
|
221
|
+
*/
|
|
222
|
+
));
|
|
223
|
+
/**
|
|
224
|
+
* The editor content value
|
|
225
|
+
*/
|
|
226
|
+
get value() { return this.#value_accessor_storage; }
|
|
227
|
+
set value(value) { this.#value_accessor_storage = value; }
|
|
228
|
+
#functionSchemas_accessor_storage = (__runInitializers(this, _value_extraInitializers), __runInitializers(this, _functionSchemas_initializers, []
|
|
229
|
+
/**
|
|
230
|
+
* Available suggestions for autocomplete
|
|
231
|
+
*/
|
|
232
|
+
));
|
|
233
|
+
/**
|
|
234
|
+
* Available function schemas for autocomplete
|
|
235
|
+
*/
|
|
236
|
+
get functionSchemas() { return this.#functionSchemas_accessor_storage; }
|
|
237
|
+
set functionSchemas(value) { this.#functionSchemas_accessor_storage = value; }
|
|
238
|
+
#suggestions_accessor_storage = (__runInitializers(this, _functionSchemas_extraInitializers), __runInitializers(this, _suggestions_initializers, []
|
|
239
|
+
/**
|
|
240
|
+
* Whether to use dark theme
|
|
241
|
+
*/
|
|
242
|
+
));
|
|
243
|
+
/**
|
|
244
|
+
* Available suggestions for autocomplete
|
|
245
|
+
*/
|
|
246
|
+
get suggestions() { return this.#suggestions_accessor_storage; }
|
|
247
|
+
set suggestions(value) { this.#suggestions_accessor_storage = value; }
|
|
248
|
+
#darkTheme_accessor_storage = (__runInitializers(this, _suggestions_extraInitializers), __runInitializers(this, _darkTheme_initializers, false
|
|
249
|
+
/**
|
|
250
|
+
* Programming language for syntax highlighting
|
|
251
|
+
*/
|
|
252
|
+
));
|
|
253
|
+
/**
|
|
254
|
+
* Whether to use dark theme
|
|
255
|
+
*/
|
|
256
|
+
get darkTheme() { return this.#darkTheme_accessor_storage; }
|
|
257
|
+
set darkTheme(value) { this.#darkTheme_accessor_storage = value; }
|
|
258
|
+
#language_accessor_storage = (__runInitializers(this, _darkTheme_extraInitializers), __runInitializers(this, _language_initializers, 'javascript'));
|
|
259
|
+
/**
|
|
260
|
+
* Programming language for syntax highlighting
|
|
261
|
+
*/
|
|
262
|
+
get language() { return this.#language_accessor_storage; }
|
|
263
|
+
set language(value) { this.#language_accessor_storage = value; }
|
|
264
|
+
#editorContainer_accessor_storage = (__runInitializers(this, _language_extraInitializers), __runInitializers(this, _editorContainer_initializers, void 0));
|
|
265
|
+
get editorContainer() { return this.#editorContainer_accessor_storage; }
|
|
266
|
+
set editorContainer(value) { this.#editorContainer_accessor_storage = value; }
|
|
267
|
+
#hasContent_accessor_storage = (__runInitializers(this, _editorContainer_extraInitializers), __runInitializers(this, _hasContent_initializers, false));
|
|
268
|
+
get hasContent() { return this.#hasContent_accessor_storage; }
|
|
269
|
+
set hasContent(value) { this.#hasContent_accessor_storage = value; }
|
|
270
|
+
#isEditorFocus_accessor_storage = (__runInitializers(this, _hasContent_extraInitializers), __runInitializers(this, _isEditorFocus_initializers, false));
|
|
271
|
+
get isEditorFocus() { return this.#isEditorFocus_accessor_storage; }
|
|
272
|
+
set isEditorFocus(value) { this.#isEditorFocus_accessor_storage = value; }
|
|
273
|
+
editorView = __runInitializers(this, _isEditorFocus_extraInitializers);
|
|
274
|
+
suggestionMap = new Map();
|
|
275
|
+
functionMap = new Map();
|
|
276
|
+
_previousValue = '';
|
|
277
|
+
/**
|
|
278
|
+
* Get all suggestions (placeholders) currently in the editor
|
|
279
|
+
*/
|
|
280
|
+
get activeSuggestions() {
|
|
281
|
+
const suggestions = [];
|
|
282
|
+
const placeholderPattern = /\{\{(\w+)\}\}/g;
|
|
283
|
+
let match;
|
|
284
|
+
while ((match = placeholderPattern.exec(this.value)) !== null) {
|
|
285
|
+
const placeholderText = match[1];
|
|
286
|
+
// Find suggestion by label
|
|
287
|
+
const suggestion = this.suggestions.find((s) => s.label.toLowerCase() === placeholderText.toLowerCase());
|
|
288
|
+
if (suggestion) {
|
|
289
|
+
suggestions.push(suggestion);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return suggestions;
|
|
293
|
+
}
|
|
294
|
+
connectedCallback() {
|
|
295
|
+
super.connectedCallback();
|
|
296
|
+
this.setupSuggestionMaps();
|
|
297
|
+
}
|
|
298
|
+
disconnectedCallback() {
|
|
299
|
+
super.disconnectedCallback();
|
|
300
|
+
this.editorView?.destroy();
|
|
301
|
+
}
|
|
302
|
+
firstUpdated() {
|
|
303
|
+
this.initializeCodeMirror();
|
|
304
|
+
this.updateEditorContent();
|
|
305
|
+
}
|
|
306
|
+
willUpdate(changedProperties) {
|
|
307
|
+
super.willUpdate(changedProperties);
|
|
308
|
+
if (changedProperties.has('suggestions') || changedProperties.has('functionSchemas')) {
|
|
309
|
+
this.setupSuggestionMaps();
|
|
310
|
+
}
|
|
311
|
+
if (changedProperties.has('value') && this.editorView) {
|
|
312
|
+
this.updateEditorContent();
|
|
313
|
+
}
|
|
314
|
+
if (changedProperties.has('disabled')) {
|
|
315
|
+
this.updateEditorState();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Initialize CodeMirror editor with extensions
|
|
320
|
+
*/
|
|
321
|
+
initializeCodeMirror() {
|
|
322
|
+
const extensions = [
|
|
323
|
+
keymap.of(defaultKeymap),
|
|
324
|
+
syntaxHighlighting(defaultHighlightStyle),
|
|
325
|
+
autocompletion({
|
|
326
|
+
override: [this.createCompletionSource()],
|
|
327
|
+
activateOnTyping: true,
|
|
328
|
+
maxRenderedOptions: 10,
|
|
329
|
+
}),
|
|
330
|
+
EditorView.updateListener.of((update) => {
|
|
331
|
+
if (update.docChanged) {
|
|
332
|
+
this.handleEditorChange();
|
|
333
|
+
}
|
|
334
|
+
if (update.focusChanged) {
|
|
335
|
+
this.handleFocusChange(update.view.hasFocus);
|
|
336
|
+
}
|
|
337
|
+
}),
|
|
338
|
+
placeholders,
|
|
339
|
+
// linter((view) => functionLinter(view as unknown as EditorView, this.functionSchemas)),
|
|
340
|
+
];
|
|
341
|
+
// Add language support
|
|
342
|
+
if (this.language === 'javascript') {
|
|
343
|
+
extensions.push(javascript());
|
|
344
|
+
}
|
|
345
|
+
// Add theme
|
|
346
|
+
if (this.darkTheme) {
|
|
347
|
+
extensions.push(oneDark);
|
|
348
|
+
}
|
|
349
|
+
// Add placeholder
|
|
350
|
+
if (this.placeholder) {
|
|
351
|
+
extensions.push(EditorView.contentAttributes.of({
|
|
352
|
+
'aria-placeholder': this.placeholder,
|
|
353
|
+
}));
|
|
354
|
+
}
|
|
355
|
+
const state = EditorState.create({
|
|
356
|
+
doc: this.value,
|
|
357
|
+
extensions,
|
|
358
|
+
});
|
|
359
|
+
this.editorView = new EditorView({
|
|
360
|
+
state,
|
|
361
|
+
parent: this.editorContainer,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Create completion source for functions and suggestions
|
|
366
|
+
*/
|
|
367
|
+
createCompletionSource() {
|
|
368
|
+
return (context) => {
|
|
369
|
+
const { pos } = context;
|
|
370
|
+
const line = context.state.doc.lineAt(pos);
|
|
371
|
+
const textBefore = line.text.slice(0, pos - line.from);
|
|
372
|
+
// Check if we're typing a function name
|
|
373
|
+
const functionMatch = textBefore.match(/(\w+)$/);
|
|
374
|
+
if (functionMatch) {
|
|
375
|
+
const prefix = functionMatch[1];
|
|
376
|
+
const functions = this.functionSchemas
|
|
377
|
+
.filter((fn) => fn.name.toLowerCase().startsWith(prefix.toLowerCase()))
|
|
378
|
+
.map((fn) => ({
|
|
379
|
+
label: fn.name,
|
|
380
|
+
detail: fn.description || '',
|
|
381
|
+
info: this.formatFunctionInfo(fn),
|
|
382
|
+
apply: (view, completion, from, to) => {
|
|
383
|
+
const functionCall = this.formatFunctionCall(fn);
|
|
384
|
+
view.dispatch({
|
|
385
|
+
changes: { from, to, insert: functionCall },
|
|
386
|
+
selection: { anchor: from + functionCall.length },
|
|
387
|
+
});
|
|
388
|
+
this.dispatchFunctionInsert(fn, from);
|
|
389
|
+
},
|
|
390
|
+
}));
|
|
391
|
+
if (functions.length > 0) {
|
|
392
|
+
return {
|
|
393
|
+
from: pos - prefix.length,
|
|
394
|
+
options: functions,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Check if we're typing a suggestion trigger (e.g., {{)
|
|
399
|
+
const suggestionMatch = textBefore.match(/\{\{(\w*)$/);
|
|
400
|
+
if (suggestionMatch) {
|
|
401
|
+
const prefix = suggestionMatch[1];
|
|
402
|
+
const suggestions = this.suggestions
|
|
403
|
+
.filter((suggestion) => suggestion.label.toLowerCase().startsWith(prefix.toLowerCase()))
|
|
404
|
+
.map((suggestion) => ({
|
|
405
|
+
label: suggestion.label,
|
|
406
|
+
detail: suggestion.description || '',
|
|
407
|
+
info: suggestion.suffix || '',
|
|
408
|
+
apply: (view, completion, from, to) => {
|
|
409
|
+
const placeholderText = `{{${suggestion.label}}}`;
|
|
410
|
+
view.dispatch({
|
|
411
|
+
changes: { from: from - 2, to, insert: placeholderText }, // -2 to include the {{
|
|
412
|
+
selection: { anchor: from - 2 + placeholderText.length },
|
|
413
|
+
});
|
|
414
|
+
this.dispatchSuggestionInsert(suggestion, from - 2);
|
|
415
|
+
},
|
|
416
|
+
}));
|
|
417
|
+
if (suggestions.length > 0) {
|
|
418
|
+
return {
|
|
419
|
+
from: pos - prefix.length,
|
|
420
|
+
options: suggestions,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return null;
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Format function information for autocomplete
|
|
429
|
+
*/
|
|
430
|
+
formatFunctionInfo(fn) {
|
|
431
|
+
let info = fn.description || '';
|
|
432
|
+
if (fn.parameters && fn.parameters.length > 0) {
|
|
433
|
+
info += '\n\nParameters:\n';
|
|
434
|
+
fn.parameters.forEach((param) => {
|
|
435
|
+
info += `• ${param.name}: ${param.type}`;
|
|
436
|
+
if (param.description) {
|
|
437
|
+
info += ` - ${param.description}`;
|
|
438
|
+
}
|
|
439
|
+
info += '\n';
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
if (fn.returns) {
|
|
443
|
+
info += `\nReturns: ${fn.returns}`;
|
|
444
|
+
}
|
|
445
|
+
return info;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Format function call with parameters
|
|
449
|
+
*/
|
|
450
|
+
formatFunctionCall(fn) {
|
|
451
|
+
if (!fn.parameters || fn.parameters.length === 0) {
|
|
452
|
+
return `${fn.name}()`;
|
|
453
|
+
}
|
|
454
|
+
const params = fn.parameters
|
|
455
|
+
.map((param) => {
|
|
456
|
+
if (param.required) {
|
|
457
|
+
return param.name;
|
|
458
|
+
}
|
|
459
|
+
return `${param.name}?`;
|
|
460
|
+
})
|
|
461
|
+
.join(', ');
|
|
462
|
+
return `${fn.name}(${params})`;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Setup suggestion and function maps for quick lookup
|
|
466
|
+
*/
|
|
467
|
+
setupSuggestionMaps() {
|
|
468
|
+
this.suggestionMap.clear();
|
|
469
|
+
this.functionMap.clear();
|
|
470
|
+
this.suggestions.forEach((suggestion) => {
|
|
471
|
+
this.suggestionMap.set(suggestion.id, suggestion);
|
|
472
|
+
});
|
|
473
|
+
this.functionSchemas.forEach((fn) => {
|
|
474
|
+
this.functionMap.set(fn.id, fn);
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Update editor content when value changes
|
|
479
|
+
*/
|
|
480
|
+
updateEditorContent() {
|
|
481
|
+
if (!this.editorView)
|
|
482
|
+
return;
|
|
483
|
+
const currentValue = this.editorView.state.doc.toString();
|
|
484
|
+
if (currentValue !== this.value) {
|
|
485
|
+
this.editorView.dispatch({
|
|
486
|
+
changes: {
|
|
487
|
+
from: 0,
|
|
488
|
+
to: this.editorView.state.doc.length,
|
|
489
|
+
insert: this.value,
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Update editor state (e.g., disabled state)
|
|
496
|
+
*/
|
|
497
|
+
updateEditorState() {
|
|
498
|
+
if (!this.editorView)
|
|
499
|
+
return;
|
|
500
|
+
// For now, we'll handle disabled state differently
|
|
501
|
+
// CodeMirror 6 doesn't use reconfigure for editable
|
|
502
|
+
if (this.disabled) {
|
|
503
|
+
this.editorView.contentDOM.setAttribute('contenteditable', 'false');
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
this.editorView.contentDOM.setAttribute('contenteditable', 'true');
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Handle editor content change
|
|
511
|
+
*/
|
|
512
|
+
handleEditorChange() {
|
|
513
|
+
if (!this.editorView)
|
|
514
|
+
return;
|
|
515
|
+
const newValue = this.editorView.state.doc.toString();
|
|
516
|
+
if (newValue !== this._previousValue) {
|
|
517
|
+
this._previousValue = newValue;
|
|
518
|
+
this.value = newValue;
|
|
519
|
+
this.hasContent = newValue.length > 0;
|
|
520
|
+
this.dispatchEvent(new Event('input', { bubbles: true }));
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Handle focus change
|
|
525
|
+
*/
|
|
526
|
+
handleFocusChange(hasFocus) {
|
|
527
|
+
this.isEditorFocus = hasFocus;
|
|
528
|
+
if (!hasFocus && this.value !== this._previousValue) {
|
|
529
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Dispatch function insert event
|
|
534
|
+
*/
|
|
535
|
+
dispatchFunctionInsert(functionSchema, position) {
|
|
536
|
+
const event = new CustomEvent('function-insert', {
|
|
537
|
+
detail: { functionSchema, position },
|
|
538
|
+
bubbles: true,
|
|
539
|
+
});
|
|
540
|
+
this.dispatchEvent(event);
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Dispatch suggestion insert event
|
|
544
|
+
*/
|
|
545
|
+
dispatchSuggestionInsert(suggestion, position) {
|
|
546
|
+
const event = new CustomEvent('suggestion-insert', {
|
|
547
|
+
detail: { suggestion, position },
|
|
548
|
+
bubbles: true,
|
|
549
|
+
});
|
|
550
|
+
this.dispatchEvent(event);
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Focus the editor
|
|
554
|
+
*/
|
|
555
|
+
focus() {
|
|
556
|
+
this.editorView?.focus();
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Get the editor's current selection
|
|
560
|
+
*/
|
|
561
|
+
getSelection() {
|
|
562
|
+
if (!this.editorView)
|
|
563
|
+
return null;
|
|
564
|
+
const { from, to } = this.editorView.state.selection.main;
|
|
565
|
+
return { from, to };
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Insert text at the current cursor position
|
|
569
|
+
*/
|
|
570
|
+
insertText(text) {
|
|
571
|
+
if (!this.editorView)
|
|
572
|
+
return;
|
|
573
|
+
const { from, to } = this.editorView.state.selection.main;
|
|
574
|
+
this.editorView.dispatch({
|
|
575
|
+
changes: { from, to, insert: text },
|
|
576
|
+
selection: { anchor: from + text.length },
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
render() {
|
|
580
|
+
const hasLabel = !!this.label;
|
|
581
|
+
const hasSupportingText = !!this.supportingText;
|
|
582
|
+
return html `
|
|
583
|
+
<div class="surface ${classMap({ 'has-focus': this.isEditorFocus, 'invalid': this.invalid })}">
|
|
584
|
+
<div class="content">
|
|
585
|
+
${hasLabel ? html `<div class="label">${this.label}</div>` : nothing}
|
|
586
|
+
|
|
587
|
+
<div class="editor-container" part="editor"></div>
|
|
588
|
+
</div>
|
|
589
|
+
|
|
590
|
+
${hasSupportingText ? html `<div class="supporting-text">${this.supportingText}</div>` : nothing}
|
|
591
|
+
</div>
|
|
592
|
+
`;
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
})();
|
|
596
|
+
/**
|
|
597
|
+
* A CodeMirror 6 based editor component that supports function autocomplete and suggestion placeholders.
|
|
598
|
+
*
|
|
599
|
+
* Features:
|
|
600
|
+
* - Dynamic function schema loading
|
|
601
|
+
* - Autocomplete for functions and suggestions
|
|
602
|
+
* - Suggestion placeholders with double curly braces ({{suggestion}})
|
|
603
|
+
* - Keyboard navigation
|
|
604
|
+
* - Accessibility support
|
|
605
|
+
*
|
|
606
|
+
* @fires function-insert - When a function is inserted
|
|
607
|
+
* @fires suggestion-insert - When a suggestion is inserted
|
|
608
|
+
* @fires input - When the editor content changes
|
|
609
|
+
* @fires change - When the editor loses focus and content has changed
|
|
610
|
+
*/
|
|
611
|
+
export default CodeEditor;
|
|
612
|
+
//# sourceMappingURL=CodeEditor.js.map
|