@gravitee/ui-components 3.24.3-migrate-codemirror-f56ff96 → 3.24.3-migrate-codemirror-bed237b

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravitee/ui-components",
3
- "version": "3.24.3-migrate-codemirror-f56ff96",
3
+ "version": "3.24.3-migrate-codemirror-bed237b",
4
4
  "description": "Gravitee.io UI Components library, based on Web Components",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,12 +21,11 @@ import { uuid } from '../lib/utils';
21
21
  import { classMap } from 'lit-html/directives/class-map';
22
22
 
23
23
  import { EditorView, basicSetup } from '@codemirror/basic-setup';
24
- import { EditorState, Compartment, Prec, EditorSelection } from '@codemirror/state';
25
- import { autocompletion, startCompletion, pickedCompletion } from '@codemirror/autocomplete';
24
+ import { EditorState, Compartment } from '@codemirror/state';
25
+ import { autocompletion } from '@codemirror/autocomplete';
26
26
  import { json } from '@codemirror/lang-json';
27
27
  import { languages } from '@codemirror/language-data';
28
- import { placeholder, keymap } from '@codemirror/view';
29
- import { get } from 'object-path';
28
+ import { placeholder } from '@codemirror/view';
30
29
  import { InputElement } from '../mixins/input-element';
31
30
  import { i18n } from '../lib/i18n';
32
31
  import { skeleton } from '../styles/skeleton';
@@ -38,7 +37,7 @@ const readonlyCompartment = new Compartment();
38
37
  const placeholderCompartment = new Compartment();
39
38
 
40
39
  /**
41
- * Code editor component
40
+ * Code component
42
41
  *
43
42
  * ## Details
44
43
  * * has @theme facet
@@ -49,8 +48,7 @@ const placeholderCompartment = new Compartment();
49
48
  *
50
49
  * @attr {String} label - code language
51
50
  * @attr {String} value - code content to be highlighted
52
- * @attr {options} Object - options based on codemirror https://codemirror.net/doc/manual.html#config
53
- * @attr {Object} grammar - The grammar for Expression Language support
51
+ * @attr {options} Object - options based on codemirror
54
52
  * @attr {String} placeholder - an example value to display in the input when empty
55
53
  * @attr {Number} rows - number of rows of the text element
56
54
  * @attr {Boolean} large - for a large input (only if the field has one row)
@@ -71,7 +69,6 @@ export class GvCode extends InputElement(LitElement) {
71
69
  small: { type: Boolean },
72
70
  clipboard: { type: Boolean },
73
71
  _clipboardIcon: { type: String },
74
- _codeMirror: { type: Object },
75
72
  rows: { type: Number },
76
73
  _error: { type: String, attribute: false },
77
74
  };
@@ -106,6 +103,7 @@ export class GvCode extends InputElement(LitElement) {
106
103
  <div id="${this._id}" class="${classMap(inputClasses)}">
107
104
  ${this.clipboard
108
105
  ? html`<gv-button
106
+ class="btn-clipboard"
109
107
  title="${i18n('gv-code.copy')}"
110
108
  ?outlined="${!this._copied}"
111
109
  ?primary="${this._copied}"
@@ -127,212 +125,31 @@ export class GvCode extends InputElement(LitElement) {
127
125
  return this.rows === 1;
128
126
  }
129
127
 
130
- _getModelType(typeId) {
131
- if (this.grammar != null) {
132
- return get(this.grammar, `_types.${typeId}`);
133
- }
134
- return null;
135
- }
136
-
137
- _isMap(typeId) {
138
- return ['Map', 'HttpHeaders', 'MultiValueMap'].includes(typeId);
139
- }
140
-
141
- _getEnum(typeId) {
142
- if (this.grammar != null) {
143
- return get(this.grammar, `_enums.${typeId}`);
144
- }
145
- return [''];
146
- }
147
-
148
- _convertType(type) {
149
- if (type) {
150
- const lowerType = type.toLowerCase();
151
- if (lowerType === 'string') {
152
- // class, constant, enum, function, interface, keyword, method, namespace, property, text, type, and variable.
153
- return 'text';
154
- } else if (lowerType === 'httpheaders' || lowerType === 'multivaluemap') {
155
- return 'type';
156
- } else if (lowerType === 'int' || lowerType === 'long') {
157
- return 'variable';
158
- } else if (lowerType.includes('[]')) {
159
- return 'enum';
160
- }
161
- }
162
- return 'variable';
163
- }
164
-
165
- _autocomplete(ctx) {
166
- const { handler, match } = this._findAutocompleteHandler(ctx);
167
- return handler.run.call(this, ctx, match);
168
- }
169
-
170
- _buildMethods(type) {
171
- const modelType = this._getModelType(type);
172
- return modelType.methods.map(({ name, params = [], returnType }) => {
173
- const command = `${name}()`;
174
- const displayParams = params.map((p) => `${p.type} ${p.name}`);
175
- const label = `${name}(${displayParams.join(', ')})`;
176
- const detail = `return ${returnType}`;
177
- return {
178
- type: 'method',
179
- command,
180
- apply: (view, completion, from, to) => {
181
- const anchor = from + name.length + 1;
182
- view.dispatch({
183
- changes: { from, to, insert: completion.command },
184
- selection: { anchor },
185
- userEvent: 'input.complete',
186
- annotations: pickedCompletion.of(view),
187
- });
188
- },
189
- label,
190
- detail,
191
- };
192
- });
128
+ _autocomplete(completionContext) {
129
+ const { handler, match } = this.findBestAutocompleteHandler(completionContext);
130
+ return handler.run.call(this, completionContext, match);
193
131
  }
194
132
 
195
- _findAutocompleteHandler(ctx) {
196
- return this._completionHandlers
197
- .filter((handler) => (this.grammar != null && handler.supportEL === true) || handler.supportEL !== true)
198
- .map((handler) => {
199
- const match = handler.expr ? ctx.matchBefore(handler.expr) : true;
200
- return { handler, match };
201
- })
202
- .find(({ match, expr }) => match);
133
+ findBestAutocompleteHandler(completionContext) {
134
+ return this.defaultCompletionHandler;
203
135
  }
204
136
 
205
- get _completionHandlers() {
206
- return [
207
- {
208
- expr: /{#[a-z]*/,
209
- supportEL: true,
210
- run(ctx, match) {
211
- const prefix = match.text.replaceAll('{#', '');
212
- const from = ctx.pos - prefix.length;
213
- const options = Object.keys(this.grammar)
214
- .filter((command) => !command.startsWith('_') && command.startsWith(prefix))
215
- .map((command) => {
216
- return {
217
- // class, constant, enum, function, interface, keyword, method, namespace, property, text, type, and variable.
218
- type: 'variable',
219
- command,
220
- apply: command,
221
- label: command,
222
- };
223
- });
224
- return { from, options };
225
- },
226
- },
227
- {
228
- expr: /{#[a-z]*.[a-zA-Z]*/,
229
- supportEL: true,
230
- run(ctx, match) {
231
- const tokens = match.text.split('.');
232
- const key = tokens[0].replaceAll('{#', '');
233
- const prefix = tokens[1];
234
- const candidate = this.grammar[key][prefix];
235
- let options = [];
236
- let from = ctx.pos;
237
- if (candidate) {
238
- // const modelType = this._getModelType(candidate._type);
239
- if (this._isMap(candidate._type)) {
240
- options = this._getEnum(candidate._type).map((value) => {
241
- const type = this._convertType(candidate._type);
242
- const command = `['${value}'][0]`;
243
- return {
244
- type,
245
- command,
246
- apply: command,
247
- label: value,
248
- };
249
- });
250
- }
251
- } else {
252
- from -= prefix.length;
253
- const candidate = this.grammar[key];
254
- if (candidate._type) {
255
- options = this._buildMethods(candidate._type);
256
- } else {
257
- options = Object.keys(candidate)
258
- .filter((command) => !command.startsWith('_') && command.startsWith(prefix))
259
- .map((command) => ({
260
- type: this._convertType(candidate[command]._type),
261
- command,
262
- apply: command,
263
- label: command,
264
- }));
265
- }
266
- }
267
- return { from, options };
268
- },
269
- },
270
- {
271
- expr: /{#[a-z]*.[a-zA-Z]*.[a-zA-Z]*/,
272
- supportEL: true,
273
- run(ctx, match) {
274
- const tokens = match.text.split('.');
275
- const key = tokens[0].replaceAll('{#', '');
276
- const word = tokens[1];
277
- const prefix = tokens[2];
278
- const candidate = this.grammar[key][word];
279
- let options = [];
280
- let from = ctx.pos;
281
- if (candidate) {
282
- if (this._isMap(candidate._type)) {
283
- options = this._getEnum(candidate._type)
284
- .filter((command) => !command.startsWith('_') && command.startsWith(prefix))
285
- .map((label) => {
286
- const type = this._convertType(candidate._type);
287
- return {
288
- type,
289
- command: `['${label}'][0]`,
290
- apply(view, completion, _from, to) {
291
- const from = _from - prefix.length - 1;
292
- view.dispatch({
293
- changes: { from, to, insert: completion.command },
294
- selection: { anchor: from + completion.command.length },
295
- userEvent: 'input.complete',
296
- annotations: pickedCompletion.of(view),
297
- });
298
- },
299
- label,
300
- };
301
- });
302
- } else {
303
- if (candidate._type) {
304
- options = this._buildMethods(candidate._type);
305
- } else {
306
- from -= prefix.length;
307
- options = Object.keys(candidate)
308
- .filter((command) => command.startsWith(prefix))
309
- .map((command) => {
310
- return {
311
- type: this._convertType(candidate[command]._type),
312
- command,
313
- apply: command,
314
- label: command,
315
- };
316
- });
317
- }
318
- }
319
- }
320
- return { from, options };
321
- },
322
- },
323
- {
324
- run(ctx) {
325
- const autocompleteLanguage = ctx.state.languageDataAt('autocomplete', ctx.pos);
326
- if (autocompleteLanguage.length > 0) {
327
- return autocompleteLanguage[0](ctx);
137
+ get defaultCompletionHandler() {
138
+ return {
139
+ handler: {
140
+ run(completionContext) {
141
+ const language = completionContext.state.languageDataAt('autocomplete', completionContext.pos);
142
+ if (language.length > 0) {
143
+ return language[0](completionContext);
328
144
  }
329
- return { from: ctx.pos, options: [] };
145
+ return { from: completionContext.pos, options: [] };
330
146
  },
331
147
  },
332
- ];
148
+ match: true,
149
+ };
333
150
  }
334
151
 
335
- getExtensions() {
152
+ _getExtensions() {
336
153
  return [
337
154
  basicSetup,
338
155
  languageCompartment.of(json()),
@@ -348,27 +165,12 @@ export class GvCode extends InputElement(LitElement) {
348
165
  activateOnTyping: true,
349
166
  override: [this._autocomplete.bind(this)],
350
167
  }),
351
- Prec.high(this.insertELKeymap()),
168
+ ...this.getExtensions(),
352
169
  ];
353
170
  }
354
171
 
355
- insertELKeymap() {
356
- return keymap.of([
357
- {
358
- key: 'Ctrl-Shift-e',
359
- mac: 'Cmd-Shift-e',
360
- run(view) {
361
- view.dispatch(
362
- view.state.changeByRange((range) => ({
363
- changes: [{ from: range.from, to: range.to, insert: '{#}' }],
364
- range: EditorSelection.range(range.from + 2, range.to + 2),
365
- })),
366
- );
367
- startCompletion(view);
368
- return true;
369
- },
370
- },
371
- ]);
172
+ getExtensions() {
173
+ return [];
372
174
  }
373
175
 
374
176
  firstUpdated() {
@@ -376,7 +178,7 @@ export class GvCode extends InputElement(LitElement) {
376
178
  const parent = this.shadowRoot.querySelector(`[id=${this._id}]`);
377
179
  this._editorState = EditorState.create({
378
180
  doc: this.value,
379
- extensions: this.getExtensions(),
181
+ extensions: this._getExtensions(),
380
182
  });
381
183
  this._editorView = new EditorView({
382
184
  root: this.shadowRoot,
@@ -388,7 +190,7 @@ export class GvCode extends InputElement(LitElement) {
388
190
  if (this.clipboard) {
389
191
  import('clipboard-copy').then((mod) => {
390
192
  const copy = mod.default;
391
- this.shadowRoot.querySelector('gv-button').addEventListener('gv-button:click', () => {
193
+ this.shadowRoot.querySelector('.btn-clipboard').addEventListener('gv-button:click', () => {
392
194
  copy(this.value);
393
195
  this._copied = true;
394
196
  this._clipboardIcon = shapeCopied;
@@ -405,38 +207,62 @@ export class GvCode extends InputElement(LitElement) {
405
207
 
406
208
  async updated(properties) {
407
209
  if (properties.has('placeholder')) {
408
- this._editorView.dispatch({
409
- effects: placeholderCompartment.reconfigure(placeholder(this.placeholder)),
410
- });
210
+ this._reflectPlaceholder();
411
211
  }
412
212
  if (properties.has('value')) {
413
- const transaction = this._editorState.update({ changes: { from: 0, to: this._editorState.doc.length, insert: this.value } });
414
- this._editorState = transaction.state;
213
+ this._reflectValue();
415
214
  }
416
215
  if (properties.has('readonly')) {
417
- this._editorView.dispatch({
418
- effects: readonlyCompartment.reconfigure(EditorView.editable.of(!this.readonly)),
419
- });
216
+ this._reflectReadonly();
420
217
  }
421
-
422
218
  if (properties.has('options') && this.options) {
423
- if (this.options.mode) {
424
- const language = languages.find((language) => {
425
- return language.name.toUpperCase() === this.options.mode.toUpperCase() || language.alias.includes(this.options.mode);
426
- });
427
- if (language != null) {
428
- if (language.support == null) {
429
- await language.load();
430
- }
431
- this._editorView.dispatch({
432
- effects: languageCompartment.reconfigure(language.support.extension),
433
- });
434
- } else {
435
- console.warn(
436
- `Cannot find language ${this.options.mode}, please use language supported by CodeMirror 6`,
437
- languages.map((l) => l.name),
438
- );
219
+ await this._reflectOptions();
220
+ }
221
+ }
222
+
223
+ _reflectPlaceholder() {
224
+ this._editorView.dispatch({
225
+ effects: placeholderCompartment.reconfigure(placeholder(this.placeholder)),
226
+ });
227
+ }
228
+
229
+ _reflectValue() {
230
+ const transaction = this._editorState.update({
231
+ changes: {
232
+ from: 0,
233
+ to: this._editorState.doc.length,
234
+ insert: this.value,
235
+ },
236
+ });
237
+ this._editorState = transaction.state;
238
+ }
239
+
240
+ _reflectReadonly() {
241
+ this._editorView.dispatch({
242
+ effects: readonlyCompartment.reconfigure(EditorView.editable.of(!this.readonly)),
243
+ });
244
+ }
245
+
246
+ async _reflectOptions() {
247
+ if (this.options.mode) {
248
+ const language = languages.find((languageDescription) => {
249
+ return (
250
+ languageDescription.name.toUpperCase() === this.options.mode.toUpperCase() ||
251
+ languageDescription.alias.includes(this.options.mode)
252
+ );
253
+ });
254
+ if (language != null) {
255
+ if (language.support == null) {
256
+ await language.load();
439
257
  }
258
+ this._editorView.dispatch({
259
+ effects: languageCompartment.reconfigure(language.support.extension),
260
+ });
261
+ } else {
262
+ console.warn(
263
+ `Cannot find language ${this.options.mode}, please use language supported by CodeMirror 6`,
264
+ languages.map((l) => l.name),
265
+ );
440
266
  }
441
267
  }
442
268
  }
@@ -458,6 +284,14 @@ export class GvCode extends InputElement(LitElement) {
458
284
  position: relative;
459
285
  }
460
286
 
287
+ .btn-clipboard {
288
+ position: absolute;
289
+ right: 0;
290
+ top: 0;
291
+ z-index: 10;
292
+ }
293
+
294
+ /** Override Codemirror style */
461
295
  .input .cm-gutters {
462
296
  display: none;
463
297
  }
@@ -477,13 +311,6 @@ export class GvCode extends InputElement(LitElement) {
477
311
  .ͼ1.cm-editor.cm-focused {
478
312
  outline: none;
479
313
  }
480
-
481
- gv-button {
482
- position: absolute;
483
- right: 0;
484
- top: 0;
485
- z-index: 10;
486
- }
487
314
  `,
488
315
  ];
489
316
  }
@@ -15,16 +15,259 @@
15
15
  */
16
16
  import { GvCode } from '../molecules/gv-code';
17
17
  import { dispatchCustomEvent } from '../lib/events';
18
+ import { pickedCompletion, startCompletion } from '@codemirror/autocomplete';
19
+ import { keymap } from '@codemirror/view';
20
+ import { EditorSelection, Prec } from '@codemirror/state';
21
+ import { get } from 'object-path';
18
22
 
19
23
  /**
20
24
  * Expression Language component
21
25
  *
26
+ * @fires gv-expression-language:input - input events with the `value` on `detail`
27
+ * @fires gv-expression-language:ready - event dispatch when component is ready
28
+ * @fires gv-expression-language:clipboard-copy - event dispatch when component the `value` has been copied to clipboard
29
+ *
30
+ * @attr {String} label - code language
31
+ * @attr {String} value - code content to be highlighted
32
+ * @attr {options} Object - options based on codemirror
33
+ * @attr {String} placeholder - an example value to display in the input when empty
34
+ * @attr {Number} rows - number of rows of the text element
35
+ * @attr {Boolean} large - for a large input (only if the field has one row)
36
+ * @attr {Boolean} medium - for a medium input (only if the field has one row) (Default)
37
+ * @attr {Boolean} small - for a small input (only if the field has one row)
38
+ * @attr {Object} grammar - The grammar for Expression Language support
39
+ *
40
+ * @attr {Boolean} [clipboard=false]- true if field has clipboard button
41
+ * @attr {Boolean} [autofocus=false] - true to put the focus on the input
42
+ * @attr {Boolean} [readonly=false] - true if field is readonly mode
22
43
  */
23
44
  export class GvExpressionLanguage extends GvCode {
45
+ static get properties() {
46
+ return {
47
+ ...super.properties,
48
+ grammar: { type: Object },
49
+ };
50
+ }
51
+
24
52
  firstUpdated() {
25
53
  super.firstUpdated();
26
54
  dispatchCustomEvent(this, 'ready');
27
55
  }
56
+
57
+ getExtensions() {
58
+ return [Prec.high(this.insertELKeymap())];
59
+ }
60
+
61
+ insertELKeymap() {
62
+ return keymap.of([
63
+ {
64
+ key: 'Ctrl-Shift-e',
65
+ mac: 'Cmd-Shift-e',
66
+ run(view) {
67
+ view.dispatch(
68
+ view.state.changeByRange((range) => ({
69
+ changes: [{ from: range.from, to: range.to, insert: '{#}' }],
70
+ range: EditorSelection.range(range.from + 2, range.to + 2),
71
+ })),
72
+ );
73
+ startCompletion(view);
74
+ return true;
75
+ },
76
+ },
77
+ ]);
78
+ }
79
+
80
+ buildMethods(type) {
81
+ const modelType = this._getModelType(type);
82
+ return modelType.methods.map(({ name, params = [], returnType }) => {
83
+ const command = `${name}()`;
84
+ const displayParams = params.map((p) => `${p.type} ${p.name}`);
85
+ const label = `${name}(${displayParams.join(', ')})`;
86
+ const detail = `return ${returnType}`;
87
+ return {
88
+ type: 'method',
89
+ command,
90
+ apply: (view, completion, from, to) => {
91
+ const anchor = from + name.length + 1;
92
+ view.dispatch({
93
+ changes: { from, to, insert: completion.command },
94
+ selection: { anchor },
95
+ userEvent: 'input.complete',
96
+ annotations: pickedCompletion.of(view),
97
+ });
98
+ },
99
+ label,
100
+ detail,
101
+ };
102
+ });
103
+ }
104
+
105
+ findBestAutocompleteHandler(completionContext) {
106
+ return this.expressionLanguageCompletionHandlers
107
+ .filter((handler) => (this.grammar != null && handler.supportEL === true) || handler.supportEL !== true)
108
+ .map((handler) => {
109
+ const match = handler.expr ? completionContext.matchBefore(handler.expr) : true;
110
+ return { handler, match };
111
+ })
112
+ .find(({ match, expr }) => match);
113
+ }
114
+
115
+ getModelType(typeId) {
116
+ if (this.grammar != null) {
117
+ return get(this.grammar, `_types.${typeId}`);
118
+ }
119
+ return null;
120
+ }
121
+
122
+ isMap(typeId) {
123
+ return ['Map', 'HttpHeaders', 'MultiValueMap'].includes(typeId);
124
+ }
125
+
126
+ getEnum(typeId) {
127
+ if (this.grammar != null) {
128
+ return get(this.grammar, `_enums.${typeId}`);
129
+ }
130
+ return [''];
131
+ }
132
+
133
+ convertType(type) {
134
+ if (type) {
135
+ const lowerType = type.toLowerCase();
136
+ if (lowerType === 'string') {
137
+ // class, constant, enum, function, interface, keyword, method, namespace, property, text, type, and variable.
138
+ return 'text';
139
+ } else if (lowerType === 'httpheaders' || lowerType === 'multivaluemap') {
140
+ return 'type';
141
+ } else if (lowerType === 'int' || lowerType === 'long') {
142
+ return 'variable';
143
+ } else if (lowerType.includes('[]')) {
144
+ return 'enum';
145
+ }
146
+ }
147
+ return 'variable';
148
+ }
149
+
150
+ get expressionLanguageCompletionHandlers() {
151
+ return [
152
+ {
153
+ expr: /{#[a-z]*/,
154
+ supportEL: true,
155
+ run(completionContext, match) {
156
+ const prefix = match.text.replaceAll('{#', '');
157
+ const from = completionContext.pos - prefix.length;
158
+ const options = Object.keys(this.grammar)
159
+ .filter((command) => !command.startsWith('_') && command.startsWith(prefix))
160
+ .map((command) => {
161
+ return {
162
+ // class, constant, enum, function, interface, keyword, method, namespace, property, text, type, and variable.
163
+ type: 'variable',
164
+ command,
165
+ apply: command,
166
+ label: command,
167
+ };
168
+ });
169
+ return { from, options };
170
+ },
171
+ },
172
+ {
173
+ expr: /{#[a-z]*.[a-zA-Z]*/,
174
+ supportEL: true,
175
+ run(completionContext, match) {
176
+ const tokens = match.text.split('.');
177
+ const key = tokens[0].replaceAll('{#', '');
178
+ const prefix = tokens[1];
179
+ const candidate = this.grammar[key][prefix];
180
+ let options = [];
181
+ let from = completionContext.pos;
182
+ if (candidate) {
183
+ // const modelType = this._getModelType(candidate._type);
184
+ if (this.isMap(candidate._type)) {
185
+ options = this.getEnum(candidate._type).map((value) => {
186
+ const type = this.convertType(candidate._type);
187
+ const command = `['${value}'][0]`;
188
+ return {
189
+ type,
190
+ command,
191
+ apply: command,
192
+ label: value,
193
+ };
194
+ });
195
+ }
196
+ } else {
197
+ from -= prefix.length;
198
+ const candidate = this.grammar[key];
199
+ if (candidate._type) {
200
+ options = this.buildMethods(candidate._type);
201
+ } else {
202
+ options = Object.keys(candidate)
203
+ .filter((command) => !command.startsWith('_') && command.startsWith(prefix))
204
+ .map((command) => ({
205
+ type: this.convertType(candidate[command]._type),
206
+ command,
207
+ apply: command,
208
+ label: command,
209
+ }));
210
+ }
211
+ }
212
+ return { from, options };
213
+ },
214
+ },
215
+ {
216
+ expr: /{#[a-z]*.[a-zA-Z]*.[a-zA-Z]*/,
217
+ supportEL: true,
218
+ run(completionContext, match) {
219
+ const tokens = match.text.split('.');
220
+ const key = tokens[0].replaceAll('{#', '');
221
+ const word = tokens[1];
222
+ const prefix = tokens[2];
223
+ const candidate = this.grammar[key][word];
224
+ let options = [];
225
+ let from = completionContext.pos;
226
+ if (candidate) {
227
+ if (this._isMap(candidate._type)) {
228
+ options = this._getEnum(candidate._type)
229
+ .filter((command) => !command.startsWith('_') && command.startsWith(prefix))
230
+ .map((label) => {
231
+ const type = this._convertType(candidate._type);
232
+ return {
233
+ type,
234
+ command: `['${label}'][0]`,
235
+ apply(view, completion, _from, to) {
236
+ const from = _from - prefix.length - 1;
237
+ view.dispatch({
238
+ changes: { from, to, insert: completion.command },
239
+ selection: { anchor: from + completion.command.length },
240
+ userEvent: 'input.complete',
241
+ annotations: pickedCompletion.of(view),
242
+ });
243
+ },
244
+ label,
245
+ };
246
+ });
247
+ } else {
248
+ if (candidate._type) {
249
+ options = this._buildMethods(candidate._type);
250
+ } else {
251
+ from -= prefix.length;
252
+ options = Object.keys(candidate)
253
+ .filter((command) => command.startsWith(prefix))
254
+ .map((command) => {
255
+ return {
256
+ type: this._convertType(candidate[command]._type),
257
+ command,
258
+ apply: command,
259
+ label: command,
260
+ };
261
+ });
262
+ }
263
+ }
264
+ }
265
+ return { from, options };
266
+ },
267
+ },
268
+ this.defaultCompletionHandler,
269
+ ];
270
+ }
28
271
  }
29
272
 
30
273
  window.customElements.define('gv-expression-language', GvExpressionLanguage);
@@ -427,10 +427,6 @@ export class GvPolicyStudio extends KeyboardElement(LitElement) {
427
427
  search.focus();
428
428
  }
429
429
  }
430
- if (this.isPressed(KEYS.Esc)) {
431
- this._closeFlowStepForm();
432
- this._onCloseDocumentation();
433
- }
434
430
  }
435
431
  }
436
432
 
@@ -1114,7 +1110,7 @@ export class GvPolicyStudio extends KeyboardElement(LitElement) {
1114
1110
  outlined
1115
1111
  small
1116
1112
  @gv-button:click="${this._onCloseFlowStepForm}"
1117
- title="Close (esc)"
1113
+ title="Close"
1118
1114
  ></gv-button>
1119
1115
  <gv-button
1120
1116
  slot="header-left"